Chapter 35 Exercises: Buffer Overflows and Memory Corruption
🔐 Security Note: All exercises in this chapter are designed for defensive understanding. They teach you to identify vulnerabilities in code so you can fix them, not to exploit production systems. Perform these exercises only on systems you own or have explicit permission to test.
Warm-Up Exercises
Exercise 35.1 — Stack layout calculation For each function, calculate the offset from the start of the buffer to the return address:
a) A function with char buf[32] and a saved RBP on the stack
b) A function with char buf[100] and no saved RBP (compiled with -fomit-frame-pointer)
c) A function with char buf[16] followed by int x on the stack
d) A function with two buffers: char buf1[16] and char buf2[32]
Assume x86-64 System V ABI, standard stack alignment.
Exercise 35.2 — Dangerous function identification For each code snippet, identify whether it is vulnerable, why, and the fix:
// a)
char buf[128];
gets(buf);
// b)
char dest[64];
strcpy(dest, source);
// c)
char buf[256];
snprintf(buf, sizeof(buf), user_format);
// d)
char buf[64];
strncpy(buf, input, 63);
buf[63] = '\0';
// e)
char buf[128];
sprintf(buf, "Hello, %s!", username);
Exercise 35.3 — Shellcode properties
Answer the following about shellcode requirements:
a) What does "position-independent" mean, and why is it required?
b) Why must shellcode typically be free of null bytes?
c) What instruction(s) are used for x86-64 system calls?
d) What is the syscall number for execve on Linux x86-64?
Analysis Exercises
Exercise 35.4 ⭐ — Disassembly analysis Examine this disassembly and answer the questions:
0000000000401196 <process_input>:
401196: 55 push rbp
401197: 48 89 e5 mov rbp,rsp
40119a: 48 83 ec 50 sub rsp,0x50
40119e: 48 89 7d b8 mov [rbp-0x48],rdi ; param
4011a2: 48 8b 45 b8 mov rax,[rbp-0x48]
4011a6: 48 89 c6 mov rsi,rax
4011a9: 48 8d 45 c0 lea rax,[rbp-0x40] ; buf
4011ad: 48 89 c7 mov rdi,rax
4011b0: e8 xx xx xx xx call strcpy@plt
4011b5: 90 nop
4011b6: c9 leave
4011b7: c3 ret
a) How large is the local buffer buf?
b) What is the offset from buf[0] to the saved RBP?
c) What is the offset from buf[0] to the return address?
d) This function lacks a stack canary. How can you tell from the disassembly?
e) What mitigation would prevent exploitation of the strcpy overflow here?
Exercise 35.5 — Format string analysis Given this vulnerable code:
void log_request(const char *request) {
char buf[256];
snprintf(buf, sizeof(buf), request); // Vulnerable!
puts(buf);
}
a) What is the vulnerability? b) What input could reveal stack contents? c) What input could write to memory? d) Fix the code with two characters of change.
Exercise 35.6 ⭐ — Offset calculation with GDB Describe the step-by-step GDB process for finding the exact offset from a buffer to the return address in a stripped binary (no source code). Include the exact pwndbg commands you would use.
Memory Corruption Identification
Exercise 35.7 — Identify the vulnerability type For each description, identify the vulnerability: (a) stack buffer overflow, (b) heap buffer overflow, (c) use-after-free, (d) double-free, (e) format string vulnerability, (f) integer overflow leading to buffer overflow.
free(p); /* ... */ *p = 1;printf(user_controlled_string);char buf[10]; strcpy(buf, argv[1]);char *p = malloc(n); p[n] = 0;(where n is exactly the allocated size)free(p); free(p);size_t n = user_value * 8;thenmalloc(n)(wherencan overflow)
Exercise 35.8 ⭐ — Heap layout understanding A program makes these allocations in order:
char *a = malloc(16);
char *b = malloc(16);
free(a);
char *c = malloc(16); // What does c point to?
a) In glibc's allocator, what does c likely point to?
b) After free(a), what additional data exists at *a (in the freed chunk)?
c) If code writes strcpy(b, long_string) and long_string is 24 bytes, what gets overwritten?
d) Why is overwriting a heap chunk header dangerous?
Exercise 35.9 — Security code review Review this function for all memory safety issues:
void handle_command(char *cmd, int len) {
char local_buf[64];
char *heap_buf = malloc(len);
memcpy(local_buf, cmd, len); // Issue 1?
memcpy(heap_buf, cmd, len); // Issue 2?
process(local_buf);
free(heap_buf);
printf("Processed: ");
printf(local_buf); // Issue 3?
if (error) {
free(heap_buf); // Issue 4?
}
}
Identify each issue and explain the fix.
Historical Analysis Exercises
Exercise 35.10 — Morris Worm analysis
The Morris Worm (1988) exploited a buffer overflow in fingerd using gets().
a) Why did gets() exist in the C standard library if it was unsafe?
b) In what version of the C standard was gets() deprecated? Removed?
c) What modern functions serve the same purpose safely?
d) The worm also used sendmail and rsh vulnerabilities in combination. Why does combining multiple vulnerability types increase the effectiveness of a worm?
Exercise 35.11 — Historical progression Place these events in chronological order and explain the cause-effect relationship between each pair:
- Introduction of stack canaries (StackGuard)
- Morris Worm (1988)
- Non-executable stack (PaX NX patch)
- Return-Oriented Programming technique described
- Introduction of ASLR in Linux kernel
- Intel CET (Control-flow Enforcement Technology)
- Introduction of PIE executables
Exercise 35.12 — Vulnerability lifecycle For a hypothetical buffer overflow in a network service:
a) Describe the steps an analyst would take to discover the vulnerability in the source code. b) Describe the steps an analyst would take to discover it via fuzzing (no source available). c) What information must the attacker know to exploit it? (offset, target address, ASLR state, etc.) d) What information does the defender need to patch it?
Defensive Coding Exercises
Exercise 35.13 ⭐ — Secure coding rewrite Rewrite this vulnerable function to eliminate all memory safety issues:
void process_username(const char *username) {
char buf[32];
char log_entry[128];
strcpy(buf, username); // Fix this
sprintf(log_entry, "User: %s", buf); // Fix this
printf(log_entry); // Fix this
char *detail = malloc(strlen(username));
strcpy(detail, username); // Fix this
process(detail);
// missing free -- Fix this
}
Exercise 35.14 — Compiler flags for security
Compile a simple C program with each of the following flags and explain what protection each adds. Use checksec to verify the resulting binary has the expected properties:
a) -fstack-protector-strong
b) -D_FORTIFY_SOURCE=2 -O2
c) -fsanitize=address
d) -pie -fPIE
e) -z relro -z now
Exercise 35.15 — AddressSanitizer usage
Write a simple C program that has a buffer overflow (for testing purposes on your own machine), compile it with -fsanitize=address, and interpret the AddressSanitizer output report. What information does ASAN provide that GDB alone does not?
Deep-Dive Exercises
Exercise 35.16 ⭐ — Assembly-level prevention Explain what each of the following assembly sequences in a function prologue/epilogue is defending against, and what mitigation added it:
; Sequence A (in prologue):
mov rax, [fs:0x28]
mov [rbp-8], rax
; Sequence B (in epilogue):
mov rax, [rbp-8]
xor rax, [fs:0x28]
jne __stack_chk_fail
; Sequence C:
endbr64 ; at the start of every function
Exercise 35.17 — Format string primitives Without writing exploit code, explain theoretically:
a) How does %K$p (the K-th argument positional specifier) allow reading a specific stack offset?
b) Why does %n allow writing to memory, and why is this dangerous even when the write target is the attacker's own stack variable?
c) Why are format string vulnerabilities often used as information leaks (reading canaries) before attempting other attack steps?
Exercise 35.18 — Heap grooming concept Explain what "heap grooming" or "heap feng shui" means conceptually:
a) Why would an attacker want to control the layout of heap allocations? b) What allocation and free patterns could be used to put a specific object adjacent to a controlled buffer? c) Why is this technique more complex than stack buffer overflows?
Exercise 35.19 — Cross-platform comparison How do buffer overflow mechanics differ on these platforms:
a) x86-64 Linux vs. ARM64 Linux (stack layout, calling convention effects) b) x86-64 Linux vs. Windows x64 (shadow space, different calling convention) c) 32-bit x86 vs. 64-bit x86-64 (address size, canonical address requirements)
Exercise 35.20 ⭐ — Vulnerability triage You are reviewing a security audit report that lists three vulnerabilities in a C server: 1. Stack buffer overflow in the HTTP header parsing, offset 256 bytes 2. Use-after-free in the connection handler when a client disconnects during parsing 3. Format string in the error logging function, only triggered on malformed requests
Without ASLR and without stack canaries: which is most easily exploitable? With full modern mitigations (ASLR + canary + NX + RELRO): which is most dangerous? Explain your reasoning.