BEHIND CTF SHL 2021
Assalamualaikum warahmatullahi wabarakatuh di penghujung tahun 2021 SHL (Surabaya Hacker Link) mengadakan perlombaan CTF (Capture The Flag) [security competition] yang dilakukan secara daring sebagai media SHL untuk melakukan proses perekrutan para sukarelawan yang akan bergabung dengan komunitas SHL di bagian divisi R&D (Riset & Development), informasi selengkapnya dapat dilihat pada tautan berikut ini klik, untuk poster dari acara ini seperti berikut ini:
https://www.instagram.com/p/CXQgPp6LzHF/
Menurut penulis pribadi desainnya sangat keren dan kekinian sekali neon-neon gitu. Di balik acara ini, penulis dihubungi oleh para pejabat di SHL diminta untuk membantu mengawal kompetisi yang akan diadakan oleh SHL ini. Diskusi tentang teknis perlombaan ini awalnya sesuai dengan poster, di mana 1 Tim berisi 2 peserta dengan lama perlombaan selama 6 jam dengan jumlah total soal adalah 20 butir. Tapi saat H-1 penulis berinisiatif memberi satan merubah format timnya menjadi perlombaan solo, dengan pertimbangan kurangnya tim yang mendaftar, lebih mudah melakukan seleksi karena yang dibutuhkan oleh SHL adalah relawan di bidang R&D maka paling mudah adalah membaca kemampuan tiap individu untuk melakukan perekrutan ini, bisa dengan melihat skor akhir di perlombaan ini atau juga membaca detail dari write up poc yang ditulis tiap-tiap pesertanya, singkat cerita, saran penulis ini dikabulkan oleh pihak SHL.
Teknis Lapangan
Dalam proses perlombaan dari pihak penulis sebagai problem setter membutuhkan setidaknya atau sekurang-kurangnya adalah 1 server sebenarnya, tapi karena pihak SHL kaya, penulis diberi 2 server yang sebagai berikut ini spesifikasinya, keduanya memiliki spesifikasi yang sama.
Server yang pertama dikhususkan digunakan untuk dashboard CTF dan server yang kedua digunakan untuk host a challenges. Untuk dashboard CTF penulis menggunakan dashboard semiliar penyelenggara CTF, yang tidak lain CTFd. Proses instalasi berjalan dengan normal sesuai dengan panduan yang ada di tautannya, hanya merubah exposed port saja. Untuk konfigurasi di level servernya, penulis tidak melakukan konfigurasi selain hanya mengganti port untuk masuk ssh. Tempatnya ada di /etc/ssh/sshd_config sisanya dengan pengaturan bawaan dari Ubuntu 18.04, lalu untuk CTFd sendiri yang menggunakan image yang sudah jadi, karena memang tidak perlu melakukan pengaturan yang lebih untuk jumlah peserta yang kurang dari 20 ini, cukup jalankan perintah docker run -p 80:8000 -it ctfd/ctfd selanjutnya bisa diakses di alamat publik dari server dengan port 80. Proses konfigurasi antarmuka CTFd tidak begitu rumit dan cenderung sangat jelas, jadi tidak perlu dijabarkan di sesi ini. Menginjak ke proses selanjutnya, yaitu pendaftaran, penulis menggunakan skrip automasi berikut untuk mendaftarkan para peserta.
Dan isi dari users.txt adalah daftar para nama pengguna dari tiap-tiap peserta, formatnya cukup pisahkan tiap-tiap data dengan newline(\n). penulis baru mengetahui bahwa proses automasi pendaftaran ini harus diberi jeda, karena CTFd memiliki anti spam di dalam sistemnya (keren). Lalu untuk perlombaan, tiap-tiap soal memiliki sistem penilaian yang dinamis, jadi tidak ada soal yang memiliki nilai poin yang pasti, jadi akan berkurang sesuai dengan seberapa banyak peserta yang berhasil menyelesaikan soalnya, seperti berikut ini konfigurasi yang penulis gunakan
Pada kolom Initial Value berisi nilai awal dari soalnya, penulis memberi nilai 100, pada kolom Decay Limit penulis isi 5, maksudnya adalah seberapa banyak peserta yang menyelesaikan soal X agar soal X mencapai nilai terendah. Pada kolom Minimum Value penulis beri nilai 10 yang maksudnya adalah nilai terendah jika sudah lebih dari 5 peserta menyelesaikan soal X. Lalu pada kolom Max Attempts penulis beri nilai 3 kadang 2 kadang 1, tergantung jenis soalnya, maksudnya ini adalah berapa banyak peserta dapat mencoba melakukan percobaan jawaban pada soal X. Di server soal, konfigurasi dasarnya sama, penulis merubah port masuk SSH sebagai tindakan preventif, lalu melakukan instalasi docker saja, karena ada 3 web, 1 pwn dan beberapa net service, maka tidak semuanya di-deploy menggunakan docker, hanya soal-soal yang kemungkinan RCE (Remote Code Execution) saja yang di-deploy menggunakan docker, dengan rincian hanya 1 web dan 1 pwn saja yang dibuat docker containernya, selain hemat penyimpanan, juga mempercepat proses deploy-nya juga, untuk net service bisa langsung menggunakan socat dan untuk web bisa langsung menggunakan apache2 dan php, selain php penulis juga deploy menggunakan uvicorn karena menggunakan FastApi milik python.
Statistik Perlombaan
Lomba mempunyai 20 soal total, yang mana memiliki kategori umum seperti Crypto,Web hacking, Reverse Engineering, Pwn, Misc dan Forensic, dengan komposisi dari web CTFd sebagai berikut ini
Kategori yang paling banyak adalah di bagian Crypto dan Reversing sebanyak 6 soal tiap-tiapnya. Dan kategori yang paling sedikit soalnya adalah pwn dan misc masing-masingnya hanya 1 soal. Total percobaan jawaban sebagai berikut ini
Dari 63 total, mendapatkan rincian 39 jawaban benar dan 24 jawaban salah, nilai yang cukup tinggi untuk jawaban salah, karena hampir semua soal dibuat dengan konsep straight to the flag. Ya setelah penulis teliti, kesalahannya memang kebanyakan dibuat dengan sengaja, dengan asumsi baru pertama kali bermain CTF, jadi wajar.
Terdapat 20 pengguna terdaftar dan 2000 poin sebenarnya yang bisa didapatkan dan soal dengan nama Win7 paling banyak diselesaikan sebanyak 11 peserta, sisanya soal Blind dengan pengguna yang hanya 1. Sejujurnya dari statistik ini menurut penulis ada yang sedikit lucu, soal Win7 ya sangat wajar, karena soalnya punya tingkat kesulitan 1-10 adalah 1. Tapi ada soal yang tingkat kesulitannya juga 1 yaitu soal e2 tapi yang dapat menyelesaikan hanya 2 peserta, sementara soal dengan nama Padding Attack yang tingkat kesulitannya 3 malah 4 peserta yang dapat menyelesaikan.
Ya asumsi penulis ini soal preferensi pengerjaan saja, tapi sebagai pembuat soal memang cukup lucu membaca statistik ini. Sekarang beranjak ke scoreboard seperti berikut ini:
Pemain nomor 1 memiliki ketimpangan poin yang terlalu jauh dengan peserta lainnya, menurut penulis pribadi ini juga soal pengalaman pemain nomor 1 yang juga menjadi problem setter di perlombaan lain. Sementara yang lainnya mungkin tidak punya waktu untuk mengerjakan, karena di hari Sabtu, yang mana waktunya untuk bersantai dengan keluarga. Dari 12 peserta di scoreboard ini berarti ada sekitar 8 orang yang tidak mengerjakan sama sekali, bisa jadi lupa atau memang belum bisa menyelesaikan soal apapun, pikiran penulis tetap di tidak sempat mengerjakan. Beralih ke statistik lain, bahwa semua soal di kategori Web Hacking, tidak ada satupun yang dapat menyelesaikan, padahal 2 dari 3 soal yang keluar di CTF SHL 2021 adalah remake 20-40% dari soal yang pernah keluar di kompetisi yang penulis jadi problem setter-nya. Berikut soal-soal Web Hacking yang tidak dapat diselesaikan peserta manapun.
Soal-soal di atas, semuanya dibuat tidak terlalu ‘hacking’ lebih ke arah analisa php-bug atau algoritma-bug saja, kalau soal client itu ditemui penulis di dunia nyata baru-baru ini. Menilai dari diri sendiri, soal Fun punya tingkat kesulitan 1-10 ya 6, soal Client 1-10 ya 5, dan soal Numb dari 1-10 ya 5, kenapa 5 di numb, bahkan penulis tidak merubah sedikitpun algoritma yang ada, jika mau dicari potongan source code-nya di Internet, pasti menemukan write up orang-orang yang pernah mengerjakannya.
POC (Proof of Concept)
Di sini penulis akan mencoba membahas 3 soal Web yang tidak sempat ada yang menyelesaikan, karena bagaimanapun soal ini memang sudah pernah keluar, dan secara bug sudah muncul jadi tak apa mari kita beda perlahan.
FUN
Diberikan sebuah web yang memiliki source code sebagai berikut ini
Secara sederhana kita dapat langsung fokus pada potongan kode berikut ini
Kita diberikan inputan pin yang mana panjang dari pinnya hanya 4 bit saja, dan yang membuat agak rancu di situ adalah perbandingannya menggunakan strcmp(), kita mungkin bisa melakukan bypass strcmpnya menggunakan memasukan array di tiap-tiap bitnya, tapi jika terlalu susah, kita dapat melakukan brute force saja, mencari respon yang tidak mengandung Failer.Menurut penulis inilah cara tercepatnya. Lalu yang menjadi masalah di salah fungsi filter() fungsi ini di-include dari config.php yang mana isi dari itu dapat didapatkan dari .git yang tertinggal, isi dari fungsi filter() adalah sebagai berikut ini
Kita dapat crafting payload yang kita inginkan, untuk dapat bisa dijadikan payload XXE yang masuk dalam fungsi ini
Inject pointnya ada di dalam elemen flag. Maka bisa dibuat solver-nya untuk mengambil isi dari config.php untuk melihat isinya yang terbaru sebagai berikut ini
Dan isi dari config.php di dalam server adalah
Kita dapat melakukan RCE (Remote Code Execution) lewat parameter shl dengan full payloadnya seperti berikut ini
curl -X POST –data “f=system&p=ls” “http://ip?shl=1” akan keluar file flagnya, bisa dilakukan direct access lewat peramban. shlctf{fun}
Numb
Diberikan sebuah web dengan tampilan sebagai berikut ini
Hanya memiliki 2 tombol yaitu, Generate dan Bet! yang mana jika ditekan Generate akan menghasilkan angka random, yang mana jika ditekan Bet! akan diadu dengan nilai di dalam server.
CTRL+U untuk melihat bagaimana proses generate dan ajax.
Angka dibuat secara acak dengan jarak 1-100, lalu diadu dengan endpoint api.php yang mana memiliki fitur debug, dan berikut ini adalah hasil dari api.php?debug=1
Didapatkan source code yang berantakan, kita bisa memakai beautify yang mana saja.
Banyak kondisi yang tidak masuk akal, meskipun kita klik 1000x mendapatkan kemungkinan tebakan yang pas, tapi tidak akan pernah dapat flag secara utuh, minimal dipotong sampai 9 bit atau paling banyak potongannya sampai 10 bit. Satu-satunya cara adalah mengikuti algoritma ini, yang mana tidak penulis ubah sedikitpun sejak kompetisi 2019.
Di dalam percabangan yang pertama sudah ada logika yang kacau, panjang inputan harus 5, tapi nilainya harus 1020 yang mana panjang dari 1020 adalah 4. Caranya kita dapat menggunakan exponen (e) untuk mengelabui ini.
Di percabangan ke-2 juga ada logika yang kacau.
Panjangnya harus 5 tapi nilainya harus lebih dari 10000000000000, maka caranya lagi-lagi dengan menggunakan exponent.
Masalah yang ke-2 adalah bagaimana kita bisa mengetahui kalau inputan kita sama dengan panjang dari flag. Cara satu-satunya adalah menggunakan brute force lagi, dengan parameter, jika kita mendapatkan “CLUE” maka itulah panjang flagnya.
Masuk ke problem yang lebih kacau lagi.
Di sini variable a1 dan a2 berisi dari inputan yang sama, hanya saja yang dirubah hanya tipe datanya, di situ terlihat, variable a1 haruslah true dari isset() dan juga true dari empty() di logika ini saja sudah kacau, bagaimana sebuah variabel yang berisi juga kosong yang lebih kacau lagi a1 juga harus NULL, yang lebih kacau lagi, variable a2 yang mana juga dari inputan yang sama dengan a1 panjangnya tidak boleh 1, jadi harus 0 atau lebih dari 1 ex 2-N. Tapi di sinilah kacaunya php dalam comparison, di forum CLUE ada seseorang yang menemukan hasil sebagai berikut ini
Di mana 0.0 yang jika adalah string panjangnya adalah 3 tapi jika dia integer maka dia bisa empty() bisa NULL lalu juga isset() juga true :) magic of php. Masuk ke problem yang tidak begitu problem, karena dari atas sudah terdefinisi dengan benar.
variable tebak substr($tebak, 0, 3) dari 102e1 atau 102 haruslah f, ini sudah benar. Masuk ke problem yang lumayan problem
Panjangnya harus 3 tapi dia harus bernilai 0. Ini sangat problem. Tapi tenang, di forum sudah diberikan tabel LOSS COMPARE
Tabelnya sudah diperbarui, tapi masih bisa dibaca, string yang panjangnya 3 dan bernilai true jika dibandingkan dengan 0 adalah string “php” itu sendiri :). shlctf{ctf_number_is_fun_php_fault?}
Client
Diberikan sebuah web yang hanya memiliki return dari json seperti berikut ini
Tidak ada informasi apapun, sebenarnya challenge ini terinspirasi dari kasus di dunia nyata, challenge ini banyak mengandalkan proses recon saja, jika kita menambahkan path random maka akan memberikan hasil yang mirip
Mari mencoba melihat response header yang diberikan
Server yang digunakan adalah uvicorn, jika kita menggabungkan keyword, detail not found uvicorn dan mencarinya lewat Google, maka akan mengeluarkan hasil yang sangat relevant.
Kita cukup membaca dokumentasi dari FastAPI untuk mengerjakan soal ini. Fitur dari FastAPI ini sangat menarik
FastAPI memiliki auto genarte docs api yang mana dapat diakses secara langsung pada path /docs
Dan yaaaa, memang ada
Ini adalah endpoint yang ada di dalam web challenge, di bagian /flag/{apikey}/{pin} membutuhkan 2 secret things. Salah satu dari hal yang bisa dikerjakan hanyalah endpoint /client/{id} yang mana id adalah inputan kita. Jika kita menginputkan id yang terlalu banyak {lebih dari 1000} maka akan mengeluarkan response Too Long. Dan jika diurutkan id 1-10 maka akan mengeluarkan value integer, yang mana jika lakukan konversi ke ascii akan menghasilkan headers dari sebuah file pyc. Maka berikut inilah script yang bisa digunakan untuk melakukan dumping.
Jalankan dengan python2 dan akan mendapatkan file pyc yang mengandung api_key dan pin. Tinggal lakukan reverse dengan cara dissas seperti berikut ini, dan mendapatkan api_key dan pin
Singkatnya jika ditranslasikan seperti script aslinya maka akan berbentuk seperti berikut ini
Terlihat ApiKey() tersimpan di sebuah fungsi, dan di-encode dengan base64 serta pin yang digunakan dilakukan hashing dengan md5 yang mana terpisah menjadi 2. Maka setelah didapatkan ApiKey dan PINnya, tinggal submit di dalam API docs.
Dan didapatkan flagnya shlctf{Basic_read_Api_documentation_leaks}
E2Client
Sekarang penulis ingin membahas soal yang menurut penulis sangat-sangat mudah, tapi hanya ada 2 peserta yang berhasil menyelesaikannya. Diberikan sebuah chipertext 0x750x6a0x6e0x650x760x680x7d0x790x670x6e0x650x710x6f0x670x610x6a0x670x740x670x610x640x740x630x760x630x700x7f dan sebuah file peserta.py yang berisi sebagai berikut ini
Terlihat di situ terdapat 4x operasi yang membuat flag dari bentuk aslinya, dilakukan pergeseran dengan penambahan *int(int((int((int((100 10)/2)<<3)/40)>>3)/3)/2)* yang terlihat rumit hanyalah bagian ini, namun kita bisa langsung tau hasilnya dengan menjalankannya di python-shell*
Terlihat, bahwa operasi pertama hanyalah menggeser 2 bit dari tiap-tiap bit flag. Di bagian operasi ke-2 malah hanya melakukan konversi dari desimal ke ascii. Operasi ke-3 juga sama, hanya merubah bentuk dari ascii kembali ke desimal. Di operasi ke-4 hanya merubah tiap-tiap desimal ke bentuk hex. Cara mengerjakannya diurut dari bawah ke atas, berarti tinggal unhex terlebih dahulu. Kita dapat membuat solver sederhana seperti berikut ini
Atau bekerja secara manual, pertama hilangkan semua 0x akan menghasilkan data seperti ini 75 6a 6e 65 76 68 7d 79 67 6e 65 71 6f 67 61 6a 67 74 67 61 64 74 63 76 63 70 7f lalu decode dari hex ke ascii di web manapun, akan menghasilkan hasil seperti ini ujnevh}ygneqogajgtgadtcvcp tinggal geser dengan caesar chiper web
Tinggal ditata sedemikian rupa, dan jadilah shlctf{welcome_here_bratan} meskipun ini tidak diberi source code pun bisa dikerjakan loh teman-teman :).
Padding Attack
penulis sendiri juga tidak tau kenapa memberi nama ini, tapi tak apa. Diberikan sebuah source code beserta chipertextnya di dalamnya.
Pada line 4 dan 5 melakukan generate random numbers dengan jarak 0,13 dan 13,37. Di bagian line 7, variable frag berisi list potongan-potongan flag yang mana pemecahanya adalah parameter pad yang mana berasal dari random numbers dengan jarak 3-5, melihat line 22, tiap-tiap elemen list berjumlah 5, maka bisa dipastikan random numbers tepat di angka 5. Seperti inilah contoh dari hasil frag
Pada bagian line 11 sampai 18, algoritmanya adalah melakukan pengecekan apakah index yang dari list genap atau ganjil, jika genap gunakan key1 jika ganjil key2, yang mana key ini digunakan untuk operasi XOR.
Cara cepatnya ini tidak lain hanyalah brute force saja, dan mencari kepala flag sebagai parameternya, berikut solver yang penulis gunakan
Jalankan dan grep prefix shlctf, berikut hasil yang penulis dapatkan
Secara teknis, soal ini sangat-sangat lebih rumit daripada soal E2, namun secara mengejutkan soal ini dikerjakan lebih banyak daripada E2. Tak apa mungkin ini soal preferensi saja.
Penutup
Terima kasih atas semua pihak yang telah berpartisipasi di acara SHL CTF 2021 tepatnya hari Sabtu, 18 Desember 2021. Acara yang sangat menyenangkan di penghujung tahun 2021, dilakukan secara daring dan minim masalah di waktu hari pelaksanaan. Semoga SHL mendapatkan kandidat relawan yang tepat untuk tim yang baru tahun depan atau mungkin tahun ini sudah bisa didapatkan. Semoga CTF SHL 2022 besok tetap ada, dengan format yang lebih seru dan mungkin bisa dilakukan secara luring, mengingat semuanya sudah berangsur pulih pasca kejadian pandemi COVID-19 ini. Terima kasih sekali lagi sangat seru, Write with love Nikko Enggaliano. Assalamualaikum warahmatullahi wabarakatuh.