The first crackme is a basic console application that prompts the user for a password.
The only thing this application does is tell us if we entered the correct password or not. The application was likely written in
assembler because it is extremely small and was trimmed to the barebone essentials. This actually simplifies
our job when disassembling because we don't have to wade through the usual compiler generated code.
INSTALLING THE FIRST CHALLENGE:
If you are running 64-bit version of Windows, you can download the first link above that is essentially a
mini-installer GUI as originally provided by the FLARE website. After accepting the license agreement,
you will be prompted to specify the path to extract
challenge #1's executable file. If you don't want boot into 64-bit Windows for whatever reason
(Olly claims to have odd problems running under 64-bit Windows and the challenges are all primarily 32-bit executables),
I provided a second download link above which contains the same file extracted by the installer.
The file supplied for challenge #1 is:
When dealing with unknown executables, you typically want to run the
program in a debugger, a sandbox environment (such as on a virtual machine), a user with limited access rights,
a machine with the network cable unplugged, or combinations of the above. Despite the warnings about potential malicious
code in the FLARE license displayed in the installer, these challenges are safe and none will cause harm to your system.
With that out of the way, lets always start the programs from a console window (cmd.exe) to ensure we don't miss any
messages that may be displayed:
Intentionally typing "spaz" as the password, we now know how the app responds to an incorrect password.
EXTRACT STRINGS FROM EXE:
Let's use the Windows "strings" utility (a free tool from
to dump plaintext character sequences from our executable, limiting the output to strings of at least 5 characters
in length. Since we know the password is an e-mail address, we should be able to spot anything with an "@"
symbol rather easily.
NOTE: you may also use the functionally-equivalent "strings" tool that comes with most Linux distributions.
VIEWING EXE IN HEX VIEWER:
Okay, so we can see the console messages were embedded in plaintext, but nothing that
looks like a password showed up.
There was only one string that contained the "@" symbol, but it certainly didn't look like an e-mail
address. We're going to have to dig a bit deeper. The next step we're going to try is to
view the executable in a hex viewer or editor. Maybe the strings tool missed something that will catch our eye.
The hex representation of the executable file should look something like this when viewed in your favorite
hex viewer or editor (I used the Linux tool xxd to generate the dump below: e.g. xxd -g 1 -c 24
If you have OllyDbg configured to break on module entry points during application startup (Options ->
Debugging -> Start), you should find yourself at breakpoint 401000 / PUSH EBP. This is the module's entry
point. I also recommend setting the environment variable _NT_SYMBOL_PATH to the location of the symbols for the
version of Windows you are running and enabling the "Allow access to Microsoft Symbol Server" option in OllyDbg
so you can see the names of the API calls rather than just addresses.
NOTE: If you are safely running this executable (along with OllyDbg) under VMWare or VirtualBox and you still
don't find yourself at this instruction or the app simply exits without a debugger break (look for the red-text
Terminated at the bottom right corner), ensure your processor supports virtualization and it is enabled in the
BIOS/UEFI. If your host machine is Linux, you can run "cat /proc/cpuinfo|grep -i vmx" (Intel) or "cat /proc/cpuinfo|grep -i svm"
(AMD) to see if virtualization is enabled (grep will return one of these flags). Under Windows, you can use a CPU
identification tool like CPU-Z and look for "VT-x" under CPU -> Instructions. If you don't have virtualization
support, you can use the "System breakpoint" option in
OllyDbg instead, but you'll need to step through some userland Windows loader code that runs in the context of
your process before you reach the first instruction in your module.
FIND LOCATION WHERE USER INPUT IS PROCESSED:
For those of you with Windows API experience, you know that
you must open console file handles to input or output text. The standard C library functions such as printf() and puts()
wrap these same API calls (under Windows that is). Since symbols are set up, we can see in the right pane of the CPU window
the two calls to GetStdHandle(). These calls get our console output and input handles, followed by a call to
WriteFile() to output the password prompt. Finally we see a call to ReadFile() to accept our input.
For this particular app, we care most about what the instructions are doing to whatever value we type in for a
password and are not really concerned with the rest. So lets step-over instructions (F8 key) until EIP (the instruction
pointer) is at the XOR instruction immediately following the ReadFile() call.
You'll notice the debugger seems to freeze once you try to advance past the ReadFile() call. This is because
ReadFile() is waiting for user input and has not returned to the caller. When this happens, switch to the window
containing the app being debugged and type in a string value followed by <ENTER>. Once this is done, the
debugger regains control at the XOR instruction following the ReadFile() call.
If you look at the PUSH instructions just prior to the ReadFile(), you can see that the address 402158
is passed as the buffer to store the user input. Lets display this buffer in the memory dump window.
Select the PUSH instruction at address 40103D, then right-click and select "Follow in Dump" -> "Immediate constant".
You should now see the string you typed in the dump window. In the sample below, you can see I typed "spaz"
ANALYZE INPUT LOOP:
Notice how OllyDbg seems to group the instructions with a bracket at 40104D thru 401061. This is actually
showing the conditional jump JL instruction jumping back to 40104D if the condition is satisfied. the XOR
instruction we're currently at sets ECX to zero which happens to be the loop counter. The "CMP ECX, 18" just
prior to the JL instruction means to keep looping back until we've counted 18 times. Since Olly shows operands
in hex, this is decimal 24. So lets analyze this loop to see what it is doing:
0040104BXORECX, ECX;reset loop counter to zero0040104DMOVAL, BYTE PTR DS:[ECX+402158];pull next char from input buffer into AL00401053XORAL, 7D;encrypt input char by XORing with 0x7D00401055CMPAL, BYTE PTR DS:[ECX+402140];hmmm, we're comparing encrypted input characters with this other buffer?0040105BJNESHORT 0040107B;does not match, exit loop to display a message0040105DINCECX;increment loop counter0040105ECMPECX, 18;have we looked at 0x18 (24) chars?00401061JLSHORT 0040104D;if not, keep looping until we have00401063PUSH0;00401065LEAEAX,[LOCAL.1];if we get here, we matched all 24 chars00401068PUSHEAX;00401069PUSH12;0040106BPUSHOFFSET 0040211C;pointer to success message00401070PUSHDWORD PTR SS:[LOCAL.2];00401073CALLDWORD PTR DS:[<&kernel32.WriteFile>];output success message00401079JMPSHORT 00401091;skip over failure message0040107BPUSH0;0040107DLEAEAX,[LOCAL.1];if we get here, the loop jumped here because of a mismatch00401080PUSHEAX;00401081PUSH12;00401083PUSHOFFSET 0040212E;pointer to failure message00401088PUSHDWORD PTR SS:[LOCAL.2];0040108BCALLDWORD PTR DS:[<&kernel32.WriteFile>];output failure message
The most important thing to notice is that this code is looping through each character in our input string, XORing each character
with a value and then comparing the result with another character. We exit after looking at 24 characters so we know the correct
password is 24 characters. If at any point the character doesn't match what is expected, we jump
out to the failure branch and exit the program.
We now know that the XOR operation is the encryption technique employed by this app explaining why the password was not
visible in the hex dump. We also know the encryption key is the byte value 0x7D, but we still need some way to determine the password.
Notice how the CMP instruction at 401055
compares one of our encrypted input characters against a value at the base address of 402140 indexed by the current counter. This base address
seems to be the beginning of a series of values that correspond to each input character. At each iteration of the loop, the program is comparing an encrypted input character
with the corresponding character in this buffer. Taking this one step further,
because we know how long the password is, we can infer that we are comparing our input characters against the encrypted password located between 402140 thru 402157
inclusive (0x402140+0x18-1). If you look back at the EXE hex dump above, this corresponds to file offset 0x540
(just after the "You are failure" string).
Because the password was encrypted/obfuscated, this range of bytes in the hex dump didn't look particularly
meaningful at the time:
Select the CMP instruction at 401055, then right click on it and then select "Follow in Dump" -> "Memory address".
This scrolls the dump window to the address in the instruction's memory address operand which happens to be
402140. The memory dump window should now display the portion of memory containing the encrypted password in the upper right portion
followed by whatever else is in memory. This is why it is important that we already know (from the code) how
much of this data is part of the encrypted password as portions directly after may belong to another part of the program.
Notice the encrypted password happens to be directly adjacent to our input buffer:
NOTE: The hex dump character column may not display the
characters the same way OllyDbg is displaying them in the memory dump window; that's ok, because hex dumps
customarily only display characters in the range of 32 thru 126 and everything else with period characters ".".
Its the hex values that are important.
EXTRACT ENCRYPTED PASSWORD INTO SEPARATE FILE:
Since XOR is a symmetric operation (XOR plaintext char with key to encrypt, XOR encrypted char with same key to decrypt), all we need to do is XOR each encrypted byte with our key value of 0x7D to
get back to each plaintext character.
One method is to patch the program in place, such as to read from the encrypted buffer instead of our input
buffer and then the XOR operation ends up decrypting the character. We then can step through the loop, writing down
the value of each decrypted character as it appears in the AL register immediately following the XOR instruction.
But, I find it better to extract a copy of the encrypted key buffer into a separate file. This way we can work
with it in isolation using other programs and techniques. To do this, click and select the 24 characters in the memory dump,
then press CTRL-Insert (or right-click the selection and choose "Edit" -> "Binary copy"). This will copy the
hex values to the clipboard. Now we need to fire up a hex editor that supports pasting hex values
from the clipboard. Pasting hex values into a hex editor ensures we'll get an exact binary copy of the data we
want, no more and no less. Copying and pasting binary characters into a text editor like notepad will not give us an exact
binary copy as text editors will interpret the non-printable characters and convert line feeds.
In this case, I happen to be using an ancient version of Hex Workshop that still works great.
Hex Workshop is nice because it supports pasting the raw hex values OllyDbg places on the clipboard to ensure
an exact copy of a memory selection. In Hex Workshop, select from the menu
"File" -> "New". Then select from the "Edit" menu -> "Paste Special...". Click the "Paste" button, leaving
CF_TEXT as the default. The hex editor now has an exact copy of the bytes you selected in OllyDbg. The data
can now be saved as a separate file using the "File" menu.
Another alternative is the FlexHEX editor, although with more steps. Before opening FlexHEX, paste the hex
codes from OllyDbg into a plain text editor such as notepad and save the file. In FlexHEX,
select from the menu bar "Edit" -> "Read/Write Data" -> "Import...". From the import dialog, choose the
path to the file you just created and set "Format" to "Hex Bytes". You can leave "Separators" with the default
value of " ," (spaces and commas). Finally click OK to import the hex codes as raw bytes. Now you can save off
the binary file.
CRACKING THE PASSWORD:
Now save the file and exit the hex editor. I named it password.bin. At this point, you could throw together a tool in your favorite
programming language to open password.bin, loop through each byte to XOR it with 0x7D and finally output
each resulting byte. This crackme is simple enough that I could just use the xorit tool
to perform the operation just described.
Since the value of 0x7D is the close curly brace character "}", we'll run the following command to decrypt the password:
xorit -q -kd "}" password.bin
Now that certainly looks like an e-mail address! Running the program again and using our cracked password as
the input confirms that we got it right.
The official solution used IDA to
statically analyze the file to get a similar disassembly listing and ultimately arrive at the same conclusion.
It also illustrated a neat IDA Python script to perform the XOR decryption.
Response from firstname.lastname@example.org:
Subject: FLARE-On Challenge #1 Completed!
Date: Wed, 05 Aug 2015 13:19:20 -0400
Congrats! I've attached the next challenge for your reversing pleasure. The password to this zip archive is "flare".
This challenge looks a lot like the last one so hopefully you'll knock this one out too, Good luck!