PWNing 2017 CTF - Simple Keygen Reverse Engineering task

Today I'm gonna show you a solution of a task from pwning2017 CTF created by p4 team. I think this challenge is much simpler than the earlier one so I hope that you will understand it quickly. Here is a content (https://pwning2017.p4.team/task/simple_keygen):



Simple Keygen (re 50)

We have received a sample of security stuff of modern DRM system, can you crack them? You can download file from here

Let's see specify of the application which we want to crack. We are going to use familiar Linux command named file.


Our file is ELF 64-bit. Before we throw this file into IDA you have to know that the registers in 64-bit mode will be bigger and their names will be different than 32-bit registers. Of course our registers will have a 64-bit space. And what about names? The difference is small, because for example 32-bit eax is rax in 64-bit and that's all. Now when you know this details we can open IDA and read some assembly code. But maybe we don't have to do it... Let's use strings program which shows us some interesting strings from the memory of analysed file. Maybe one of the strings will be a flag?


There is not that easy like we want to. We didn't find a flag in strings, but in the other hand we will have more fun with crack it! Before decompile this file we should just open it in console and see what we have to do with this application.


We just have to write a password and our file writes a message to stdout 'Good job!' when password is good and 'Wrong!' when password is bad. We need to know which function of a program checks if password is good and what this function exactly do. So the only correct way is to decompile the code. I will use IDA, but you can use for example Radare2 or Hopper. Let's throw the file into IDA disassembler and look into the main for some interesting instructions.


Main function has two local variables. var_8 is not an interesting variable, because our file uses it only once and does nothing special. But the second one is more interesting. First call is printf function which write "Password: " to stdout. Second call is scanf and we can see that the first argument of this function is "%s" so we will write a string as password. This means that second argument must be our input. I changed the name of local variable to input earlier, but you can do it now. The next called function after scanf is checkFlag and this is the most interesting function of this program for sure. We should look into it and think what the assembly code exactly do with our input.



This function is pretty easy. After fast analyse we can see that every block of instructions has the letters of the flag! Why? Look at the top of one of these blocks. Program moves address in the memory of our input to rax register. After that operation it adds some value to the address which means - point to the x byte of string. Value of this pointed x byte of string is a char of input. Char has always 1 byte size in the memory so we can put it into smaller register than rax. (al or eax for example) The last two instructions of each block are cmp and jump if zero. We can see that the program compares x char of our input with char from the flag. If the char from input on x byte is similar to char from the flag on x byte than the program jumps into another block if not program jumps from the function and that means that our input is wrong. Fine. So we have something like an array here. Look at the last block in the picture above. There is an add rax, 29 instruction and 61h value which is 'a' in char representation. So 29 char of input has to be 'a'. An if expression will be look like: if( input[29] == flag[29] )
First decimal value is the index of the char in a flag and second hex value is a char which is in this index. It sounds easy to crack it manually, but we are going to do it by different way. Python is our friend!

We are going to write a script which will crack the password, but we should think about how to do it. How to get something from the long string? By the regular expressions! I am not going to teach you about regular expression, because this is such a long topic. If you don't know what regular expressions are, here is a link of official HOWTO - https://docs.python.org/2/howto/regex.html But first we need to copy assembly code to the text file, because we are going to search only checkFlag function, not a whole assembly code of our application.

When you see a graph view of the function you can press space. After that you can copy whole assembly code of checkFlag into text file.


My text file is looking like in the picture above. I have deleted addresses of instructions and names of the blocks of instructions, but you don't have to do it if you want to crack it. When we have the assembly code in text file all we need to do is to take indexes of signs and signs from the correct flag. I have written Python script to do this. Look.


Let's try to understand this code. Imported module re is the module of regular expressions. First function is looking for indexes in assembly code of checkFlag function. pattern variable is our regular expression which takes every number from assembly code with whitespace character before number and after the number. re.findall returns numbers which were find as a list to indexes variable. But in each block of the code we have this instruction - mov eax, 0. Zero is not an index of the char in the flag. So the next instruction throws out every zero from list of indexes. find_indexes function returns a list. The next function is find_chars. Each character of a flag in assembly code is ; '<char>' so our pattern is looking for chars between ' and '. Each block has one char of a flag so we need only one char between ' and ' - that's why in our pattern is {1}. This function also returns a list but this is a list of chars not of indexes. 


crack_the_password is the most interesting function in my opinion. It takes indexes and chars as the arguments. In each block of instructions we had char and index in an array of this char. So we can easily connect index and char. Look: indexes[0] is index of chars[0], because 19 is an index of 'o' indexes[1] is index of chars[1] and so on. We want to crack the password so the password can be in the list. I don't know what is the length of a flag, but I have created list of 50 elements with random char for example '$'. After that I threw chars of the flag in correct places (from indexes list). We don't have a 0 element of an array of a flag, because we deleted all 0's before, but every flag in PWNing CTF's are pwn{<some mystery text>} so the char in 0 element is 'p'. Our password is shorter than 50 length and we can delete '$' which will be after cracked flag. And we do this by this instruction - password = [char for char in chars if char != '$']. After this we can print a flag!





Comments

Popular posts from this blog

Learning of malware analysis. Solving 9-1 lab from the "OllyDbg" chapter. ("Practical Malware Analysis" book)

Learning of malware analysis. Basic static analysis labs from "Practical Malware Analysis" book