WordLock - Hacking a Bicycle Bike Lock with C# - for fun!
Walking with a friend last week, we found a bike lock in the middle of the street. The lock is called a "Word Lock" and is a popular design. Instead of using numbers, it uses letters -- where you choose a word to make the lock's combination. The available letters are a mixture of real words and nonsense words.
I wondered, is it crackable?
Can I write a C# program, showing all possible combinations, both nonsense and real words, and then whittle-down that list to only "word-like combinations"? Then use that list to break in? Yes!
Don't care to write the program - go to the end of the article to see how to protect your word-lock from programs like this.
Assumptions:
* 4 Rings, with 10 letters each.
* Some word are good words: "band", "bald", "rose"
* Not all four-letter words are real, but look like a word: "baad", "dumm", "wity"
* Some four-letter words are not words and can be discounted: "bwrd", "fwot"
* Assume the person who owned the lock used a word or word-like four-letter phrase.
Of
all the possible combinations, why limit my choices to "wordy-words? It is called WordLock! Of course the previous owner used a word! -- I hope.
The Rings have mixed letters in a variable order.
For example, on my lock, Ring1 has the letters b,f,r,m,d,t,s,w,p, and L, for a total of 10 letters. The second Ring has a different set of letters (a,u,y,r...), the third ring (a,o,k...), and so on.
That makes 10,000 combinations -- 10x10x10x10 -- but I was betting only a few hundred were "real" words -- a majority of the combinations could be discounted.
You
are invited to try writing a program to generate all combinations yourself. Use Python, C#,
Cobol, VB, and it could even be done in an Excel macro. Compare your
program with the one in this article. Drop a comment here and tell me how you did.
If you don't know how to write a C# program, consider my book "War and Peace Programming" (Amazon.com), where I talk about things just like this (but not bike locks).
The Rings, if you are trying this yourself:
R1 b, f, r, m, d, t, s, w, p, l
R2 a, u, y, r, w, h, e, l, o, i
R3 a, o, k, s, n, t, m, r, e, l
R4 d, l, y, p, e, t, s, m, k, g
Each
bike lock might have different letters on its rings, but I think all are the same. Photos on the Internet show the same characters as
mine. Other testing shows Ring1, which is all consonants, starts more real words than the
other rings, so I doubt the manufacturer varies the letters.
The Technique:
Four nested loops are needed, one for each Ring. The first loop starts with Ring1's first-letter 'b', then it needs to try every combination on Rings2, 3, and 4. Once done, Ring1 moves to its second letter 'f' and repeats the tests.
It is a bit more complicated than that. It really works like this:
Ring1 (R1), Letter 1
Ring 2, Letter 1
Ring 3, Letter 1
Ring 4, try every letter
>> Generate words R1-1, R2-1, R3-1, [R4 1-10]
then
Ring 1, Letter 1
Ring2, Letter 1
Ring3, Letter 2
Ring4, every letter >> R1-1, R2-1, R3-2, [R4 1-10]
Ultimately, after several hundred combinations, working your way up the rings, Ring1 will move to Letter 2. This can be done with a half-dozen lines of code.
Are you serious?
Did I actually think the logic through before writing the code? No.
Knowing a loop-within-a-loop was needed, there was confidence it would generate the results -- it is in the nature of how nested loops work. Once written, all I needed was to spot-check the results from the top of the combinations, then spot-check the bottom. I could spin the dials on the actual lock to confirm the right order.
The program writes the results into a text file, 10,000 records deep. Here are the first-20, and last-20 results. Most of the generated words are nonsense:
First20 Last20
baal (where "b" was Ring1's first letter)
baap lirk
baae lirg
baas liel
baam liey
baak* liep
baag* liee
baod liet* Possible word? Dune fan?
baol liem
baoy liek
baop lieg
baoe lild
baot lill*
baos lily*
baom lilp
baok lile
baog lilt*
bafd lils
bafy lilm
bafp lilk
lilg (where each letter is the 10th in its Ring)
In the hope of narrowing-down the list to legitimate words, I loaded the list into a word processor and glanced at the spelled and misspelled words.
Bad news: Too many words were "word-like" but were flagged as misspelled.
What teenage boy wouldn't want to use these words: "baad", "baty", "byod", "blep".... How do I know it was a teenage boy? A girl would never be dumb enough to lose her bike lock.
Flagging the words:
Instead, I loaded the words into a spreadsheet, keeping them in their generated order. Then I manually flagged each 'word-like-word' by hand, putting an 'x' in the next column. In a short evening, I found 608 likely words. Flagging 608 was easier than unflagging 9,392 words. Large swaths of similar letters (fyug, fy-this, fy-that) were quickly ignored. If a word were missed here-or-there, it had a low probability of being the "one true word."
Filter the list by the flags, keeping their same order.
My flagged word list, starting with Ring1, letter-1 ... spellcheckers be damned:
1 baad first flagged
3 baay
6 baak
7 baag
22 bafl
32 basl
35 base
39 bask
41 band
45 bane
49 bank
50 bang
: plus lots of other word-like words...
:
and ended here, at row 608:
9965 lime
9981 lied
9987 lies last-flagged
Grunt Work:
Starting at the top, I methodically tried each combination on the actual bike lock.
Gasp!
(Remember, this is a project with a clear, well-motivated goal of a free bike lock.)
Since the combinations are generated in order, I usually only needed to change one or two letters per word because the previous word already set the first two or three letters.
Screaming Success!
On the 363rd attempt, the lock opened on the word "DERP".
Yep, a teenage boy.
I now own a new bike lock, saving over $30!
Is DERP even a word? Good thing I flagged it. Total time testing combinations was about 60 minutes(?) -- not counting the labor in building the word list. Time well-spent for bragging rights such as this.
Statistically, of 600 possible words, finding the right word near the half-way mark (363) was no surprise. I should have started there. Time-and-time again, I find when working with sorted random lists, the results are closer to the middle than the wings -- Statisticians among you -- please explain this! Is it simply being within one standard deviation of the center? They could have picked the word "baad" just as easily as "lilt"... somewhat puzzled, I am.
The Code:
I wrote this as a console app in Visual Studio C# -- a free development environment from Microsoft (it could have easily been a Windows app tied to a Button1 event).
At the top of the program, where you would normally initialize new variables, build four single-dimensioned string arrays, representing each Ring. See the Ring list, earlier.
string[] astrRing1 = new string[] {"b", "f","r","m","d",...};
string[] astrRing2 = new string[] {"a","u","y","r"...};
etc.
Then, in Button1_Click, or in this case, in the main module, allocate an output file and write the nested loops. The loops are all the same -- a for-next loop -- with each running from one to ten, selecting one letter from their respective array.
Ring 2's loop is nested inside of Ring 1, and the other rings are nested inside of 2's.
The net effect is Ring 1's loop runs a leisurely 10 times while the inside loops run like hamsters on a wheel. As they whirl, they concatenate each found letter, assembling into a four-letter word. As-is typical, it took ten minutes to write the program and an hour to test and verify.
Running all 10,000 results took about 14 milliseconds. This constantly amazes me.
Because this blog isn't well-designed for displaying code, I'll use a screenshot. The entire program is small enough to fit:
Click for larger view |
Note: This code is more condensed and obtuse than one I would write for the real-world. This goes against the programming skills I teach in my books and classes. But it was only needed for this one run and I was not expecting it to arrive in an article like this.
Protect your lock from me, and now you
If the rings were digits, all 10,000 combinations would be in-play and there would be no rhyme or reason for the numbers chosen, and a filtered list would not have worked.
Here are two tricks that keep the natural words, but make it appear scrambled to the human safe-cracker.
Both tricks could be combined, using the left-edge on the back-side, expanding the number of combinations exponentially -- all appearing as gibberish to the casual attacker but
are a clear word when you know where to look.
Either of these ideas thwart+ programmatic attempts and would force an expensive sequential search. Trying every possible combination takes on average 5,000 attempts before finding the answer, but on a bad day it would take 9,999 attempts.
This was a fun project.
+It is not every day one gets to use the word 'thwart'.
-end
These techniques are described with kid-gloves in my programming book, War and Peace Programming - Amazon.
(For other projects, see Volume 6 - the Student and Instructor's
guide, where you can write a Star Trek Bridge computer simulation, take
apart credit card numbers, and other interesting projects. This is a fun book and makes for a good introduction to programming.)
No comments:
Post a Comment
Comments are moderated and published upon review. (As an aside, not a single spam has been allowed through; why bother?)