[Pragyan CTF 2018] Old school hack
I played Pragyan CTF for a few challenges and I thought the “Old School Hack” challenge would be great for a first post.
Gatheting information
The challenge is running at 28.199.224.175:13000, but it also comes with a binary file named “police_academy” so let’s start with it:
The file command informs us that this binary is an ELF for x86-64 architectures, that it’s dynamically linked and most importantly that it’s not stripped so it should be easier to disassemble.
Then comes the obvious strings command, which will give us useful information:
Yeah, a lot of things.
First detonation
Let’s run the binary and see what it does:
Hmmm… It asks for a password. At this point we can note the first input field of the program, it can be useful for later. I tried several random characters; didn’t work.
Maybe our input password will merely be compared to another string that is hardcoded in the binary, and if so, this string should be in the strings output. But let’s be clear here, I just randomly tried the string “kaiokenx20” just after “Enter password to authentic yourself :” before thinking about this because it was too big to not try, and it works.
Second input field. Now obvisouly, let’s try the option 7 “Flag”:
Yeah, it may not be that easy. The other options just close the connection, but we’ll see why later. Let’s try to find something interesting in the disassembly code.
Static analysis
I used radare2 + cutter as a GUI (which is very cool to use) to disassemble the binary. Let’s examine our first input, and navigate to the first scanf call:
After the first scanf, we can observe that our input is indeed compared to kaiokenx20, but it uses strncmp with the first 10 characters (the last parameter seems to be edx which is 0xa). Thus, for example strncmp(“kaiokenx20”, “kaiokenx20AAAA”, 10) should return 0. We will exploit this later. Also, our input is rbp-0x40 in the stack.
Next scanf:
Our second input is rbp - 0x48 in the stack and is compared to 7 which is the number of options. If above (ja), it jumps to 0x400cb8. I thought in the first place that it was the end of the main function. But nope, just before a function called print_record.
Just after the jump, there is another jump and 6 times the almost same code pattern.
The first three lines will jump to one of these patterns, for instance to 0x00400ad3 if we choose the first option. It clearly constructs a string which is placed just after our first input in the stack… Seems like a buffer overflow exploitation. This is in little endian so the string is:
It also adds a null byte at the end, to terminate the string (0x00400b15). Then, it jumps just before print_record again.
A quick look at this function seems to show that it first checks the lenght of a string (the one it just constructed?), compares it to 36 (0x24) (which is the size of the constructed string again), and if it’s not equal quits the function. Otherwise it will try to open the file and print what’s inside (fopen and fread).
For the option 7, it constructs the string “flag.txt”. We can now easily assume that the flag is in this file.
Dynamic analysis
Let’s now run it with gdb. For now I just want to see if it’s gonna open the file named 80702a58e5f50ae8df9c1ca9356d5a44.dat for the first option. I’ll just set a breakpoint at the fopen instruction to see if the string is one of its arguments:
Yep, looks like it will try to open 2a5880700ae8e5f51ca9df9c5a44356d.dat. I created a file with this name and some random characters in it and yes it opens and outputs it.
We also want to give a look on how and where the string is placed in the stack. Let’s set a breakpoint before the call to print_record in the main function:
Following our first input, we have the name of the file to open. And note that the print_record function read the same string at the same address (0x7fffffffe010).
It’s now easy to think of a potential payload:
- First we have the password buffer which is not controlled and with which we can write futher down the stack. Remember, the password buffer is at rbp-0x40 and the file name should start at rbp - 0x30. The password string should be 16 characters.
- Then, we will write the name of the file. It has to be 36 characters long to match the strlen test in print_record.
- Finally, we have to choose an option above 7 to jump directly to print_record.
A potential payload should be:
Now, let’s create a file named “flag.txt” in the same folder as the binary. We’ll try to read it.
It works ! Now, let’s try it on 28.199.224.175 13000:
Really easy one but it was kind of fun.