Appearance
45. Buffer Overflow
Occurs when a program writes more data into a buffer than it can hold, overwriting adjacent memory.
Stack layout (why it's dangerous): local buffers sit below the saved base pointer and return address on the stack. Overflowing a local buffer with enough data overwrites the return address itself.
c
void vulnerable() {
char buf[8];
gets(buf); // reads unlimited input — never use this
}A 20-byte malicious input can overwrite the return address with an attacker-chosen value (e.g., pointing at injected shellcode) — when the function returns, the CPU jumps there instead.
Even without full shellcode, overflowing past a buffer can flip an adjacent flag variable (e.g., an authenticated int placed after password[16] on the stack), granting access without the correct password.
Defenses:
- Stack canary — a random value placed between the buffer and the return address; checked before returning, aborting if it changed (
gcc -fstack-protector). - ASLR — randomizes stack/heap/library/executable base addresses so an attacker can't hardcode a jump target.
- NX bit / DEP — marks the stack and heap non-executable; injected shellcode simply can't run.
- Safe functions —
fgets/strncpy/snprintfwith explicit bounds instead ofgets/strcpy/sprintf. - Bounds-checked languages — Rust, Java (throws on out-of-bounds), Python (no manual memory management) versus C/C++ (no bounds checking at all).
Attack evolution (bypassing defenses): NX blocks injected shellcode → attackers use return-to-libc (jump to existing code like system(), passing /bin/sh). ASLR randomizes addresses → attackers use ROP (Return-Oriented Programming), chaining small existing code snippets ("gadgets") already present in the binary.