After the last reverse-engineering challenge, I walked into this one with a bit of bias and muscle memory on how I thought the result would be found. I expected some encoded string of characters to decode, and the password would again fall into my lap. A tendency in life and in tech is to repeat the processes that were once successful in hopes of another positive result. At times it works, but in this case it did not for me. After spending too many hours banging my head against the wall looking for something that was not there, I reset and started from scratch. Given the opportunity to step back, analyze and assess the situation, I found that the key to this hack was far more common than a single unknown key hidden away. If you would like to take a crack at the hack, obtain the ‘crackme’ file on Github or view my result files in my reverse-engineering repository.

Unveiling The Man Behind The Curtain

Starting over in my analysis of the crackme file, I worked from the beginning of the main function line by line. Instead of looking at the data statically with objdump, I used the interactive debugging tool gdb. For those unfamiliar with these, they are both tools available on the Linux platform. They can be used to disassemble compiled code and read the instruction set in Assembly. With gdb, the program is stopped in process using breaks to identify what is changing each step of the way.

Commands used:

gdb crackme - Opens the crackme file with the gdb debugging tool.

disass main - Disassembles the main function.

disass checksum - Disassembles the checksum function.

b [address ie. *0x400608] - Sets a breakpoint at the memory location.

run 1234 - Runs the program until the first breakpoint is reached.

As I read the first lines of code, the program was checking for two arguments on the command line. The first being the program itself and the second the attempted password. It does not care about the actual values stored in these two arguments, it just checks if there are two. If not, it prints the usage advisory string, and the program ends. When running the program without a password, you can see the process play out. If there are two arguments, ‘crackme’ pulls the second argument value into position to pass to the checksum function. For this test, I used the string “1234” as the password.

In the checksum function I noticed a loop. I set breakpoints in various places and checked register values to gain insight on how the values were changing inside it. The loop removed the first character in the string and isolated it to perform a function. The process was repeated until every character was processed. The checksum then handed data off to the main function for comparison. Initially I printed the comparison value expecting it to be a string and instead received an octal value. The integer value returned was 202. Having recently seen an ASCII table I suspected that this was the sum of the ASCII values for the characters. The value it was being compared against was 0xdee or 3566. To quickly test and confirm, the first password I used to unlock the crackme file was 35 ‘d’ characters and 1 ‘B’. The ASCII value of a lowercase d is 100 and for a capital B is 66. Once confirmed that the ASCII total of the string was the password, the number of possible password strings increased exponentially.

Finding The Unknown Through The Known

Now that the key had been unveiled, I felt like the New York Post when publishing a key to New York City for anyone to easily duplicate. The next goal was to build a random password generator that would spit out a working password for the crackme program. First I needed a random string of characters to work with in a bash script file. For that I called upon /dev/urandom and translated the output into alphanumeric values (cat /dev/urandom| tr -dc '0-9a-zA-Z'| head -c $1). Using bash I used functions that converted characters from the printed character to the ASCII value and back. A loop cycled through the string until the value approached 3566, if the next character in the source string was too big then the character equal to the remaining difference was added to the solution string. Once complete, the random password was returned. To test the script I used the command, ./crackme $(./3-password-generator) which directly input the output of the password generator into the crackme program. Numerous tests confirmed the generator to be reliable although not perfect.

Filling In The Potholes



The final goal was to patch the program so that the program would unlock no matter what password was entered. To meet this challenge, I used radare2 or r2 for short. Not fully understanding what I could and could not do with the tool and a compiled executable, I admit I broke the program more often than not. My first attempts were to modify the values being compared. This resulted in segmentation faults and starting over again. Instead of trying to go around the pothole, I realized that I should just fill it in so that it can be easily bypassed. In this case, a jump if equal function needed converted to a simple jump command. I copied the crackme file and saved it as ‘1-cracked’. I disassembled the program using the command, radare2 -w 1-cracked. I used the seek functionality to move to the main function (s sym.main) and printed out 40 lines of code (pd 40) to review where to make the change. Using seek, I moved to the line after the compare where the jump if equals instruction occurs (s 0x400608). Finally I wrote the assembly code to fill in the pothole (wa jmp 0x40061b). To test and confirm I quit the program and tested the string ‘Hello World!’ and other strings that did not total to 3566. The result was successful. Crackme had officially been cracked.

Lessons Learned

I feel that the biggest lesson learned aside from working with radare2, is not to assume anything when approaching a new project. First analyze the problem, and then work through the solution. Some of the processes may including things that have been done before. However, it’s more important to be ready for things not seen before. Technology is constantly changing and security threats come from many different directions. Adaptability and learning about the unknown from the known have to be the primary tools one has at the ready as new threats or challenges appear.