[Cyber Jawara 2019 Qualification] – Starlight

Diberikan sebuah zip yang berisi binary. language directory dan juga source code dari soal yang dapat didonwload di sini.

Disclaimer: Penulis tidak melakukan solve pada jalannya kompetisi dikarenakan oleh ketidak telitian penulis dalam mengerjakan soal ini. Writeup ini dibuat untuk menjadi sebuah refleksi pembelajaran dari kegagalan yang telah dilakukan.

Part I: Percobaan Analisis

Karena soal memiliki source, kita dapat melakukan analisis terhadap source yang diberikan. Pada bagian comment diberikan juga cara compiling dari binary yang menunjukkan bahwa semua security option digunakan (checksec full hijau). Jika kita lihat source codenya, tentu hal paling menarik adalah fungsi show_flag(). Mari kita coba lihat cara kerja fungsi ini.

Garis besar fungsi ini adalah kita harus memasukkan input berupa password yang nantinya akan dicek dengan file yang ada di remote service bernama “password.txt”. Ketika passwordnya cocok, flag akan dioutput. Pada fungsi ini, semuanya terlihat aman dari serangan apapun. Coba kita analisis bagian lain dari source code.

Fungsi main hanya memanggil 2 fungsi lainnya yaitu init dan service.

Bagian init hanya melakukan inisiasi pada buffer dan memberikan sigalarm 60 detik.

Fungsi service pertama akan menampilkan pilihan bahasa yang bisa kita input, lalu inputan kita akan diappend dengan languages/[inputan].lang yang menggunakan fungsi snprintf. Setelah itu path akan dibaca dan diprint ke stdout. Setelah itu akan ada inputan menu yang bisa kita input yaitu 1,2 atau 3. Pilihan 1 dan 2 hanya mengeprint segitiga dengan bintang. Sedangkan pilihan 3 akan membuka fungsi show_flag yang sudah kita analisis.

Part II: Ideas & Hitting Rock Bottom

Semua yang sudah dianalisis tadi tidak terlalu menunjukkan flaw apapun. Ide yang muncul pun sebatas samar-samar. Kemungkinan kita untuk eksploitasi buffer overflow dan secara gamblang mengendalikan jalannya program tertutup oleh security options. Tidak terlihat juga ada tanda-tanda format string vulnerability.

Ide yang masih acceptable adalah menggunakan path traversal di bagian languages. Hal ini dimungkinkan karena path yang dipasang adalah “languages/[Inputan].lang”. Inputan kita bisa control yang artinya dapat kita isi dengan “../” atau mundur 1 directory ke belakang. Namun, permasalahannya adalah bagaimana kita menyingkirkan append dari “.lang” yang ada di belakang input.

Karena stuck, ide yang digunakan untuk debugging adalah dengan memodifikasi source code dan membuat binary baru yang mengoutput isi dari path. Cukup dengan menambahkan

Setelah path diappend dengan snprintf.

Bisa kita lihat output debug kita sudah berjalan dan kita sudah bisa mulai melakukan fuzzing. Note bahwa dari titik ini kita hanya perlu memahami 1 hal lagi sebelum kita bisa mendapatkan flagnya. Tetapi, penulis sangat amat ceroboh dan tidak teliti akan hasil debug yang dikeluarkan oleh debug output ketika lomba berjalan dan akhirnya berpindah ke soal-soal lainnya.

Kita hanya membutuhkan alat debug binary yang sudah kita tambahkan tadi. dan untuk menjalankan program di local, kita perlu 2 file yaitu “password.txt” dan “flag.txt” yang berada di directory yang sama dengan directory dari binary kita.

Part III: Teliti Adalah Kunci

Ini adalah part di mana akhirnya kita dapat melakukan implementasi atas ide path traversal. Tanpa pengetahuan apapun, jika kita melakukan fuzzing asal kepada input dari path, kita bisa melihat sebuah kejanggalan.

Ketika kita menginput 500 huruf a ke dalam input path, maka isi dari path berubah menjadi languages/aaaaa…..aaaa tanpa ada lagi “.lang” yang menjadi penghalang untuk melakukan path traversal. Loh kok bisa?

Menurut http://www.cplusplus.com/reference/cstdio/snprintf/ fungsi snprintf memiliki parameter sebagai berikut.

Yang bisa dibedah menjadi:

  • char *s yang berfungsi sebagai penampung hasil dari snprintf
  • size_t n yang berfungsi sebagai penanda maksimum panjang dari string yang bisa ditampung oleh variable s
  • const char * format yaitu format string atau yang biasa kita ketahui dengan %s,%d dan banyak format-format lainnya
  • …. adalah bagian dari apa yang direpresentasikan oleh format string

Jika kita bandingkan dengan fungsi yang ada di source code, maka akan menjadi seperti ini

Yang berarti:

  • variable path adalah tempat hasil tampungan dari fungsi snprintf
  • 128 adalah panjang maksimum yang ditampung oleh variable
  • string nantinya akan berbentuk “languages/” + “%s” + “.lang” di mana %s merupakan representasi dari variable lang yang didapat dari inputan yang dapat kita control

Beberapa catatan dari man snprintf:

Some programs imprudently rely on code such as the following

sprintf(buf, “%s some further text”, buf);

to append text to buf. However, the standards explicitly note that the
results are undefined if source and destination buffers overlap when
calling sprintf(), snprintf(), vsprintf(), and vsnprintf(). Depending
on the version of gcc(1) used, and the compiler options employed, calls
such as the above will not produce the expected results.

Maksudnya adalah, ketika panjang string dari variable format lebih besar dari maximum size yang bisa ditampung, maka bagian belakang yang akan ditambahkan tidak muncul. Itulah mengapa “.lang” tidak ada di variable path karena kita menimpanya dengan input yang lebih besar dari maximum sehingga yang diambil hanyalah 128 karakter pertama dari string “languages/%s.lang”.

Tinggal kita mengeksploitasinya dengan cara mundur 1 directory dari languages (“../”), dan tetap bertahan di directory tersebut (“./”) sampai kita bisa memanggil flag ataupun passwordnya dengan catatan “.lang” harus hilang dari path kita.

Yang perlu kita lakukan tinggal menghitung panjang dari “aaa” yang ada ketika kita fuzzing tadi. Ternyata panjang a adalah 117 yang berarti maksimum panjang hinggga flag.txt adalah 117. Lalu kita susun stringnya menjadi string “../” + “./”*(panjang hingga maksimum) + “flag.txt”

maka payloadnya adalah “../” + “./” * ((117-len(‘../’)-len(‘flag.txt’))/len(‘./’)) + “flag.txt.
Note: Service hingga hari post ini dikeluarkan masih berjalan.

 

P.S.: 

Kompetisi berjalan pada tanggal 7 September jam 12:00- 8 September 00.00 dan binary itu mendapatkan last modification masih di hari yang sama dengan berjalannya kompetisi. Sehingga seharusnya soal ini dapat diselesaikan tepat waktu jika penulis teliti dan semoga hal ini bisa menjadi pelajaran untuk seluruh pembaca dari writeup ini.

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *