From https://fbesnard.com/2018/04/13/HITB-XCTF-Quals-2018-babypwn/
1. Challenge description
nc 47.75.182.113 9999
2. Challenge resolution
2.1 Introducing ourselves
Using netcat to connect to the challenge, we are greeted with the following message :
|
|
Very talkative server, I appreciate that…
Maybe we should introduce ourselves :
|
|
So, it seems that the server is sending our input back to us (or is called Florent as well, which is likely not the case…).
At this point, one of the possible vulnerabilities that comes to our mind is a format string vulnerability :
|
|
By sending %p
to the server, it replies to us with an address to which a pointer refers.
Unfortunately for us, we don’t have access to the binary or its source code.
But here we are : a blind format string vulnerability !
2.2 Further knowing the server (or getting our hands dirty)
Now it’s time to find a way to grab the flag.
I’ve already played with format strings vulnerabilities but never blindly.
While looking on the net for information on how I could efficiently leak usable addresses from the binary, I came across a challenge from the 33c3 CTF entitled Eat, Sleep Pwn, Repeat
or ESPR
(which is also the name of the German team which organized the 33c3 CTF).
The situation is pretty much the same and I started looking at write-ups of the challenge.
I stumbled across these 2 excellent ressources that I encourage you to take a look at :
In order to solve the challenge, I used the script from @jay_f0xtr0t (available here) that I adapted a little bit.
Below is the explanation of what the different parts of the code do :
First, we connect to the challenge (obviously) and set the architecture accordingly.
The challenge is a 64-bit binary : our %p
inputs reveal addresses like 0x7f2a8a02f780
which start with 0x7f
and are 6 bytes long.
|
|
The exec_payload
function is the function that exploits the format string vulnerability strictly speaking.
We prepend an arbitrary value (_EOF
in this case but it could have been something else…) to our payload and the function will parse the server’s response until it reaches our value.
We ignore \n
because if the server is using the gets
function (or similar) for reading our input, the fact that there is a newline character will cause a weird behaviour : the function will replace \n
with \x00
(null byte) and we will get the output twice.
|
|
The find_elf
function attempts to find an address that might be in the ELF binary.
To do so, it looks for an address starting with 0x400
(because 0x400000
is the default base address for binaries).
The address that we find will be useful later when we will use the DynELF
function from PwnTools.
|
|
The find_leak_point
function attempts to find the correct offset so that our input refers to itself and is sent back to us.
|
|
The leak
function leaks data from the address given as argument.
Some workarounds were made by the initial creator to handle the case of the special \n
that we previously mentionned.
|
|
Now using DynELF
from PwnTools, we can find the addresses of the printf
and system
functions.
The idea behind this is to overwrite the printf
address from the Global Offset Table (GOT) with the one from system
.
By doing so, when the server will attempt to reply to our request, it will use the system
function instead of the printf
function and thus execute the payload we send.
|
|
The find_plt_got
function attempts to find the address of the GOT inside the Procedure Linkage Table (PLT).
Indeed, the addresses for the printf
and system
functions we found before are in reality jumps to other addresses.
So if we find the GOT, we will be able to have the real address of the printf
function from the GOT.
If you don’t get this point, you may want to take a look at this video from @LiveOverflow.
|
|
The forge_exploit
function generates the final payload to be send to the server, replacing the value at the given address by the one we choose.
|
|
Below is the full exploit code :
|
|
2.3 Revealing its secret
It’s now time to run our exploit :
|
|