Keamanan Siber

BroncoCTF Proper Pwning: Eksploitasi Buffer Overflow Berulang dan Penyelarasan Stack Modern

Ringkasan

  • Writeup teknis mendalam tantangan BroncoCTF Proper Pwning mengungkap eksploitasi buffer overflow berulang melalui empat gerbang, serta solusi penyelarasan stack 16-byte untuk memanggil system() pada glibc modern tanpa SIGSEGV.

Komunitas keamanan siber internasional kembali digerakkan oleh tantangan binary exploitation dari BroncoCTF yang bertajuk "Proper Pwning". Tantangan ini menawarkan pelajaran mendalam mengenai kerentanan klasik buffer overflow yang dieksploitasi melalui empat lapisan fungsi berturut-turut, serta mengungkap kompleksitas tersembunyi dalam memanfaatkan fungsi system() pada lingkungan glibc modern. Writeup yang dipublikasikan oleh exploitnotes di platform Dev.to merinci proses reverse engineering, perhitungan offset presisi, hingga penanganan penyelarasan stack 16-byte yang menjadi titik kegagalan banyak eksploit ret2win kontemporer.

File arsip proper.zip yang disediakan berisi empat berkas utama: Dockerfile, flag.txt, binary proper, dan kode sumber proper.c. Analisis Dockerfile mengungkap konfigurasi kompilasi yang sengaja melemahkan proteksi memori: flag -fno-stack-protector menonaktifkan stack canary, -z execstack membuat stack dapat dieksekusi, dan -no-pie menghilangkan Position Independent Executable. Hasil checksec mengonfirmasi Partial RELRO, tidak ada canary, NX disabled, dan No PIE — kondisi ideal untuk demonstrasi eksploitasi buffer overflow murni tanpa perlu infobocoran alamat memori.

Arsitektur program mengimplementasikan rantai empat fungsi yang harus dilalui berurutan: main() memanggil gate1(), lalu gate2(), gate3(), dan akhirnya treasure_room(). Setiap fungsi gate menggunakan gets() untuk membaca input pengguna tanpa pemeriksaan batas, menciptakan empat titik kerentanan buffer overflow independen dengan tata letak variabel lokal yang berbeda. Fungsi win() yang menjadi target akhir memanggil system("/bin/cat flag.txt") untuk menampilkan bendera, namun hanya dapat dijangkau setelah ketiga gate berhasil dilewati dan alamat kembali (return address) di treasure_room() ditimpa dengan alamat win().

Penentuan offset presisi menjadi krusial karena kompiler C tidak menjamin urutan deklarasi variabel mencerminkan tata letak memori stack. Penulis menggunakan objdump dengan sintaks Intel untuk mendisassemble setiap fungsi dan mengukur offset langsung dari kode mesin. Di gate1, offset buffer ke variabel gate adalah 268 byte; gate2 memerlukan preservasi baby_chicken (nilai 41) pada offset 520 byte sebelum menimpa gate pada offset 524 byte; gate3 menuntut nilai tepat 13371337 (0xcc07c9) pada offset 76 byte. Treasure_room mengalokasikan 6768 byte untuk buffer, diikuti 8 byte saved RBP, lalu 8 byte return address — total 6784 byte sebelum mencapai alamat kembali.

Eksploitasi awal berhasil melewati ketiga gate dan memasuki fungsi win(), mencetak pesan "oh my goodness, you're the greatest C pwner of all time...", namun program berhenti dengan SIGSEGV (exit code -11) sebelum system() sempat mengeksekusi /bin/cat flag.txt. Penyebabnya adalah ketidaksejajaran stack 16-byte yang dibutuhkan instruksi SSE (movaps) di dalam implementasi system() glibc modern (Ubuntu 18.04+). Melompat langsung ke win() melalui return address yang ditimpa meninggalkan stack pointer dengan paritas salah sebesar 8 byte, memicu crash di dalam system() sebelum pemanggilan eksternal terjadi.

Solusi klasik untuk masalah ini adalah menyisipkan satu gadget ret (instruksi 0xc3) sebelum alamat win() dalam payload. Gadget ret ini hanya mem-pop satu nilai 8-byte dari stack dan melompat lagi, secara efektif menggeser stack pointer 8 byte untuk mencapai penyelarasan 16-byte yang dibutuhkan. Penulis menemukan gadget ret bebas di akhir disassembly gate1 pada alamat 0x40141a, yang kemudian digunakan sebagai alignment nudge tanpa mengubah logika program.

Skrip eksploit Python menggunakan library pwntools dengan arsitektur amd64. Payload gate1 mengirim 268 byte 'A' diikuti p32(1) untuk mengatur gate != 0. Gate2 mengirim 520 byte 'A', p32(41) untuk mempertahankan baby_chicken, dan p32(1) untuk gate. Gate3 mengirim 76 byte 'A' dan p32(13371337). Payload final treasure_room mengisi 6768 byte 'A', 8 byte 'B' sebagai junk RBP, p64(0x40141a) sebagai ret gadget, dan p64(0x40123b) sebagai alamat win(). Skrip mendukung eksekusi lokal maupun remote via argumen host dan port.

Tantangan ini mengilustrasikan betapa kerentanan klasik seperti gets() tetap relevan dalam pendidikan keamanan ofensif, serta menegaskan pentingnya memahami detail implementasi library standar modern. Bagi praktisi keamanan siber di Indonesia, kasus ini menjadi pengingat bahwa eksploitasi biner tidak hanya soal menimpa memori, melainkan juga menguasai ABI, calling convention, dan perilaku runtime library. Pengetahuan tentang penyelarasan stack, gadget ROP minimal, dan debugging post-exploitation menjadi keterampilan wajib yang memisahkan analis junior dari engineer eksploitasi handal.

Mengapa Ini Penting

Tantangan ini menunjukkan bahwa kerentanan klasik gets() tetap menjadi fondasi pendidikan eksploitasi biner modern, namun keberhasilan eksploitasi nyata kini bergantung pada pemahaman mendalam tentang ABI x86-64 dan perilaku runtime glibc. Bagi industri keamanan siber Indonesia, kasus ini menekankan perlunya kurikulum yang tidak hanya mengajarkan teknik menimpa memori, tetapi juga debugging post-exploitation, penyelarasan stack, dan pemanfaatan gadget ROP minimal. Keterampilan ini krusial untuk red teamer, pembuat eksploit, dan analis kerentanan yang harus berhadapan dengan binary yang dikompilasi dengan proteksi parsial di lingkungan produksi nyata.

Sumber Asli
Dev.to - exploitnotes
Tanggal
12 Juli 2026
Waktu Baca
5 menit