How to crack VxWorks password hashes

Recently H.D. Moore discovered a serious vulnerability in the widespread VxWorks 5.x operating system (or at least rediscovered it, expanded upon it, and publicized it). I was lucky enough to be one of the sixty people who saw his DEFCON 18 skytalk on the subject. He released information about the vulnerability to the public on August 2nd, 2010.

In addition to the main vulnerability (the fact that many vendor implementations of VxWorks 5.x had a publicly-accessible debug service running on port 17185), H.D. Moore discovered that the default VxWorks password hashing algorithm was insecure. He said he would release much more information about it one month from the initial release, which would mean September 2nd, 2010.

That was too long a wait for me, so I started working on the problem soon after I got home from DEFCON, and I was successful in finding the weakness H.D. talked about. I’ve also written a tool which can turn any password hash into a workalike password (i.e., a password different from the original password that produces the same hash), and another tool which scans VxWorks memory dumps for password hashes and reveals the workalike password for each one.

Although I’ve completed this work in early August, I have decided to follow the same release schedule that H.D. Moore has committed to, so this post is set to publish at midnight on September 2nd. I’m sure that I’ve only discovered part of what he will reveal on that day.

Background on VxWorks' weak password hash

By default, passwords in VxWorks 5.x are hashed using a really stupid one-way hashing mechanism. I don’t understand why the VxWorks developers didn’t use a stronger hash. MD5 was popular as a one-way hash when VxWorks 5.x was being written, but they didn’t use it. Instead, they created their own hash, what H.D. Moore called a “Bob the Wizard” hash at the skytalk.

The meat of VxWorks password hashing algorithm is a checksumming step where each byte of the plaintext (the password to be hashed) is multiplied by and then XOR’d with its position in the string, and added to an accumulator. It looks like this in C (modified slightly from the original source for clarity):

checksum = 0;
for (ix = 0; ix < strlen(plaintext); ix++)    /* sum the string */
  checksum += (plaintext[ix]) * (ix+1) ^ (ix+1);

Next, this checksum (an integer) is multiplied by a magic number and turned into a string in decimal:

sprintf (hash, "%u", (long) (checksum * 31695317)); /* convert interger
                                                    to string */

(The lulzy typo in interger is original.)

The final step of computing a hash is doing a character substitution for each byte in the string:

for (ix = 0; ix < strlen (hash); ix++)
  if (hash[ix] < '3')
    hash[ix] = hash[ix] + '!';    /* arbitrary */

  if (hash[ix] < '7')
    hash[ix] = hash[ix] + '/';    /* arbitrary */

  if (hash[ix] < '9')
    hash[ix] = hash[ix] + 'B';    /* arbitrary */

This character-substitution step really has no point other than making the hash look “hashlike.” It simply converts decimal digits to letters (except 9, which isn’t changed). For example, it always converts 0 to Q, 1 to R, 2 to S, 3 to b, etc. The input string 0123456789 would come out as QRSbcdeyz9 every time. It might look more like a bonafide hash of some sort, but it’s not.

In the end, every VxWorks 5.x password hash is composed of the alphabet QRSbcdeyz9 and is up to 10 characters long.

Reversing the VxWorks password hash

For any password hashing algorithm, if you can generate a list of all possible outputs, and you know the inputs that generated those outputs, then you can turn any given password hash into a workalike password. (This is also called “reversing” the hash.) For a hashing algorithm like MD5 or SHA1, doing this is infeasible; however, the VxWorks checksum space is so small that it’s possible to generate such a list pretty easily.

The weakness in the VxWorks hashing algorithm is the summing step (the first step). For any output value of the summing step (checksum), it’s easy to modify the input (plaintext) to produce an output incremented by one (checksum + 1). This means you can start with an input that generates the lowest possible checksum and then, by continuously generating subsequent checksums, you can create a table of all possible output values and all the input values needed to create them.

I started working on this approach, but very quickly realized that there is a simpler way to generate such a table that requires very little thinking!

The VxWorks hashing algorithm just happens to be so bad (because it crunches so large an input space into so small an output space), that randomly generating a few million input values generates nearly every possible output value. In my tests, this approach found a workalike password for 100% of dictionary words and more than 99.99% of random input strings composed of all legal input characters.

Download my code

I’m releasing some Ruby scripts and a C program here:

vxworks_hash on GitHub

This is what’s included:

  1. vxworks_collide.rb: VxWorks password hash reverser library (plus vxworks_find_collision.rb, a handy command-line tool for using the library)
  2. vxworks_mem_search.rb: Scanner that finds and reverses hashes in VxWorks memory dumps
  3. vxworks_make_hash_table.rb and vxworks_collide.c: C program for generating VxWorks password checksum tables and a Ruby script to turn checksum tables into hash tables (for use with #1 and #2)

I’m also releasing a precomputed hash table (lookup.txt) for use with #1 and #2.

7 responses
Thanks for your work! It helped me a lot, when I had to delete some users from a storage router running VxWorks. There was no way to delete the users without knowing their passwords, so your work was a real life-saver!
Awesome, janosh! I'm glad my work was helpful to you. Thanks for your kind compliment.
Hi, thanks for your work.

I get the follow error when I try to run vxworks_mem_search.rb:

ruby vxworks_mem_search.rb lookup.txt dump.mem
<internal:lib />:29:in `require': no such file to load -- vxworks_collide (LoadError)
from <internal:lib />:29:in `require'
from vxworks_mem_search.rb:4:in `<main>'

Do you know how I could solve it?

@Pedro: Make sure vmworks_collide.rb is either in the current directory or in your Ruby include path. The easiest thing to do is probably to make sure all the .rb files in my Github project ( ) are in one directory. Then run `ruby vmworks_mem_search.rb lookup.txt dump.mem` from that directory.
I know this is an old thread, but hopefully it will get noticed. I need a tutorial on how to do all of this. There is a particular piece of equipment I need to do it on. It is on my local network here at my house and I have no Idea where to start. Any help would be appreciated. I am using backtrack linux if that helps any,


@Brian: Unfortunately, I'm not working on VxWorks stuff anymore, and I won't be putting together a complete tutorial on how to do it. However, there is some stuff out there that can explain many parts of the process separately which you can assemble into your own sort of personal tutorial. HD Moore and my friend John Sawyer ( ) have both put out some writeups on the VxWorks vulnerabilities, and there are VxWorks modules for Metasploit. If I were you, I'd start looking at those resources first.
Hope some one can help me here. I have tried to use this, but keeps getting the same error as Pedro: /usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- vxworks_collide (LoadError) from /usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require' from vxworks_mem_search.rb:4:in `' I have compiled vxworks_collide.c using this: gcc vxworks_collide.c -o vxworks_collide vxworks_collide has execute permissions. All files are in the same directory. Is this still working on newer versions of VXWorks? I have no idea what version of VXWorks I'm working on though. My plan is to get in to the boot loader of my router (TP Link TD-W8960N), dump the memory, and see if I can get access. My ISP won't give me the password, not even the ADSL settings so I can use my own router :(