NCSC 2019 - pwn
Hi, this post will cover some binary exploitation tasks that we solved during the first edition of the National Cyber Security Congress organized by Securinets.
The binary was PIE enabled as we can see by running
This protection (PIE) combined to ASLR results in full randomization (or not) of the program’s address space.
The first bug is a call to fork function: after a fork, both parent and child will have the same address layout.
The second bug is a call to read in
handle_child function: the program reads 0x400 bytes of user input but saved rbp is only 0x10 bytes away from our our buffer start.
So, if we are able to overflow the buffer and overwrite rbp and the return address then we have control over rip and the execution flow.
Our goal is to execute
In order to do so, we need to find a valid rbp and the code base defined by the PIE protection. As stated before, the address space is the same at each connexion.
The solution consists in guessing the two parametres (rbp and rip) one byte at once starting from the first rbp byte:
if the guessed byte value is correct, the program execution will go on and we will receive
Bye as an answer and we can move to the next byte.
if not, then we have no output return because the program crashed or jumped to another code location, we can conclude that the guessed byte value was wrong.
Once we have rip, we can calculate the code base and then jump to
A classic buffer overflow, we have control over rip after 168 bytes in the vuln function.
ASLR is enabled on the server.
We have got some unusual gadgets from the
help_gadgets function to help us setting our arguments
To find out the libc version and base, we started by leaking two adresses of libc using the following payload
padding + pop rdi , rsi and rdx + 0x1 + write from the GOT table + 0x8 + write from the PLT table + vuln address
padding + pop rdi , rsi and rdx + 0x1 + printf from the GOT table + 0x8 + write from the PLT table
which results in the execution of the following functions:
write(1,write_got,8) --> call to vuln function again and then
By checking libc database search , we can easily determine libc version and the the system function and
/bin/sh string offsets from the libc.
Second execution payload will contain:
padding + pop rdi , rsi and rdx + 0x1 + write from the got table + 0x8 + write from the PLT table + vuln address
padding + pop rdi , rsi and rdx ( too lazy to look for pop rdi ) + calculated '/bin/sh' string address + 0x0 + 0x0 + calculated system address
Too lazy to clean the code, so i put both of the steps in one script
A binary containing very few code that reads user input to the top of the stack and calls for sigreturn (rax set to 0xf).
All we need to do is to read the flag at address 0x00402000
The supplied input is our constructed sigreturn frame containing a call to write(1, flag address, 50)