2023-09-12

WordLock - Hacking a Bicycle Bike Lock with C#

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

Knowing my victim likely selected a real word, I am amused that 9,400 combinations were immediately tossed.  Having a limited pool of real words is the weakness in the lock's design.  Of course, you could pick a nonsense word, but these are hard to remember, and nobody wants to do that.

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. 

Testing the remaining 600 combinations is entirely manageable while watching TV, sitting on the front porch on a lazy summer afternoon.  On average, you would find the answer after 300 attempts.  Once unlocked, it is easy to re-set the lock to any other word, as long as it fits within the letters of the rings.

Here are two tricks that keep the natural words, but make it appear scrambled to the human safe-cracker. 

The back side of the lock shows the same four rings, but does not have a center index.  DERP on the front is LUKK on the back.  Spelling LUKK on the front, is DERP on the back.  Don't tell anyone, but just use the back.  Because the lock-picker does not know you did this, it doubled the number of possible words (and made it a lot messier with 600 nonsense words).

Or, who said the index needs to be in the center of the dial?  If using the left-edge of the window, DERP is spelled "TLEE" on the center-front index. 


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?)