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.

  1. free(p); /* ... */ *p = 1;
  2. printf(user_controlled_string);
  3. char buf[10]; strcpy(buf, argv[1]);
  4. char *p = malloc(n); p[n] = 0; (where n is exactly the allocated size)
  5. free(p); free(p);
  6. size_t n = user_value * 8; then malloc(n) (where n can 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.