[KKSI 2019] – Sandbox1

Diberikan sebuah binary dengan konfigurasi seperti di bawah

Ketika binary dijalankan akan meminta input sebagai berikut.

Dengan konfigurasi seperti tadi dan juga input beserta segfault, maka dapat dipastikan bahwa soal ini adalah soal shellcoding dan namanya mengacu pada shellcode sandbox. Untuk memahami bagaimana cara kerja program, kita lihat hasil disassembly nya.

Bisa dilihat bahwa main functionnya sangat sederhana.
Pertama, soal akan mengoutput string Enter Your Shellcode, lalu akan membaca input dan dimasukkan ke variable bernama shellcode. Input yang dibaca hanyalah sebesar 0x10 atau 16 karakter.

Lalu akan dilakukan loop yang berfungsi untuk memfilter isi dari shellcode yang diinput. Namun, sepertinya hasil decompile ghidra cukup kacau pada showing data typenya karena jika dilihat dari assemblynya, comparisonnya tidaklah seperti pada hasil decompilation.

Seperti yang bisa dilihat di atas, yang dicompare adalah posisi shellcode[i] dan shellcode[i+1]. Sehingga yang dicek adalah jika ada 2 byte berurut yang sesuai dengan apa yang difilter, maka program akan di kill.

Jadi persyaratannya adalah:

  1. Panjang shellcode maksimal 16 karakter.
  2. Tidak boleh mengandung ‘\x0f\x05’ , ‘\xb0\x2’, dan ‘\xb0\x3b’ yang memiliki arti instruksi assembly sebagai berikut

Bagaimana cara mendapat shell dengan hanya mempunyai lahan shellcode 16 bytes saja? Ketika penulis mengerjakan soal ini, penulis mendapat beberapa ide namun stuck dalam development payloadnya. Ide yang pertama muncul adalah melakukan syscall read yang panjang agar dapat melakukan read yang lebih leluasa yang tentunya sudah pasti gagal karena syscall sudah diblacklist.

Lalu ide kedua adalah menyusun register agar bisa melakukan read sesuai dengan keinginan dan memanggil read dari fungsi yang sudah pernah dipanggil dalam binary yaitu read@plt. Penulis sempat tidak terlalu memperhatikan registers pada binary saat dijalankan (kurang teliti dalam debug) dan setelah dicek lagi, binary ini sangat amat membantu dalam crafting payload.

Ada 2 posisi dimana binary membantu dalam crafting payload yaitu sebelum read, dan sebelum payload dijalankan. 

Ketika program akan melakukan call terhadap read, maka program pasti akan menyusun registernya terlebih dahulu. Hal ini bisa menjadi acuan kita saat kita ingin membangun payload yang dapat melakukan read. (Bisa juga dilihat dari syscall reference table). Menurut reference table, ada 3 register penting sebelum memanggil fungsi read yaitu:

  • RDI (Destination Index) yang menandakan dari mana input akan dibaca. Untuk membaca input user, maka dibutuhkan input dari stdin atau standard input yang memiliki fd  0.
  • RSI (Source Index) akan menandakan dimana buffer akan disimpan. Pada read yang akan dipanggil di atas, input kita akan dimasukkan ke alamat 0x601060 yaitu alamat variable shellcode.
  • RDX akan menandakan berapa panjang input yang bisa diterima oleh read. Pada decompiler kita bisa lihat bahwa hanya 0x10 bytes saja dan memang value dari RDX adalah 0x10.

Kita ingin, shellcode kita nanti bisa membaca lebih banyak dari 16 bytes. Maka kita harus mengontrol 3 registers tadi dengan masing-masing value adalah:

  1. RDI harus 0
  2. RDX adalah value bebas yang bisa disesuaikan dengan kebutuhan
  3. RSI adalah bagian dari memory tempat penyimpanan shellcode phase2.

Setelah melihat bagaimana read dipanggil, kita bisa mengecek kondisi terakhir sebelum shellcode dipanggil.

Kita bisa melihat bahwa kondisi terakhir adalah:

  • RDI = 0, sudah sesuai jika kita ingin melakukan read dari stdin.
  • RDX = 0x601060 yang merupakan alamat dari shellcode.
  • RSI = 0x601060 yaitu alamat shellcode. Jika kita tidak menggantinya, maka kita akan menimpa value pada shellcode sebelumnya.

Berarti, jika ingin memanggil read untuk membaca lebih banyak input, kita harus melakukan set up seperti ini:

  1. RDI sudah 0, sehingga tidak perlu diubah karena kita ingin melakukan read dari stdin.
  2. RSI merupakan valid address yang juga RWX. Sehingga boleh diganti ataupun tidak asalkan bagian memorynya adalah bagian yang RWX juga.
  3. RDX merupakan address yang jika dilihat sebagai integer adalah nilai yang sangat besar. Bisa langsung digunakan sebagai length, tetapi bisa juga diubah sesuai dengan keinginan.
  4. Call alamat read@plt.

Untuk payload phase 1, saya tidak akan mengubah isi register apapun kecuali untuk RDX. RDX saya ubah hanya untuk mempermudah ketika ingin melakukan nop slide pada phase 2 nanti.

Ini adalah script phase 1 dari exploit ini.

Ketika dilihat, sudah berhasil melakukan input ke-2. Mari dkita bedah apa yang membuat shellcode ini berhasil. Pertama, dengan keadaan register yang sudah ada, saya hanya mengganti value dari RDX yang menjadi size dari input. Setelah itu, saya memanggil alamat read@plt pada program. Perlu diingat pada pwntool bahwa jika kita ingin melakukan fungsi asm() dengan relocation, maka kita harus melakukan set pada parameter vma. Apa itu vma? vma adalah virtual memory address yang menjadi titik awal dari assembly. Kita ingat bahwa posisi shellcode kita berada pada variable “shellcode”. Sehingga titik awal dari shellcode kita adalah di 0x601060 yaitu alamat variable itu sendiri. Pada waktu perlombaan, penulis kurang memahami cara menggunakan fungsi asm() ketika ingin melakukan call terhadap fungsi yang menggunakan relocation dan itu membuat tim penulis kehilangan 200 poin.

Setelah berhasil melakukan input yang lebih panjang, kita bisa melanjutkan exploit kita dengan mengirimkan shellcode “betulan” untuk memunculkan shell tanpa ada batasan apapun. Kita tinggal menginput shellcode kita yang diberikan nopsled (‘\x90’) agar kita dapat memastikan bahwa program akan melakukan return pada shellcode kita.

Full exploit:

 

 

Leave a Reply

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