Building a Safe Cracking Robot
The Problem Domain
The dial on our SafeSentry safe is 100 digits. If there are three discs, our domain is 100 * 100 * 100 or 1 million possible combinations. Testing on a safe at the store, we found it took about ten seconds on average to reset the dial then dial in the three solution numbers. So, worst case, it will take 115.74 days to try every combination. And 10 seconds is pretty fast; humans get tired and less precise over time. Luckily, there are some tricks we can do.
On some high-end, secure safes, if you turn the dial to 81.5 and the combination is 81, it won’t open. With these lower cost home-brand safes the manufacturing tolerances are much larger. On the safe we tried at the store if one of the numbers in the combination is 53 then 52 and 54 will also work.
The first part of this video will show you the tolerances on the combination dial are +/-1 digit.
This quickly reduces the domain to 33 * 33 * 33 = 35,937 combinations. That’s still over 4 days of trying.
One of the ways to pick a safe is by feel. Manufacturers know this, so, to prevent it, the last disc (we’ll call it disc C) has a series of indents. If you press down on the handle and spin the dial, the thing trying to push down into the notches on the dials, called the fence, will fall into these false indents and lock up the dials. Bummer. But, you quickly figure out that there are 12 indents. And, one of these ‘indents’ must be the correct slot when you dial in the combination.
The problem domain is now 33 * 33 * 12 = 13,068 combinations or 1.51 days. About the speed of paint drying.
The locations of these indents are found easily by hand. 100 / 24 (12 indents, 12 raised bits) = 4.17 dial positions per indent. It’s not super clean, but, if an indent ranges 21 to 25.2, then it’s safe to go to 24 to ‘test’ that indent to see if it’s actually a solution slot.
On our safe we located the indents as follows:
case 0: return (99); //98 to 1 on the wheel
case 1: return (8); //6-9
case 2: return (16); //14-17
case 3: return (24); //23-26
case 4: return (33); //31-34
case 5: return (41); //39-42
case 6: return (50); //48-51
case 7: return (58); //56-59
case 8: return (66); //64-67
case 9: return (74); //73-76
case 10: return (83); //81-84
case 11: return (91); //90-93
Here's what disc C looks like:
Disc C has the following dimensions:
- Outer diameter: 2.815” (55.5mm)
- Width of solution slot: 0.239”
- Width of 11 indents: 0.249” +/- 0.002”
C = 2πr, so our circumference is 17.69”. Our motor has an 8400 tick encoder. Each encoder tick is therefore approximately 0.0021”. So, we’re looking for a difference of about 5 ticks. Eeek! That's not many.
If we can externally measure the widths of the various slots, we may be able to discern which slot is the skinniest and therefor the solution slot. This would reduce our combination domain to 33 * 33 * 1 = 1,089 and a crack time of 3 hours, worst case.
The function measureDiscC()
is designed to take a series of readings and add them together. The motor is a gear head motor and has a tremendous amount of torque, so, if there is any flexing or give in your apparatus, this will show up in the readings as noise. However, if we take many simultaneous readings, any flexion should be replicated to all indent measurements allowing the skinny slot to bubble to the top.
Set screws will not work. A hub that uses a set screw to connect to the D-shaft on a motor will not survive the constant torture of indent measuring. Once we switched to a 6mm clamping hub we had much less noisy readings.
Here’s the output from five measureDiscC()
tests on our Craigslist safe with no combination:
Measuring complete
Smallest to largest width [Width]/[Depth]
Indent 8: [1911] / [1130]
Indent 1: [1925] / [1122]
Indent 3: [1953] / [1091]
Indent 0: [1955] / [1099]
Indent 11: [1966] / [1105]
Indent 2: [1992] / [1100]
Indent 9: [1994] / [1126]
Indent 7: [2011] / [1098]
Indent 10: [2036] / [1096]
Indent 4: [2077] / [1109]
Indent 5: [2083] / [1100]
Indent 6: [2114] / [1096]
Indent 8 bubbles to the top on almost all the tests we have run on our safe. We also output the depth measurements (how far does the handle go down), but I am much more suspicious of these readings.
We can’t be sure the smallest indent is the solution ident, or if we’ve even measured the indents correctly so the Safe Cracker firmware allows the user to control which indents are to be tested. Turn them all on, turn on 5, or turn on only one, it’s up to you. We recommend trying to crack your safe with the smallest four indents. If you fail to open the safe then turn these 4 off, turn the other 8 indents to true, and run again.
We are down to 33 * 33 * 4 = 4,356 or a little over 12 hours. Still not great. What other tricks can we do?
Quick Note: When we live streamed the safe cracking, we were conservative and selected four indents to try. The winning indent was indent 8, the skinniest indent. So with two data points, I’d say this vulnerability has potential. You can re-watch the stream if you'd like. The magic moment occurs at 45:20, but start around 44:30 to get the full scope.