Python scripting in GDB: a CTF example
By Thomas Berlioz
What to know before reading
This articles supposes you have a little x86-64 Assembly knowledge (registers,
basic instructions like mov
, addresses) and some GDB experience (breakpoints,
stepping, continuing).
It introduces Python scriting in GDB assuming you know some about Python bases like loops, conditions, libraries and types.
Introduction
Back when I used to play CTF every week-end, I was a huge fan of Reverse Engineering (RE) and Exploitation challenges. GDB had no secrets for me as I loved its simplicity and lisibility when associated with plugins like GEF or pwndbg.
Solving RE challenges means running and debugging the same executable again and again with different inputs to check if the instructions differ from an execution to another. That is quickly said and done for easy challenges but this becomes a penible task to do by hand on more complex ones.
gdbpython is a Python wrapper for GDB scripting that uses the GDB Python library to run GDB and execute command directly from the Python script. I only added a few functions to automate some tasks we do a lot in Reverse Engineering challenges like breaking at the entry point or setting breakpoints considering PIE (Position Independant Executable) is present. Let’s see a simple application resolving a challenge and do not hesitate to check the wrapper’s code directly! It’s pretty well documented.
Challenge
You can download the challenge
here
if you want to try it yourself! As in every CTF challenges, we are searching for
a flag to validate it. The flag format is HDCTF{}
and the only thing we have
is a binary:
$ file challenge
challenge: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically
linked, interpreter /lib/ld-linux.so.2, not stripped
$ ./challenge
Wrong!
$ ./challenge password
Wrong!
Looks like a classic executable waiting for the correct argument to give us the flag. Let’s see what we have in GDB:
|
|
The binary has been obfuscated with
movfuscator. This transforms
basic Assembly into Assembly with only mov
instructions, making it
very hard to read and to understand. It is also impossible to get C code back
from obfuscated code. Instead of deobfuscate it with tools doing the job for us,
let’s try to deal with it as such.
Setting up the environment
|
|
Now that we are on the entry point, we can start iterating until we notice something interesting: our argument being processed! A few steps later, here comes our flag:
We can notice
|
|
|
|
This could be a pure coincidence, but what if not? Let’s try to modify our input so the first letter matches but not the second one and see if we pass the check once.
We also notice there is a signal handler to handle the exception raised by
main
on its last instruction. We do not need to understand its job in the code
as we are going to bruteforce the characters of the flag, but we do need to
take it in consideration as GDB stops on interruptions by default.
|
|
|
|
Yay, we do pass the first check ! Now we can do the process for the whole flag !
Exploit
|
|
Run the exploit, wait a little bit and here we are !
flag='HDCTF{M0V3_IS_TUR1NG_C0MPLETE}'
Conclusion
gdbpython
helps you with a few functions to interact with GDB easier than if
you had to type all the commands by hand. It really is a wrapper around the
tools as it does not add any debugging feature that GDB (GEF in particular) does
not provide already.
GDB scripting is a very powerful knowledge to gain some time in repetitive debugging tasks like bruteforcing with the wil of controll and use debugging informations during the process. This example illustrates how easy such things can be with the adapted environment.