Within this post I’ll be doing a write up of the Space Pirate: Entrypoint challenge from the HackTheBox Cyber Apocalypse 2022 CTF competition (14/05/2022). This write up will be written according to my thought process whilst I was trying to complete the challenge.
Reconnaissance
First look
I started off by downloading the provided zip file from HackTheBox’s CTF platform and unzipping it. Within the zip file was a binary called ‘sp_entrypoint’ along with a glibc folder providing runtime .so libraries to allow the binary to run. Firstly, I decided to take a look at the binary file using the tool ‘strings’ to view all the printable strings present in the file.
The ‘0nlyTh30r1g1n4lCr3wM3mb3r5C4nP455’ string seemed interesting, so I tried to run the program and enter it when prompted for a password!
Damn, it seemed that it wouldn’t be that easy. I had to take a closer look at the program.
Analysis
After opening up the binary with radare2, analysing it, seeking to the ‘main’ function and printing the disassembled function, I began looking through the contents of the function. The following instructions in particular stood out to me:
|
|
The ‘sym.open_door’ function reference looked interesting to me, so I decided to trace the jump instruction that lead to it. It seemed the function would be triggered if ‘[var_40h]’ variable was equal to the hex value ‘0xdead1337’. So perhaps it was possible to manipulate the value of that variable to get access?
[var_40h]
I looked further up in the function, namely near the start and saw that the ‘[var_40h]’ variable was being set there.
|
|
Unfortunately, it was being set to a hardcoded value of 0xdeadbeef and there doesn’t seem to be any way of modifying this variable myself, so this seemed like a dead end.
sym.check_pass
So I instead started looking around in other areas of the function and noticed the following function call.
|
|
Looking inside the ‘sym.check_pass’ function, the following instructions were of interest to me.
|
|
Within this function I could see it was configured to do as the name implies, check the given input against a hardcoded password value. However, looking closer, I realised there was something wrong with the logic in the program. First, it was comparing my inputted string with a hardcoded string using strncmp, which returns 0 in ’eax’ if the strings are the same. Then the ’test’ instruction is checking if ’eax’ holds a value of zero, as it performs a bitwise AND and then sets the ‘ZF’ (zero flag) if the result is zero. This works because the only possible input values which result in zero with a bitwise AND is two zeroes. Then, instead of the expected ‘je’ (jump equal) instruction, which would jump if the ZF is set (meaning the strings are the same), there is a ‘jne’ (jump not equal) instruction. This means that it will trigger if the strings are not equal, instead of being equal, which is likely an error, as it’s the opposite of traditionally expected functionality. This jump instruction went right to a function call:
|
|
sym.open_door
Printing the contents of the sym.open_door function yielded the following:
|
|
So it seems this function will provide us with the flag when called because of the system call on line 16! And all I needed to do to trigger this was enter a string not equal to the hardcoded one due to the inverted logic! Brilliant!
Exploitation
So now all I need to do is exploit the binary. I connected to the container running on the CTF platform and set about exploiting it.
Bingo! All I had to enter was 2 to choose to enter a password and any string I wanted after as the password to get access because of the inverted logic, great!
Mitigations
Inverted password check
The behaviour of the ‘check_pass’ function should be changed to only call ‘open_door’ if the entered password is equal to the “0nlyTh30r1g1n4lCr3wM3mb3r5C4nP455” string. This can be achieved by replacing the ‘jne’ (jump not equal) instruction with a ‘je’ (jump equal) instruction instead.