CMM7XX
Software Security
Session 7: Vulnerabilities and Protections
Learning Objectives
- Deconstruct memory corruption vulnerabilities (Stack Smashing, Heap Exploitation).
- Understand advanced exploitation techniques like Return-Oriented Programming (ROP).
- Evaluate OS-level mitigations (ASLR, DEP/NX, Stack Canaries, CFI).
- Apply Secure SDLC principles, including Fuzzing and SAST/DAST integration.
- Analyze C code to identify architectural and memory flaws.
Seminar Structure (3 Hours)
Part 1: Memory Corruption & Exploitation (1.5 hrs)
- Stack and Heap Architecture
- Buffer Overflows & Return-Oriented Programming
- Task 1: Binary Exploitation Analysis
Part 2: Mitigations & Secure SDLC (1.5 hrs)
- OS Defenses (ASLR, DEP, CFI)
- Secure Development (SAST, DAST, Fuzzing)
- Task 2: Bypassing Defenses (ROP Chains)
Part 1: Memory Corruption & Exploitation
Duration: 1.5 Hours
Process Memory Architecture
When a program runs, the OS allocates virtual memory, typically divided into:
- Text Segment: Executable machine instructions (Read-Only).
- Data/BSS Segments: Initialized and uninitialized global variables.
- Heap: Dynamically allocated memory (e.g.,
malloc()). Grows upward.
- Stack: Local variables, function parameters, and return addresses. Grows downward (in x86/x64).
Stack Smashing (Buffer Overflow)
Occurs when data written to a buffer exceeds its allocated boundary, overwriting adjacent memory locations.
The Target: The Return Address (EIP/RIP).
- When a function is called, the CPU pushes the address of the next instruction onto the Stack.
- If an attacker overflows a local variable, they can overwrite this return address.
- When the function exits (
ret), the CPU jumps to the attacker's injected address (e.g., pointing to malicious shellcode).
Return-Oriented Programming (ROP)
What if the attacker cannot inject shellcode into the stack because the stack is marked as non-executable?
- ROP: The attacker searches the existing executable (and linked libraries like
libc) for small sequences of instructions ending in a ret. These are called Gadgets.
- Instead of injecting code, the attacker overflows the stack with a chain of return addresses pointing to these gadgets.
- The CPU executes the existing code in an order defined by the attacker to achieve malicious goals (e.g., calling
system("/bin/sh")).
Introduction to Task 1
To defend against memory corruption, you must be able to think like a reverse engineer and understand how data maps to the stack.
We will analyze a vulnerable C application to calculate exactly how many bytes are needed to hijack the control flow.
Task 1: Binary Exploitation Analysis
Timing: 45 minutes
You are provided with the source code of a C program utilizing the unsafe strcpy() function.
- Draw a diagram of the Stack frame for the vulnerable function, labeling the Buffer, the saved Frame Pointer (EBP), and the Return Address (EIP).
- Calculate the exact buffer offset required to begin overwriting the Return Address (accounting for architecture alignment).
- Construct a theoretical hex payload consisting of a NOP sled, placeholder shellcode, and the overwritten Return Address.
15 Minute Break
Please return promptly for Part 2.
Part 2: Mitigations & Secure SDLC
Duration: 1.5 Hours
OS-Level Mitigations
Modern operating systems employ deep architectural defenses against exploitation.
- DEP/NX (Data Execution Prevention / No-eXecute): Marks memory regions (like the Stack and Heap) as non-executable. Stops traditional shellcode injection.
- ASLR (Address Space Layout Randomization): Randomizes the memory locations of the stack, heap, and libraries (like
libc) every time the program runs. Prevents attackers from hardcoding return addresses.
- Stack Canaries: A random value placed between local variables and the return address. Checked before the function returns; if altered, the program crashes to prevent hijacking.
Control-Flow Integrity (CFI)
An advanced compiler-level mitigation against ROP and indirect pointer overwrites.
- Before compilation, the compiler maps out the legitimate Control Flow Graph (CFG) of the program.
- It inserts runtime checks before every indirect branch (function pointers, virtual tables).
- If a program attempts to jump to a gadget or address not explicitly permitted in the CFG, the program terminates.
- Hardware-assisted CFI (e.g., Intel CET) is becoming the new industry standard.
Secure SDLC & Automated Testing
Mitigations fail; preventing the vulnerability in the code is always the priority.
- SAST (Static Application Security Testing): Analyzes source code for flaws (e.g., finding
strcpy) without executing it. Fast, but prone to false positives.
- DAST (Dynamic Application Security Testing): Interacts with the running application to find flaws (e.g., injecting SQL payloads). Cannot see internal code.
- Fuzzing: Feeding massive amounts of malformed, unexpected, or random data to a program's inputs to deliberately cause a crash, revealing memory corruption bugs.
Introduction to Task 2
Security is an arms race. When defenders introduced DEP/NX, attackers invented ROP. When defenders introduced ASLR, attackers invented Info Leaks.
We will architect a theoretical ROP chain to bypass DEP.
Task 2: Bypassing Defenses (ROP Chains)
Timing: 50 minutes
Assume the target system has DEP enabled, but ASLR is temporarily disabled.
- Using the provided list of assembly "gadgets" extracted from the binary's memory, design a ROP chain.
- Your chain must sequentially pop the address of the string
"/bin/sh" into the RDI register, and then jump to the address of the system() function.
- Discussion: Explain how turning ASLR back on would completely break your ROP chain, and discuss what an "Information Leak" vulnerability is and how it helps bypass ASLR.
Summary & Next Steps
- Memory corruption remains a critical threat in systems-level programming (C/C++). Memory-safe languages (Rust) are the ultimate mitigation.
- Modern exploitation requires chaining multiple vulnerabilities (e.g., Info Leak + Buffer Overflow + ROP) to bypass OS mitigations.
Next Week: Malware Detection and Firewalls (Perimeter Defense)