Skip to main content

All weeks · Worksheet · Overview

Week 11 · Lecture slides

Week 11

Contents14 sections

Memory-Safety & Exploitation

Software Security · Nutthakorn Chalaemwongwan


Today

  • The C/C++ memory model & the stack
  • Finding bugs with fuzzing
  • Stack overflow → control hijack
  • Mitigations + the memory-safe language shift
  • 🎮 Game: Fuzzing Race → Pwn the Binary

Why this still matters

  • C/C++ runs the world's critical infrastructure
  • Memory bugs = ~70% of severe CVEs historically
  • Now a national-policy issue (CISA/ONCD roadmaps)

The stack frame

  • gets/strcpy/unchecked memcpy → overflow
  • Overwrite return address → redirect execution
What a stack canary detects and what FORTIFY_SOURCE prevents (Week 11) — open full size

Bug classes

  • CWE-121 stack overflow · CWE-787 OOB write
  • CWE-134 format string · CWE-242 dangerous function (gets())
  • (this week's binary: no UAF, no off-by-one — those are real bug classes but not in this lab's code)

Fuzzing — how bugs are found today

clang -g -fsanitize=address,fuzzer fuzz_harness.c -o fuzz && ./fuzz   # libFuzzer
afl-fuzz -i seeds -o out -- ./vuln @@                                  # AFL++
  • Coverage-guided mutation finds crashes fast
  • Pair with sanitizers (ASan) for root cause

Exploiting a stack overflow

  1. Find offset to return address with a cyclic pattern — verify it, don't assume it: this build's offset is 72 bytes, but enabling the stack canary alone moves it to 80 (the canary word sits between buffer and return address)
  2. Overwrite RA → jump to win()
  3. Format string: %x%x%x leak, %n write

Mitigations raise the bar

  • FORTIFY_SOURCE — checks the copy itself, at call time (__strcpy_chk)
  • Stack canaries — detect overwrite at function return, after the copy already happened
  • ASLR / PIE — randomize addresses
  • NX/DEP — blocks executing shellcode planted on the stack — irrelevant to this exploit, which returns into existing code (win()), never injects shellcode
  • On this lab's hardened build, FORTIFY fires first — *** buffer overflow detected ***: terminated. The canary never gets a chance to speak. Verify the order yourself before teaching it as "canary catches it" — that's the wrong answer this lab's own materials were rewritten to correct.

From strcpy to shell — where each mitigation cuts the chain

A vertical attack chain from fuzzing finding the crash, to the unchecked strcpy overflow, to the overwritten return address, to a ret2win shell. Three mitigations cut this chain at different links: FORTIFY_SOURCE aborts at copy time, before any overwrite — this hardened build traps here, which students often misread as the canary. The stack canary only detects the smash at return, after the copy already happened. ASLR/PIE randomizes addresses so the fixed win() address is wrong. NX/DEP stops the OTHER exploitation path (injected shellcode), not this one.


The real fix: memory-safe languages

  • Rust / Go remove whole bug classes by design
  • CISA "Secure by Design" + ONCD: move off C/C++ for new code
  • Borrow checker / bounds checks = no overflow, no UAF

💥 Game — Fuzzing Race → Pwn the Binary

  1. Round 1 (Fuzzing Race): first team to crash the target wins
  2. Round 2 (Pwn): exploit the overflow / format string
  3. Round 3 (Defend): rebuild with canary+ASLR+PIE, then rewrite in Rust

Deliverable

📋 Worksheet 11 — labs/week11-memory-safety-exploitation/worksheet.md (Part 3) · kickoff: in the toolbox container (labs/toolbox — Apple clang has no libFuzzer runtime): clang -g -fsanitize=address,fuzzer fuzz_harness.c -o fuzz && ./fuzz

  • Fuzzing crash + exploit script
  • Annotated Ghidra/gdb analysis
  • Memory-safe (Rust) rewrite + why the bug is now impossible
  • + Audit the AI / EiPE / Prompt Problem (see worksheet)

Key takeaways

  • Fuzz to find, debug to understand, mitigate to slow attackers
  • Mitigations ≠ cure — memory-safe languages are the cure
  • This is where the industry is moving

Questions?

Next week: Software supply-chain security

All weeks in Software Security