Challenge description:
Pwn
Host : pwn2.chal.ctf.westerns.tokyo
Port : 16317
Reversing and Finding the Bug
Reversing with radare2:
Looks like another textbook format string vulnerability because the user buffer is put into sprintf
and then straight into printf
. This time I had to actually do the work of getting code execution because the flag was not loaded onto the stack.
Running the binary
I wanted to see the bug in action so I loaded up my Ubuntu VM using vagrant and checked it out:
→ ./greeting
Hello, I'm nao!
Please tell me your name... %08x
Nice to meet you, 080487d0 :)
Neat. Now for exploitation.
Background
For information on printf
and a more basic format string exploit, check out the post I did on the judgement pwn challenge also from this CTF. In addition to having positional arguments, printf
also has a cool feature where you can write the number of bytes that have been printed so far to a variable. This feature is what makes format string vulnerabilities so dangerous. If you can exploit one, you can get arbitrary write.
Passing %hn
to printf
in the format string will write up to a half word value of the number of characters written so far. Combining this with positional arguments allows for half a word at a time to be written to anywhere. So this is bad.
If you are interested in learning more about how format string vulnerabilities work then check out this paper
Exploitation
I decided to use libformatstr for this because I have never used it before and it seemed useful so I didn't have to craft the buffer manually.
The payload function takes two arguments: an argument number and a padding number. The offset number is the word distance in memory away from your input and the padding is the number of bytes your input needs to be padded for the addresses you enter to be word aligned. Libformatstr can be used to determine these numbers:
from pwn import *
from libformatstr import *
e = ELF("./greeting")
r = process(e.path)
r.sendline(make_pattern(0x40))
r.recvuntil("you, ")
res = r.recv()
print(res)
argnum, padding = guess_argnum(res, 0x40)
log.info("argnum: {}, padding: {}".format(argnum, padding))
Running this resulted in an output of argnum: 12, padding: 2
. There was one other bit that needed to be changed as well. Since "Nice to meet you, " was being prepended to my input I had to set an additional argument when setting up the format string exploit called start_num
.
Armed with the argument number, padding, and start number I was ready to try and overwrite some values. The issue I ran into was that there are no function calls after the call to printf
in main. I though of trying to overwrite a destructor (dtors), but there were none. I came across a way to overwrite the fini
section of a binary to execute a function when the program was supposed to be quitting. I could not find much documentation on exactly what I needed to overwrite to make this work so I just used objdump
and grep
to find the symbols with fini
in the name:
→ objdump -t greeting | grep fini
08048780 l d .fini 00000000 .fini
08049934 l d .fini_array 00000000 .fini_array
08049934 l O .fini_array 00000000 __do_global_dtors_aux_fini_array_entry
08048740 g F .text 00000002 __libc_csu_fini
08048780 g F .fini 00000000 _fini
Five choices. Through trial and error I determined that overwriting whatever was at __do_global_dtors_aux_fini_array_entry
gave me control of the program.
My plan of attack became the following:
1. Overwrite __do_global_dtors_aux_fini_array_entry
with main
2. Overwrite the GOT entry for strlen
with system
3. Write the full format string line into the program
4. When main executes the second time, write /bin/sh
so that the call to strlen
in the getnline
function executes system("/bin/sh")
and gives me a shell!
I wrote the following script to do the above:
Running it resulted in the flag :)
→ python greet2.py REMOTE
[*] '/home/vagrant/CTF/tokyo/greeting'
Arch: i386-32-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE
[x] Opening connection to pwn2.chal.ctf.westerns.tokyo on port 16317
[x] Opening connection to pwn2.chal.ctf.westerns.tokyo on port 16317: Trying 40.74.112.206
[+] Opening connection to pwn2.chal.ctf.westerns.tokyo on port 16317: Done
[+] Wrote system onto strlen and main onto fini... trying shell
[+] got shell
[+] Flag: TWCTF{51mpl3_FSB_r3wr173_4nyw4r3}
[*] Closed connection to pwn2.chal.ctf.westerns.tokyo port 16317
W00t!
TWCTF{51mpl3_FSB_r3wr173_4nyw4r3}