Reverse Engineering

Challenge
Link

Branches (260 pts) πŸ₯‡

Serial (390 pts) πŸ₯‡

Box (623 pts) πŸ₯‡

Standard (1000 pts) πŸ₯‡

Branches (260 pts)

Description

-

Solution

Diberikan file ELF. buka dengan IDA

Dari kode diatas diketahui bahwa input kita akan dimapping ke check_function. Check_function berisi shellcode karena akan dieksekusi/dipanggil nantinya. Jadi disini ada dua kemungkinan mengenai input kita, yang pertama adalah input kita akan digunakan sebagai instruksi atau yang kedua sebagai operand. Mari kita cek

Input pertama akan terletak pada 0x4066 (nilai setelah t). Lakukan disasm

Jika kita ganti nilai ff dengan nilai lain misal 00 maka bisa dilihat bahwa operand dari jz akan berubah

Jadi disini bisa kita simpulkan bahwa input kita berfungsi sebagai operand dari jz, kita tahu jz pasti akan dieksekusi dikarenakan cmp 0,0 pasti true. Langkah selanjutnya hanya menentukan dimana address yang valid untuk eksekusi selanjutnya. Cek input selanjutnya berada dimana

Input selanjutnya berada pada 0x40b4, lakukan disasm di instruksi sekitar 0x40b4. Dengan trial dan error bisa kita temukan bahwa instruksi valid berada pada 0x40b0.

Dari sini kita tahu bahwa nilai operand pada jz adalah 0x40b0. Jadi untuk mendapatkan operand yang valid kita bisa dengan melihat selisih dari kedua address tersebut. (target_address - address_after_jz). Contoh untuk input pertama adalah 0x40b0 - 0x4067

Asumsi kami disini semua bentuk assemblynya sama, jadi tinggal scripting letak β€˜t’ lalu kurangi dengan letak β€˜t’ selanjutnya dimana perlu ada padding sebesar 5 supaya menghasilkan seperti kasus manual diatas. Berikut solver yang kami gunakan

Flag: INTECHFEST{Br4nch3s_As_Fl4g_Ch3ck3r_Wh0_W0uld_Hav3_Th0ught}

Serial (390 pts)

Description

-

Solution

Diberikan file PE .NET, buka dengan dnspy

Fungsi yang dibuat oleh probset tidak terlihat langsung, buka dengan IDA untuk melihat fungsi lebih jelas. Pada IDA fungsi terlihat lebih jelas

Lakukan decompile pada dnspy untuk fungsi decryptflag. Lakukan analyze pada fungsi decryptflag untuk mendpat xref.

Bisa dilihat bahwa fungsi DecryptFlag akan dipanggil jika username equal dengan Administrator dan serial untuk user Administrator valid. Jadi selanjutnya cek fungsi checkSerial. Fungsi checkSerial menerima dua argument yaitu username dan serial. Bisa dilihat bahwa username akan diproses pada kode berikut

Nilai akhir dari proses yang memanfaatkan username akan disimpan pada variable num6. Kemudian num6 akan disimpan pada vector _u0020

Selanjutnya vector tersebut akan digunakan untuk validasi terhadap serial. Sampai disini kita tahu bahwa nilai num6 bisa kita dapatkan dari program, selanjutnya tinggal mencari tahu bagaimana serial divadliasi berdasarkan num6.

Sebelumnya ada validasi seperti input harus valid hexadecimal karakter. Selain itu terdapat validasi sebenarnya yaitu seperti pada (num20 << 4) | num21 != _u0020 & 255. Jadi tinggal reverse kode tersebut dan dapat serial yang valid. Berikut kode yang kami gunakan

Dapatkan num6 dari breakpoint pada line berikut

Selanjutnya tinggal masukkan user dan serial ke program dan dapat flag

Flag: INTECHFEST{did_you_know_that_this_serial_system_was_the_same_serial_system_for_010_editor?}

Box (623 pts)

Description

-

Solution

Diberikan file APK, decompile dengan apktool. Dari struktur direktori asset diketahui bahwa apk tersebut dibuat dengan unity

Karena tidak ada Assembly-CSharp.dll pada assets maka kita perlu melakukan reverse engineering terhadap libil2cpp.so yang ada pada lib direktori. Disini kita bisa memanfaatkan global-metadata.dat untuk melakukan recover terhadap beberapa informasi pada libil2cpp.so seperti nama fungsi. Gunakan il2cppdumper untuk mendapatkan datanya https://github.com/Perfare/Il2CppDumperarrow-up-right.

Selanjutnya gunakan script sesuai dengan tools yang digunakan untuk melakukan patch terhadap executable berdasarkan hasil extract dari global-metadata.dat. Disini saya menggunakan ida lalu melakukan load terhadap ida_py3.py dan pilih script.json. Selanjutnya kita fokus pada fungsi dengan awalan GameManager.

Lihat fungsi AddScore

Dapat diketahui bahwa AddScore pasti melakukan penambahan score. Penambahan score dilakukan sebanyak 1 kali, jadi bisa diketahui bahwa (v1+32) merupakan address untuk score. Dapat diketahui juga bahwa terdapat increment lain yaitu untuk v2 (v1 + 36) dimana nilainya didecrypt lalu ditambah dan diencrypt lagi. Jadi disini nilai score sebenarnya ada 2 yaitu pada v1 + 32 dan v1 + 36. Selanjutnya lihat fungsi Update

Pada bagian awal dari fungsi Update dapat diketahui bahwa terdapat decrypt process untuk +0x24 atau 36, hasil decrypt akan dibandingkan dengan plaintext score (+0x20). Jika berbeda akan menampilkan β€œCHEATER DETECTED!!!”

Selanjutnya dibawah ada pengecekan nilai plaintext score, jika lebih besar atau sama dengan 0xCC07C9 maka akan dilakukan decrypt. Asumsi disini decrypt adalah fungsi untuk menampilkan flag. Lanjut pengecekan ke fungsi decrypt

Fungsi decrypt menggunakan a1+0x24 yang mana nilai dari score yang terencrypt sebagai argument untuk md5. Dimana nilai md5 tersebut akan digunakan untuk melakukan decrypt flag. Sampai disini kita tahu bahwa kita harus melakukan update terhadap 2 nilai jika ingin melakukan cheat, yaitu 0x20 dan 0x24. Gunakan IDA untuk debug

Set breakpoint pada address 0xD7ADE4 dan 0xD7AE10. Pada 0xd7ade4 ubah nilai x8 menjadi 0xCC07C9

Selanjutnya pada 0xD7AE10 ubah nilai x0 menjadi 0xCC07C9

Selanjutnya tinggal continue saja dan jika ada error pilih pass to the app

Flag: INTECHFEST{Byp4ss1ng_Sh1tty_4nt1_Ch34t_S0_EZ}

Standard (1000 pts)

Description

-

Solution

Diberikan file ELF 64 bit, decompile dengan IDA. Terlihat bahwa program dibuat dengan c++.

Pada awal program dilakukan penerimaan input lalu pengecekan format flag dan membagi input berdasarkan dengan _ sebagai separator. Pada line 114 dilakukan pengecekan nilai dari vector (hasil split) jika panjangnya 4 maka valid lanjut ke line 121 untuk looping pengecekan setiap nilainya. Jadi total ada 4 nilai yang dicek.

Masuk ke fungsi std::for_each<__gnu_cxx::__normal_iterator<std::string *,std::vector<std::string>>,main::{lambda(std::string&)#1}.

Input kita akan diproses pada fungsi main::{lambda(std::string &)#1}::operator()((__int64)&v5, v3);. Pada fungsi tersebut akan dilakukan pengecekan untuk keempat nilai, berikut untuk gambaran besar flownya

  • Pengecekan terhadap karakter yang menyusun nilai tersebut/charset (step 1)

    • Contoh fungsi

      • std::all_of<__gnu_cxx::__normal_iterator<char *,std::string>,main::{lambda(std::string&)#1}::operator() const(std::string&)::{lambda(char)#1}

    • Semisal pengecekan apakah 1 block tersebut terdiri dari angka semua, huruf, dkk

    • Jika semua pengecekan menghasilkan nilai true maka lanjut ke pengecekan nilai setiap indexnya (step 2)

    • Jika tidak, lanjut ke pengecekan terhadap karakter yang menyusun nilai tersebut tetapi berbeda pengecekan (misal yang awalnya cek untuk huruf besar dan angka jika salah lanjut ke pengecekan huruf kecil dan angka)

  • Pengecekan nilai setiap index(step 2)

    • Contoh fungsi

      • std::find_if<__gnu_cxx::__normal_iterator<char *,std::string>,main::{lambda(std::string&)#1}::operator() const(std::string&)::{lambda(char)#3}>

    • Setiap index akan dilakukan pengecekan dengan pemanggilan fungsi yang sama beberapa kali

    • Tidak ada pengecekan urutan setiap block, jadi kita tentukan sendiri (step 3)

    • 1 yang pasti adalah pengecekan terhadap nilai 0xDEA47FED merupakan pengecekan terakhir karena diakhir akan ditambahkan }

    • Pengecekan terhadap

Total ada 4 block, jadi total ada 4 block pengecekan. Kita tahu nilai dari masing-masing block jika valid akan digabung dengan _ , jadi kita bisa memberi tanda bahwa setiap penambahan _ akhir dari block pengecekan (diluar penambahan }). Untuk mengetahui nilai yang dicek, kita bisa decompile fungsi secara berulang jika ada fungsi didalamnya, hal tersebut saya ketahui dari debugging. Contoh

  • …or<char *,std::string>,main::{lambda(std::string&)#1}::operator() const(std::string&)::{lambda(char)#3}>

    • …__ops::_Iter_pred<main::{lambda(std::string&)#1}::operator() const(std::string&)::{lambda(char)#3}>>

      • …_Iter_pred<main::{lambda(std::string&)#1}::operator() const(std::string&)::{lambda(char)#3}>>

        • …const(std::string &)::{lambda(char)#3}>::operator()<__gnu_cxx::__normal_iterator<char *,std::string>>

          • …:operator() const(std::string &)::{lambda(char)#3}::operator()(a1, *v2

            • return a2 == 84;

Jadi selanjutnya tinggal cek semua fungsi dan dapat flag. Berikut komen pada hasil decompile yang saya simpan

Flag: INTECHFEST{The_C++_G0d_dea47fed}

Last updated