Sisi gelap yang wajib dipahami setiap assembly programmer: buffer overflow dan layout stack, Return-Oriented Programming, konsep shellcode, serta mitigasi modern NX, ASLR, stack canary, dan PIE — termasuk cara memeriksa binary dengan checksec dan menulis shellcode syscall execve di lingkungan lab yang aman.

Semua yang kalian pelajari tentang stack (episode 8), memori (episode 11), dan alamat kembali (episode 6) kini bermuara di episode yang paling penting untuk dipahami — bagaimana sistem bisa diretas, dan bagaimana sistem bertahan. Episode 17 membedah buffer overflow, ROP, shellcode, dan mitigasi yang membuat semua itu jauh lebih sulit dilakukan.
Warning
Episode ini bersifat edukasi defensif: memahami cara kerja exploit adalah syarat untuk menulis mitigasi yang benar. Praktikkan hanya di lingkungan yang kalian miliki — VM, lab pribadi, CTF, atau bug bounty yang disetujui. Pengetahuan ini tidak untuk menyerang sistem milik orang lain.
Mengapa penting? Karena semua mitigasi modern — NX, ASLR, canary, PIE — pada dasarnya mengubah layout memori yang kalian pelajari di episode 8. Kalau kalian tidak paham mengapa mitigasi itu ada, kalian tidak akan paham mengapa binary yang kalian reverse di episode 16 terlihat "aneh".
Ingat strcpy kita di episode 11 — fungsi yang menulis tanpa memeriksa batas. Ketika buffer di stack terlalu kecil dan diisi data lebih panjang, data menimpa data lain di stack. Dari episode 8, layout stack sebuah fungsi:
+--------------------+
| alamat kembali | <- target exploit: jika ditimpa,
+--------------------+ ret melompat ke alamat penyerang
| rbp pemanggil |
+--------------------+
| buffer[16] | <- input penyerang masuk dari sini
+--------------------+ (tumbuh dari alamat rendah ke atas)#include <stdio.h>
#include <string.h>
void vulnerable(char *input) {
char buffer[16];
strcpy(buffer, input); // TIDAK cek panjang!
printf("Isi: %s\n", buffer);
}
int main(void) {
vulnerable("AAAA");
return 0;
}Input "AAAAAAAAAAAAAAAAAAAAAAAA" (24 A) menimpa buffer (16 byte) dan alamat kembali — program crash saat ret melompat ke 0x4141414141414141. Coba:
gcc -no-pie -fno-stack-protector -z execstack vuln.c -o vuln
./vuln $(python3 -c "print('A'*24)")
# Segmentation fault (core dumped)Perhatikan flag yang sengaja kita matikan: -fno-stack-protector (tanpa canary), -z execstack (tanpa NX), -no-pie (tanpa ASLR untuk binary). Ketiganya adalah mitigasi yang di episode ini akan kita aktifkan kembali.
Jika alamat kembali di-overwrite ke buffer, dan buffer berisi instruksi assembly — CPU akan mengeksekusi buffer tersebut. Inilah shellcode: kode mesin yang di-inject. Contoh klasik adalah membuka shell (execve("/bin/sh", NULL, NULL)):
; execve("/bin/sh", 0, 0)
xor rdx, rdx ; rdx = 0
lea rbx, [rel path] ; rbx = alamat "/bin/sh"
xor rsi, rsi ; argv = NULL
mov rdi, rbx ; filename = rbx
mov rax, 59 ; syscall execve
syscall
path:
db "/bin/sh", 0Untuk mendapatkan bentuk byte murni, assemble lalu ekstrak .text:
nasm -f bin shellcode.asm -o shellcode.bin
objdump -d -M intel -b binary -m i386:x86-64 shellcode.bin
# atau sederhananya: xxd -p shellcode.binNote
Shellcode nyata sering mengandung trik eliminasi null byte (karena strcpy berhenti di 0x00) — inilah mengapa pelajaran tentang encoding dan flag di episode 7 sangat relevan. Shellcode harus bisa melewati fungsi string yang menjadi pintu masuknya.
Ketika NX aktif, stack tidak bisa dieksekusi — shellcode mati. Penyerang kemudian memakai gadget: potongan instruksi yang diakhiri ret yang sudah ada di binary (biasanya dari libc). Rantai gadget disusun berurutan di stack, dan setiap ret melompat ke gadget berikutnya — semuanya mengeksekusi kode yang sah tanpa menanam kode baru.
stack: [gadget1_addr][gadget2_addr][gadget3_addr]...
pop rdi; ret → nilai argumen
system → system("/bin/sh")Ini alasan mengapa mitigasi NX saja tidak cukup — ROP tidak mengeksekusi data, ia mengeksekusi kode yang memang sudah ada. Mitigasi tambahan (ASLR, PIE, CFI) dibuat untuk mempersulit menyusun gadget dan alamat yang valid.
Menandai page stack sebagai tidak dapat dieksekusi. Dengan NX, buffer overflow tidak bisa mengeksekusi shellcode di stack. Di kernel: W^X (write XOR execute).
Me-randomisasi alamat stack, heap, dan library setiap kali program dijalankan. Alamat libc dan stack berubah tiap run, sehingga alamat target yang di-hardcode tidak berlaku. PIE (Position-Independent Executable) memperluas ASLR ke binary utama — lihat episode 19.
Menaruh nilai rahasia (canary) antara buffer dan alamat kembali. Sebelum ret, fungsi memeriksa canary; jika berubah (karena overflow), fungsi langsung abort():
; gcc -fstack-protector-all
mov rax, fs:0x28 ; muat canary dari TCB
mov QWORD PTR [rbp-8], raxBinary dikompilasi sebagai PIE sehingga seluruh executable ikut di-randomisasi alamatnya, bukan hanya stack/heap/libc. Dengan PIE + ASLR, alamat _start, fungsi, dan gadget semuanya bergeser setiap run.
checksec (dari pwntools) merangkum status mitigasi sebuah binary:
pip install pwntools
checksec --file=./vulnOutput contoh:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabledSebagai perbandingan, compile versi aman dari vuln.c:
gcc -O2 vuln.c -o vuln_safe
checksec --file=./vuln_safe
# Stack: Canary found
# NX: NX enabled
# PIE: PIE enabledKombinasi keempatnya — NX + ASLR + canary + PIE — adalah standar distribusi modern. Inilah mengapa exploit dari era tahun 2000-an umumnya tidak lagi bekerja di sistem 2026 tanpa tambahan kerja keras (leak alamat, mem-bypass canary, dst).
Important
Bukan hanya binary yang perlu mitigasi: menulis C yang aman (pakai strncpy/snprintf/bounds-checking, ukuran buffer selalu diteruskan) tetap kewajiban utama. Mitigasi memperlambat penyerang; kode yang benar menghentikannya sejak awal. Baca materi OWASP dan CWE-121/122 untuk daftar lengkapnya.
| Serangan | Mitigasi Utama |
|---|---|
| Inject shellcode di stack | NX (stack tidak executable) |
| Alamat return statis | ASLR + PIE |
| Menimpa return address diam-diam | Stack canary |
| Reuse gadget dari libc | ASLR (posisi libc acak) + CFI/IBT |
| Menulis format string / memori acak | RELRO (halaman read-only) |
strcpy, gets) → ganti strncpy/fgets atau lebih baik Rust/GC.memcpy/read — terima panjang yang diverifikasi.Pada episode 17 ini, kalian telah memahami sisi gelap yang menentukan desain sistem keamanan modern.
Inti yang harus dibawa pulang:
ret yang sudah ada — mengapa NX saja tidak cukup.checksec.Di episode 18 selanjutnya, kita berpindah dari keamanan ke performa: optimasi kinerja & profiling — loop unrolling, register allocation, alignment, cara menghindari branch misprediction, serta tool profiling perf, gprof, dan timing presisi rdtsc. Setelah memahami bagaimana sistem bisa rusak dan dilindungi, saatnya membuatnya berjalan secepat mungkin.