This post spoils a CTF challenge … Don’t read if you want to try it !
ECW (European Cyber Week) is a Jeopardy student CTF challenge. It is organized by Thales, Airbus and the Bretagne region. I had a great time solving these challenges, especially reverse and pwn ones. Hurry to hand over this !
[+] Presentation
Once analyzed, this binary will probably give you a flag!
[+] Recon
This is a Windows x86 binary, as file
tells us.
Let’s run the binary with some sample inputs to have a first look at it !
It looks very classic, we must provide the flag as argv and the binary will check if it is alright.
[+] Reversing
The binary is stripped :(
So, we will first need to identify the entry point and the main function, in order to find the password checking code.
With the help of static strings, we can find the main function at 0x1520.
The main function simply check if there are two args, print the usage string if not or check the input password and print the win/lose message.
check_password function first calculates a value based on the input length. It basically calculates the value -length * 2, with length as the input length. Let’s call this value the reverse length.
Then, the code computes a division (0x79cb0347 / 3). It then substracts 0x79cb0379 to the result.
If the new result is equal to the reverse length computed before, it goes to the following checks, which are simple checks on the first 4 chars and last 4 chars.
The problem is that the code will never pass this comparison since it needs an impossible input length : 0x79cb0347 / 3 - 0x79cb0379 = 0xaecdfd9e <=> input_length = 681115953.
One interesting thing is that the division code is executed inside a try/catch statement.
mov [ebp+ms_exc.registration.TryLevel], 0 ;
marks the beginning of the try statement, and mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
marks the end.
If this statement catches an exception, an handler function is called and it then resumes to the handler_func_return label (in red).
The exception handler function code can be easily found because this function is passed as reference to the try/catch statement at the beginning of the check_password function.
After reviewing it’s code, it appears that the exception handler calls VirtualProtect function from the Windows API to remap the text segment of the executable as RXW, modify it and remap it as RX after. It basically modifies some of the checks on the password in check_password.
After digging deeper in the executable, I found that the presence of a debugger is checked 2 times :
-
at 0x1720, isDebuggerPresent() function is called, and if it returns False, check_password function code is modified at the very beginning of the execution with VirtualProtect :
div ecx, 3
becomediv ecx,0
in the try/catch statement… which will trigger an error and execute the previously mentionned exception handler. -
at 0x1627, a custom isDebuggerPresent_custom() function is called (it’s uses the PEB and his BeingDebugged field to check for the presence of a debugger), and if it returns False, one on the checks on the password in check_password is modified with VirtualProtect again.
So, the final strategy is to put a breakpoint after the calls to IsDebuggerPresent() and IsDebuggerPresent_custom(), set their return value to 0 (False
) and set another breakpoint at the beginning of the password checking code to extract the real conditions.
Let’s run the binary with some sample inputs to have a first look at it !
I made a quick and dirty Python script which recover the password from the conditions we can extract from the patched check_password function.
#!/usr/bin/python3
import struct
def ROR(x, n, bits = 32):
mask = (2**n) - 1
mask_bits = x & mask
return (x >> n) | (mask_bits << (bits - n))
def ROL(x, n, bits = 32):
return ROR(x, bits - n, bits)
flag_0 = ROR(0x79d71377, 5) ^ 0xdeadc0de
flag_0 = struct.pack("I", flag_0)
flag_1 = ROL(0x100000000 - 0x1a9391dd, 5) ^ 0xfaceb00c
flag_1 = struct.pack("I", flag_1)
print("Password : %s" % (flag_0 + flag_1).decode('utf-8'))
#ExceptCW
Finally, let’s use this password to get the flag !!
[+] Bye
Feel free to tell me what you think about this post :)