Worksheet 11 — Memory-Safety & Exploitation (3 hrs)
← all weeks · readme · slides
Worksheet 11 — Memory-Safety & Exploitation (3 hrs)
Course: Software Security (KOSEN69) · Week 11 Aligned: CWE-121 (stack-based buffer overflow) · CWE-787 (out-of-bounds write) · CWE-134 (externally-controlled format string) · CWE-242 (dangerous
gets) Signature game: 💥 Fuzzing Race → Pwn the Binary Ethics note: The binaries built fromvuln.care intentionally exploitable andwin()spawns/bin/sh. Build and run them only inside the isolated toolbox container (or a VM) — never on your host. Never run these on a machine you care about, and never aim these techniques at software you are not authorized to test.
Part 1 — Student Information
| Name | Student ID | Date | Group |
|---|---|---|---|
Part 2 — Lecture Questions
- Draw the x86-64 stack frame for
parse_input()invuln.c(buf[64], saved RBP, saved return address). Where doesstrcpystart writing and which word must the exploit overwrite? - Why is
gets()(CWE-242) impossible to use safely, and why did C11 remove it from the standard library? - Explain CWE-134: why is
printf(user_input)dangerous butprintf("%s", user_input)safe? What can%ndo? - Name the four mitigations in the hardened build (
make vuln-hardened) — stack canary, PIE/ASLR, NX/DEP, FORTIFY — and say which one each defeats: ret2win, shellcode-on-stack, return-address overwrite detection. - The CISA/ONCD roadmaps push memory-safe languages. Using
safe.rs, explain why CWE-121 is impossible in safe Rust, not merely harder.
Part 3 — Hands-on Lab (145 min)
Learning goals: Find the overflow with a coverage-guided fuzzer, exploit it (ret2win), watch mitigations break the exploit, then port the routine to safe Rust.
Prerequisites: the toolbox container (bundles clang+libFuzzer, gdb, objdump, python3) — Apple clang has no libFuzzer and gdb is painful on macOS, so do this week in the container. (pip install pwntools and rustc inside as needed; an isolated Linux VM also works.)
Environment setup
# from the repo root, build the toolbox once, then open a shell IN this week's folder:
docker build -t softsec-toolbox labs/toolbox
docker run -it --rm --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
-v "$PWD/labs/week11-memory-safety-exploitation":/work -w /work softsec-toolbox
# --- inside the container ---
make vuln # teaching build: mitigations OFF
make vuln-hardened # mitigations ON
clang -g -fsanitize=address,fuzzer fuzz_harness.c -o fuzz # libFuzzer + ASan
setarch `uname -m` -R ./vuln # disable ASLR for repeatable addresses
What to submit per task: the exact command(s) + crash/exploit output, a screenshot, and a 2–3 sentence mitigation note tied to the CWE.
Task 0 — Onboarding (15 min)
Run make syntax (parses both C files even on macOS) and confirm make vuln / make vuln-hardened build. Read the bug-class banner in vuln.c. Deliverable: build output + the list of CWEs vuln.c claims to demonstrate.
Task 1 — Fuzzing Race: find the crash (35 min)
Goal: Make libFuzzer + AddressSanitizer crash on the strcpy overflow in parse_input() — CWE-121/787. Steps:
./fuzz -runs=100000(or just./fuzz).- Read the ASan report — note
stack-buffer-overflowinsideparse_inputand the savedcrash-<hash>file. - Replay it:
./fuzz crash-<hash>.
Deliverable: the ASan crash summary + the byte length of the crashing input + mitigation note.
Task 2 — Pwn the Binary: ret2win (40 min)
Goal: Overwrite the saved return address so the binary returns into win() and prints FLAG{stack_smashing_for_fun_and_education} — CWE-121. Steps:
- Find
win()'s address:objdump -d vuln | grep '<win>:'(orgdb -batch -ex 'p win' ./vuln). - Verify the offset with a cyclic pattern (do not trust 72 blindly): feed
cyclic(200), crash in gdb,cyclic_findthe value in RIP. - Edit
exploit_skeleton.py: setWIN_ADDRto the address from step 1, confirmOFFSET. python3 exploit_skeleton.py(pwntools path) — orpython3 exploit_skeleton.py | ./vuln.
Deliverable: the verified OFFSET, your WIN_ADDR, and the printed flag / [+] win() reached line.
Task 3 — Hardened rebuild: watch the exploit break (25 min)
Goal: Observe modern mitigations defeating the same payload. Steps:
- Run your Task 2 payload against
./vuln-hardened. - Record the failure mode:
*** buffer overflow detected ***: terminated(FORTIFY_SOURCE's__strcpy_chk, which on this build catches the overflow before the stack canary's epilogue check ever runs), or a SIGSEGV at a randomized address (PIE/ASLR makesWIN_ADDRwrong).
Deliverable: the crash message + a note explaining which mitigation fired and why the fixed WIN_ADDR no longer works.
Task 4 — Rust rewrite: defend / fix it (30 min)
Goal: Show the bug class disappears in safe Rust using safe.rs. Steps:
rustc safe.rs && ./safe, thenpython3 -c "print('A'*5000)" | ./safe— note it never overflows or crashes.- Feed format-string metacharacters:
printf '%%x %%n' | ./safe— note the program just reports the character count with no crash and no stack leak; the input is treated as ordinary data (CWE-134 impossible:println!'s format string must be a compile-time literal, soprintln!(line)would not even compile). - From the inline comments, write why
String/&str+ bounds checks make CWE-121/787 impossible.
Deliverable: the safe output for both inputs + a 3–4 sentence explanation tying it to the CISA memory-safe roadmap.
Part 4 — Reflection
- Mapping: table — bug | function in
vuln.c| CWE | mitigation that stops it (canary / PIE / NX / Rust). - Real breach: pick a memory-safety incident (Morris Worm fingerd overflow, or Heartbleed CVE-2014-0160). Which CWE applies, and would a stack canary or a Rust rewrite have prevented it?
- Best mitigation: compare "harden the C build" vs. "rewrite in Rust." Which gives durable risk reduction for new code, and what is the cost?
Grading rubric (100)
| Criterion | Weight |
|---|---|
| Lecture questions (Part 2) | 20 |
| Exploitation + evidence (Tasks 1–3: fuzz crash, ret2win flag, hardened break) | 40 |
| Defense (Task 4: Rust rewrite + explanation) | 25 |
| Reflection (Part 4: mapping, breach, best mitigation) | 15 |
| Total | 100 |
Evidence & Integrity (required)
- Identity proof: every screenshot/diagram must show your **
whoami/ login email / student ID and a timestamp**. Generic or borrowed evidence is not accepted. - Personalized flag (if this lab issues one): ____________________
Flags are unique per student — submitting another student's flag is a violation. See SUBMISSION.md.
- Explain in your own words (graded on your reasoning, not copied text):
- What did you do, and why did the vulnerability work?
- Why does your fix actually stop it — and what could still break it?
🤖 Audit the AI (required)
AI is a power tool you must distrust — you are graded on your critique, not the AI's answer.
- Ask an AI assistant to exploit or fix this week's vulnerability. Paste its full answer.
- Find what's wrong or risky in it — insecure code, a subtly incomplete fix, a hallucinated API/function/CVE, a missed edge case, or wrong reasoning. Quote the exact line(s).
- Produce the correct, verified version yourself and explain in 2–3 sentences why the AI's output was insufficient.
Disclose your AI use in the Part 1 table. This task counts toward your Defense + Reflection score.
🧠 Comprehension & Prompt (required)
A. Explain in Plain English (EiPE). In 2–3 sentences, in your own words, describe what this week's vulnerable code/endpoint actually does and why it is exploitable — explain the mechanism, don't dump jargon.
B. Prompt Problem. Write a single prompt that makes an AI produce a correct, secure fix for one finding. Run it: does the exploit now fail? If not, refine the prompt and try again. Submit the final prompt + the verified result. Graded on the prompt's precision and your verification — this trains problem decomposition and AI literacy (Denny et al. 2024).