So today I’m going go review the process of cracking the Terminal.exe crackme by Kaspersky, used in some kind of selection process, ctf, hackathon or whatever this cool people does.
So first of all, after downloading the crackme, we try to open it to see what it does:
Ok So basically, it asks for a user and a password but then if the password is wrong it does not print anything, so we can guess that the program will do some kind of calculation, then a comparision and then will print something ONLY if the input is right.
This may make things a little bit harder for us as we don’t have any “fail” string to search for in Radare2.
Let’s open the program in Radare2
As you can see it works the same way in Windows. We do aaaa for a full analysis and then we are ready to work.
One of the first things to do here as usual is to search for some strings in the program, with what we already know:
With that command we will get all the strings loaded in the binary, so as you can see we will get a lot of strings:
This may scare you, as going over all those strings one by one, searching for something interesting may be slow or near impossible depending on the program.
As we already know, we surely have a “Enter your e-mail..” string in the program, so we can search it first.
Ok now that we found the address(es) where our “Enter…” strings are, let’s move there and examine their context:
Voilà! We found three interesting things here. “apof.bin” which may be some kind of binary file managed by the program, a named pipe and a “You got it!” string. The two first strings we encountered, may be useful for understanding the program later, when we start debugging it. What is interesting here is the success string.
We can also examine that memory region in more detail without moving our pointer there using the address by:
px @ 0x0049bdd8 – ‘b’ /3
Being 3 the ratio.
If we know the string we are looking for we can use
To perform a case insensitive string search, searching for “you” string.
Now that we know some interesting strings to look at, we can examine their context by
We will get some more information regarding to the context of the string.
And as we see not relevant information, we can look the xref of that string with
axt @ hit12_2
And then do s to that position and use pdf to know disasm the function and inspect it a little bit.
As we can see the function is long enough for us to know what it does on first sight, and also if we inspect it we can begin to see some bifurcations/loops going on in there.
We can use
To inspect the function a little bit better in the area around our function our string reference.
So when you are dealing with programs that are more complex than the typical introductory-level crackme instead of going instruction by instruction (you will get crazy) you have to focus on examining function calls for example. So examine strings, parameters and function calls, to get a general idea of the main functionality of the program.
For example in here we can see how the “You got it!” string gets pushed and then the program calls “WriteConsole“, then disconnects from a pipe, then closes a process, all working with the kernel32 lib.
The next thing I recommend you on working with crackmes is to take notes about these interesting things, for example as we know that the termination of the program is somehow related to a pipe, that may be an important mechanism of the program, so we can try to find that pipe before in the code.
Again we can use /i t search for pipes.
And we see the name of the pipe and a create, connect and disconnect functions as well. All of them really near in the code.
As they are near, and they are already in the main function we are analysing. We can just keep analyzing that function in visual mode to make a better picture of it.
So we can see how on first stage it starts with asking for a user and password:
But then some instructions after reading the initial data, the program does something a little bit more interesting:
So it calls the CreateFileA function, creating a file named “apof.bin“. Then it starts writting into that file.
So at this point we need to go the the MSDN documentation and see how “WriteFile” function works, so we will be able to know what is the program writting inside that file, because we can clearly see how after writting that info the file gets closed.
BOOL WriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped );
What’s interesting here for us is “lpBuffer” a buffer of bytes containing the content that will get written in the file.
So in our case the program will write whats stored in the address (we pass the pointer) 49e204.
Now we can try to inspect the XREFS on that, to see if that reference is used anywhere else, like, for example, for writting some values there (that later will be written in that file).
And then if we look close into that, we can see nothing really special. But again, let’s look at the big picture. We can see a fcn.00410049 reference, let’s look into that.
Surprise! We can identify a lot of values being written one after another in consecutive positions in that buffer, we can assume that this will be done before the program writes the file.
Wen can also see that after the program creates and writes that file, the program runs it using the CreatProcessW function.
Also, I would like you to note this detail and think about the following. In here we see a call fcn.0048b7b0, so in our analysis we may think that this function is important, and jump directly into an analysis over there, this may be a mistake and will make us lose a lot of time. So why we don’t have to go straight into analyzing it? Well, in here we see a CreateProcessW call with the file right after writting it! So what is going on here seems to be pretty clear! If in our analysis, a little bit later on, something starts making no sense to us, or we experience a problem, or maybe we think that we are missing something, then we may go back to that function and perform a deeper analysis there.
To note that, let us inspect that function:
What do we see here? A function, jumping into another function, jumping into another function and then at the end we see a “HeapFree” call!
We can quickly guess that the program is liberating heap space after doing that crazy thing with the file writting. Understandable, but is this useful for our analysis? I think not. Be careful not toget lost with such amount of jumps and better inspect the memory and the state of the registers before and after the function call to make a first approach to an idea of how it may work!
So we see how right after launching that process, the program creates a named pipe:
Named “KasperskyLabOffZone“. A named pipe is basically a file created on the system used for communication between programs. So one program can write content into that pipe and then another program can read it, think about it like with tcp/udp sockets somehow. It is important to know that a named pipe is a FIFO data structure, so the first thing we put in, will be the first thing we’ll get when doing “read”.
So after creating that named pipe, the program connects to it:
Then it does something interesting:
It writes content into that pipe, it calls the “sleep” function, sleeping 1 second = 1000 milli seconds = 3e8h milliseconds and then it reads back from the pipe. But at this point what do we want to know is what we write into that pipe.
We see a local_10h variable over there as a parameter. We can use axt for references based on mem addresses, but what about variables?
We can use.
And we will find references on WRITES to that variable and
And we will find references on READS to that variable.
So we can inspect those addresses in more detail:
So in here, we can vaguely guess that the user input is the buffer that gets passed to that function (by doing a couple of writes). But if we really want to know for sure whats the content of the local_10h variable right before entering the write function, what we need to do is to debug the file and inspect the memory before calling write at that point (or we can do a more in-depth and precise analysis over there).
So then the program reads the “answer” from that pipe and compares it with 0x19 (25dec)
If the answer is equal to that number, we are done:
And we display the “You got in!” message. Now with this we can have a general overview of the program. So the program asks for a user and a password, then creates some file and writes a relativelly large amount of bits in there, with that file the program creates a process, then the program creates a named pipe and sends the user input over there via WriteFile, the program reads something and compares it with 25 being that cmp true, the program launches a success message.
So now we can jump into the debugger, we can debug a program either doing
Radare2 -d Terminal.exe
Or a better option for this particular case we are, as we already have our program opened, we can do
To launch a process of the program.
And we can do:
To place a breakpoint right before running the WriteFile function, and then hit
Till we are placed in that breakpoont
And as we can see, we get placed right before the call.
So we have some options there, one is to inspect the pointers that are passed to the function, the other one is to inspect the stack itself and dump the content of the pointers from there.
We can inspect the stack by:
pxr–2 @ esp
With this we will print the recent values been pushed in the stack and we will print them in a confortable and useful format. pxr = print hexadecimal references.
And now that we have the stack content, we can see the parameters and with x dump the conteint to be written:
x @ 0x00627918
And, oh surprise:
There is a program being written on the disk. We can see that by quickly identifying the MZ (4d5a) magic number.
In our analysis, another breakpoint may be useful right before the WriteFile call, that writes the user input to the pipe:
And doing exactly the same we can inspect the parameters:
And we can also se the dump:
We see how we first send the username, then the password one right after another as what is pointed by “edi” register gets called twice:
So at this time of the match, the X in our equation is the “apof.bin” executable file that gets written in the disk and then executed. We can also see that the apof.bin program is not re-written or deleted at any point in the code, at least from what we see at plain sight.
We can try to open that in Radare2 and analyze it as well.
And as we open the program:
Another surprise, the program seems to be packed with the famous UPX packer.
Trying to analyze a packed program this way may result into a lot of pain, but there is no problem here as we can use
upx -d apof.bin
To simply unpack it
And then go back to Radare2 with the new executable.
After opening the program, we can quickly identify that the main content contains all the interesting stuff. We start by seeing how the program tries to create a file
So why a CreateFile here? This does not mean that the program is made for creating the pipe file itself, but CreateFile can also be used for just opening the file as if the file exists, eax will contain the file handle (file descriptor) in return.
So we can see that the apof.bin program and the main program share the same pipe, so they talk into another over there. And as we saw how the main program sends the user and password over the pipe and expects some value in return being later compared with 25dec, we also have a general idea about what is going on here.
So we can see a Read after the pipe connection (user?):
Then another one(password?):
And then we get to see this:
So as we see “ka$per$ky_1a8” being used there: We can think that this is the password or this is some string used along with the username to generate the password.
Then after that what we can see is a bunch of asm blocks with no known function calls and then a another Write and the end of the program:
What comes after this point is a little bit tricky (just a little bit). We would like to analyze how the whole system works. So we need to analyse in real time how both programs work and interact. The main problem in here is that is the Terminal.exe program the one that creates and launches apof.bin. So we cannot open it ourselves for analysing the sequence.
We will have to patch the main program to skip the CreateProcess call, add a breakpoint there and launch apof.bin manually ourselves in the debugger.
We need to patch the program right here:
We can create a label in Radare2 with f so we do
f new @ 0x0048a8cc
Then we can edit the program as easy as it follows:
wa jmp new
With s we move to that address for a better inspection, and then we use wa to edit that position inserting a jmp new being new the tag we just defined previously, corresponding to the address of “push 7“, the first argument we push in the stack for the CreatePipe function.
We can insert a second breakpoint after our added jmp, just to be sure that everything works fine and we land there.
Then we can simply use dc to move from one breakpoint to another, to make sure everything works properly.
So after we hit that breakpoint, we are ready to launch our Terminal.exe program whenever we want, and the program will start creating the pipe, writting to it and then waiting for an answer through it. What we have to do now is to move to the other program “apof.bin” and start it in debug mode, add some breakpoints and launch it.
To be more clear, we are doing the whole process that is done by the main program, but manually ourselves watching every interesting step in the debugger, so we have to follow the stages defined in the main program (exec process, create pipe, write1, read2, write1, read2…)
So now we will move to the second program, the unpacked apof.bin and we will place some breakpoints. Where to put them? I suggest you to put breakpoints before and after functions, specially unknown functions, also it is interesting to add breakpoints in areas of the code where you can guess that a comparision is done, or an interesting loop, a lea, near an interesting string also, and in places like that, anyway this is just a general recommendation.
So we add some breakpoints before and after fcn.* calls.
If we want to know what breakpoints we have and where, we can simply do db with no parameters and we will retrieve a list
If we want to know what function calls does the program in the current module we can dot
This may be useful as we will be able to identify interesting functions for our analysis, for placing breakpoints there.
Now that we have added our breakpoints let us run the program(s).
We move right after the first interesting function call, and we see how that function receives the “ka$per$ky_1a8” string as the one and only parameter.
We can confirm that by examining the stack.
After that call we can see that something has happened in the stack:
That may be the indicator of some operation with the string… We don’t se anything specially relevant in the memory and the context. So we jump over the other function and we examine the memory context after its execution.
As we examine the stack and the registers before and after the call, we don’t see anything so relevant.
But after a few minutes we can see something interesting in the eax register after the call.
So what if the contents of what is pointed by the eax register is a pointer to another region of code?
So we just found an md5 hash over there 917b280bf39854ba9e78b960407d1229 We can quickly identify this hash as the md5 value for ka$per$ky_1a8. As this string is the parameter we just passed to the first function, we can quickly assume that this is the hash for that even without having to calculate it, anyway common sense and a little bit of intuition are necessary for this kind of analysis.
So what may be a good idea for analysing what is happening with the fcn.004012cd function is to run it again and see what we get as a result. If we see no md5 hash, nothing new in memory related to that, and anything useful as we saw when we ran it for the first time, then we can pretty guess that the md5 calculator function is fcn.004019ac
That would be the very basics without entering in-depth into the asm.
So at first sight we can see how the username gets passed as the parameter of the function.
And then, as we enter inside the function, we can see how the function performs some kind of operation on it.
But after running that we don’t see anything useful in eax, as we saw previously at the beggining of the block.
Another easy thing we can do here is to examine the memory searching for the string we pass to the function before and after.
We can define our search by
That will prompt
Valid values for search.in (depends on .from/.to and io.va): raw search in raw io (ignoring bounds) block search in the current block io.map search in current map io.maps search in all maps io.maps.[rwx] search in all r-w-x io maps bin.section search in current mapped section bin.sections search in all mapped sections bin.sections.[rwx] search in all r-w-x sections dbg.stack search in the stack dbg.heap search in the heap dbg.map search in current memory map dbg.maps search in all memory maps dbg.maps.[rwx] search in all executable marked memory maps anal.fcn search in the current function anal.bb search in the current basic-block
A search in all memory maps will be good for finding some strings.
We can do the same with the other call, for the password, and quickly compare:
And yes, something has been copied:
And it looks like it is close to our md5 hash in memory..!
With that we can make:
afn MAKEMD5 fcn.004019ac
afn MOVE fcn.004012cd
So we will be able to know what is happening further in the code if we encounter them.
So what comes after that block is the following:
So we can see a initialization of some variables into registers, and a comparision.
We see how we are operating with the username variable:
As esi is set to zero, then we see a reference to eax+esi we can see how this block takes the username and goes letter by letter on it, checking if each letter is between a range and performing some operation on it.
The interesting part is the following:
From that we can see how this block will go letter by letter and on each one it will perform:
get the ascii code of the char -> sub 102h – that ascii code -> sar previous result 2.
We can enter the visual mode and keep pressing s to go through the loop and see how
pau@kaspersky -> $(#0%(#$’$#%”
Then, after that, we jump right to the final stage of the program, where we can see our MAKEMD5 function playing again:
So we can see how that encoded string those two last blocks calculated gets passed somehow to the md5 function:
We can see how we get the md5 hash of that string $(#0%(#$’$#%”
Then we can move forward and see how the code tricks us into calculating the hash of the entered password, even though that hash is used for nothing in the program
The interesting part that follows is the function fcn.00401000 this function receives a couple of parameters, being one of them the hash that we’ve just generated
As we can see those parameters get passed by pointers
And here we see the hash we just created previously
What is interesting here is to see that in return, we get the concatenation of those two md5 strings! And then we pass that big string into another md5 function.
Finally we can see how it calls the md5 function for a last time and gets a hash,
58af92c3921df26900f5004d1c755f38 (valid password for pau@kaspersky)
If we investigate further in the code what we can see is a comparision with the entered password and the calculated hash being done, so our search stops here.
So we can conclude with the key generation function:
- md5(ka$per$ky_1a8) = part0
- get e-mail
- for each letter of the e-mail get hex value, sub 102h letter -> sar 2 substraction = encoding
- md5(encoding) = part1
- md5(part1CONCATpart0) = valid password
We can see how it moves those values for doing the write with the right number though
And we can run the program with the right password we just found:
That’s all for today I would like to thank kaspersky for providing with good crackmes and the whole radare2 community for being so awesome.