LOMPAT KE KONTEN
PIXZ.DEV
← BUKU CATATANENTRI 001 / 001
TERVERIFIKASI — SELF-HOSTEDAUDIT KEAMANANSEPTEMBER 2026

Garuda CBT — Pembongkaran Keamanan

Laporan kerentanan bergaya HackerOne untuk Garuda CBT — aplikasi ujian dan rapor sekolah open-source di atas CodeIgniter 3 — diaudit sepenuhnya pada instance lokal yang dibangun sendiri dari repositori publik. Sembilan temuan, delapan terverifikasi dinamis: tiga Critical (pembuatan akun admin tanpa autentikasi di CVSS 9.8, RCE tanpa autentikasi lewat injeksi config installer di 9.9, dan eksekusi SQL arbitrer yang berevolusi menjadi rantai webshell penuh di 10.0), tiga High, dua Medium, satu Low/Info. Review statis atas source yang di-deobfuscate menuntun ke sink berbahaya; bench uji PHP 8.3 + MariaDB 11.8 memastikan setiap klaim kritis lewat reproduksi dinamis (detail reproduksi diredaksi dari publikasi ini).

APPSECPHPCODEIGNITERSQLIRCE

PROFIL SEVERITAS

8 DARI 9 TERVERIFIKASI DINAMIS
3KRITIS
3TINGGI
2SEDANG
1RENDAH / INFO
CVSS TERTINGGI10.0

01  KONTEKS

Garuda CBT adalah platform sekolah nyata yang dipakai aktif — pelaksanaan ujian, rapor, data siswa — didistribusikan sebagai PHP open-source di atas CodeIgniter 3, dipasang dan dijalankan sendiri oleh sekolah. Kombinasi itu (data sensitif, deployment luas, framework lama) persis profil yang layak diaudit sebelum aktor jahat melakukannya lebih dulu. Pertanyaannya sederhana: apa yang sebenarnya dibutuhkan penyerang untuk menguasai data ujian sebuah sekolah? Jawabannya: tanpa kredensial, satu request HTTP, dan — pada kasus terburuk — cukup repositori publiknya saja.

02  METODE

Source yang didistribusikan di-obfuscate dengan payload hex-escape, jadi langkah pertama adalah men-decode-nya kembali menjadi PHP yang terbaca. Dari situ audit berjalan sebagai loop:

  1. Decode source hasil distribusi menjadi PHP yang terbaca.
  2. Identifikasi sink berbahaya — penulisan file, konkatenasi query mentah, pemanggilan yang mengarah ke eksekusi.
  3. Lacak input pengguna sampai ke setiap sink.
  4. Urutkan mana yang benar-benar berbahaya.
  5. Verifikasi setiap kandidat secara dinamis pada instance yang dibangun sendiri.
  6. Pastikan dengan general log database — kebenaran absolut atas apa yang tereksekusi.

Bench uji: server built-in PHP 8.3.28, MariaDB 11.8.8, skema lengkap 74 tabel, akun uji buatan sendiri, ENVIRONMENT dibiarkan pada default rilisannya. Setiap klaim diuji sebagai request curl biasa ke instance lokal. Pembersihan paling akhir — webshell dihapus, tabel uji di-drop.

VerifikasiTemuan
Terverifikasi dinamis pada instance lokal8
Review statis kode saja1
Total9

LINGKUP & LINGKUNGAN UJI

TARGETgithub.com/garudacbt/cbt @ 2d96e28f (master)
APLIKASIGaruda CBT 1.5.3 — CodeIgniter 3.1.x
BENCH UJI127.0.0.1:8080 — PHP 8.3.28 built-in server
DATABASEMariaDB 11.8.8 — DB cbt, schema 74 tables
AKUN UJIadmin / siswa_test / guru_test — self-created
PRODUKSI DISENTUHNONE — self-hosted instance only

INDEKS TEMUAN — 09

CVSS
  • GBT-001KRITIS9.8

    Pembuatan Akun Admin Tanpa Autentikasi

  • GBT-002KRITIS9.9

    RCE Tanpa Autentikasi — Injeksi PHP ke database.php

  • GBT-003KRITIS10.0

    Eksekusi SQL Tanpa Autentikasi hingga Webshell — Enkripsi Palsu, Kunci Publik

  • GBT-004TINGGI8.6

    Blind Time-Based SQL Injection — /siswa/getPost

  • GBT-005TINGGI8.6

    Blind Time-Based SQL Injection — /bukurapor

  • GBT-006TINGGI8.2

    CSRF Dimatikan di Endpoint Materi — Upload Webshell Paksa

  • GBT-007SEDANG6.1

    Stored XSS pada Pengumuman

  • GBT-008SEDANG5.3

    Encryption Key Hardcoded + Default Development

  • GBT-009RENDAH / INFO

    Aneka Temuan Code Review — Dev Tool Tanpa Auth dan Temannya

GBT-001KRITISCVSS 9.8TERVERIFIKASI — POC DINAMISKOMPONEN
application/controllers/Install.php — createAdmin()

Pembuatan Akun Admin Tanpa Autentikasi

RINGKASAN

Controller Install tidak pernah dinonaktifkan setelah instalasi selesai, dan createAdmin() tidak punya guard autentikasi. Siapa pun, tanpa login, bisa meng-POST akun baru ke group admin pada instance yang sedang berjalan — lalu cukup masuk dengan akun itu. Guard check_installer() yang tersedia hanya menempel di index(), bukan di method yang benar-benar berbahaya.

DAMPAK

Takeover total aplikasi: kelola guru dan siswa, baca dan ubah semua nilai serta rapor, ekspor bank soal beserta jawabannya, unduh backup database — dan bersama GBT-003, eksekusi SQL dan kode arbitrer. Tanpa kredensial, tanpa interaksi korban.

REMEDIASI

Kunci installer setelah instalasi (lock file atau konstanta INSTALLED) dan tolak createAdmin, createApp, createSetting, serta checkDatabase begitu admin sudah ada — guard harus menempel di setiap method, bukan hanya halaman pembuka.

BUKTI — DIREDAKSI

Diredaksi demi keamanan. Langkah reproduksi dan hasil pengamatan sudah diverifikasi pada instance self-hosted, namun ditahan dari publikasi ini selagi patch tersebar — detail eksploitasinya diserahkan ke maintainer sebagai bagian dari coordinated disclosure, bukan ke web terbuka.

GBT-002KRITISCVSS 9.9TERVERIFIKASI — POC DINAMISKOMPONEN
Install::checkDatabase() — write_db_config() + assets/app/db/database.php

RCE Tanpa Autentikasi — Injeksi PHP ke database.php

RINGKASAN

checkDatabase() menerima nilai koneksi dari POST, melakukan str_replace ke template config PHP, lalu menulis ulang application/config/database.php — file yang justru diinstruksikan panduan instalasi untuk di-chmod 777. Filter xss_clean memblokir pemanggilan mencolok seperti system( dan eval(, tetapi daftar hitamnya mudah dilewati: popen() plus stream_get_contents() mengeksekusi perintah dengan sama senangnya. Satu request menulis payload; request berikutnya meng-include-nya.

DAMPAK

Eksekusi perintah OS sebagai user web-server, tanpa autentikasi. Reverse shell, pembacaan config, persistensi — dan karena alur instalasinya sendiri menuntut config yang writable, kondisi rentan ini justru keadaan default dari deployment nyata.

REMEDIASI

Kunci installer (lihat GBT-001); hentikan interpolasi string mentah ke PHP yang tereksekusi — validasi input secara ketat (hostname hanya boleh [a-zA-Z0-9.:-]) dan tulis nilainya dengan var_export(); setelah instalasi, kembalikan permission yang waras dan hapus installer.

BUKTI — DIREDAKSI

Diredaksi demi keamanan. Langkah reproduksi dan hasil pengamatan sudah diverifikasi pada instance self-hosted, namun ditahan dari publikasi ini selagi patch tersebar — detail eksploitasinya diserahkan ke maintainer sebagai bagian dari coordinated disclosure, bukan ke web terbuka.

GBT-003KRITISCVSS 10.0TERVERIFIKASI — RANTAI PENUHKOMPONEN
application/controllers/Update.php — createTable()/runQuery() · config.php:45 hardcoded key

Eksekusi SQL Tanpa Autentikasi hingga Webshell — Enkripsi Palsu, Kunci Publik

RINGKASAN

Controller Update dirilis tanpa guard autentikasi. Method-methodnya mendekrypt payload POST masukan dan langsung memasukkannya ke mysqli::multi_query() — stacked queries sesuai pesanan. Jebakannya seharusnya payload terenkripsi; tapi encryption key di-hardcode di repositori publik, identik di setiap instalasi. Saya mereimplementasi derivasi kunci HKDF-SHA512 milik CI3 secara offline, memalsukan payload terenkripsi yang valid, dan server mengeksekusi SQL di dalamnya. Dengan hak FILE milik user DB, SELECT ... INTO OUTFILE kemudian menulis webshell PHP ke webroot — rantai penuh, dari repo publik hingga RCE.

DAMPAK

Dump seluruh database — hash password, data siswa, bank soal, jawaban, nilai. Sisipkan user admin, ubah atau drop tabel apa pun. Bila user DB memegang hak FILE (umum di shared hosting), webshell mendarat di webroot. Ini temuan dengan severity maksimum dari audit ini: CVSS 10.0, terverifikasi dari ujung ke ujung.

REMEDIASI

Guard admin dan nonce status update wajib di semua method Update; encryption key acak per-instalasi yang di-generate saat instalasi; validasi skema JSON payload terhadap whitelist alih-alih konkatenasi ke multi_query; cabut hak FILE dari user DB aplikasi dan set secure_file_priv yang ketat.

BUKTI — DIREDAKSI

Diredaksi demi keamanan. Langkah reproduksi dan hasil pengamatan sudah diverifikasi pada instance self-hosted, namun ditahan dari publikasi ini selagi patch tersebar — detail eksploitasinya diserahkan ke maintainer sebagai bagian dari coordinated disclosure, bukan ke web terbuka.

GBT-004TINGGICVSS 8.6TERVERIFIKASI — POC DINAMISKOMPONEN
controllers/Siswa.php — getPost() · models/Post_model.php — getPostForUser()

Blind Time-Based SQL Injection — /siswa/getPost

RINGKASAN

Parameter GET kelas melewati xss_clean — yang bukan escaping SQL — lalu dikonkatenasi langsung ke klausa LIKE di dalam string WHERE. Sesi level siswa (role terendah di sistem) bisa menutup quote, menyeimbangkan kurung, mengomentari sisanya, dan menjalankan oracle waktu: SLEEP(4) dijawab empat detik, SLEEP(0) seketika. Ekstraksi karakter demi karakter menyusul setelahnya.

DAMPAK

Siswa mana pun bisa mengekstrak seluruh database lewat blind retrieval otomatis: hash kredensial, data pribadi teman sekelas dan guru, bank soal beserta jawabannya. Error SQL yang verbose (GBT-008) mempermudah pemetaannya.

REMEDIASI

Query Builder binding — where("a.kepada LIKE", "%$kelas%") — atau prepared statement; jangan pernah konkatenasi input ke string WHERE. Audit setiap pemanggilan get_where() yang menginterpolasi variabel.

BUKTI — DIREDAKSI

Diredaksi demi keamanan. Langkah reproduksi dan hasil pengamatan sudah diverifikasi pada instance self-hosted, namun ditahan dari publikasi ini selagi patch tersebar — detail eksploitasinya diserahkan ke maintainer sebagai bagian dari coordinated disclosure, bukan ke web terbuka.

GBT-005TINGGICVSS 8.6TERVERIFIKASI — POC DINAMISKOMPONEN
controllers/Bukurapor.php — GET tp/smt · Dashboard_model::getTahunById()

Blind Time-Based SQL Injection — /bukurapor

RINGKASAN

Pola sink yang sama, pintu berbeda: controller buku rapor meneruskan tp (tahun ajaran) dan smt (semester) ke lookup model yang membangun klausa get_where() dengan konkatenasi mentah. Satu AND SLEEP(4)# pada parameter tp menahan respons selama empat detik. Pola identik berulang di beberapa controller lain — satu sink, banyak jalur.

DAMPAK

Kelas ekstraksi database penuh yang sama seperti GBT-004, dapat dijangkau role terautentikasi mana pun yang bisa membuka buku rapor.

REMEDIASI

Parameter binding di getTahunById() dan getSemesterById() plus audit atas semua pemanggilnya; perlakukan ini sebagai satu perbaikan sistemik, bukan dua tiket.

BUKTI — DIREDAKSI

Diredaksi demi keamanan. Langkah reproduksi dan hasil pengamatan sudah diverifikasi pada instance self-hosted, namun ditahan dari publikasi ini selagi patch tersebar — detail eksploitasinya diserahkan ke maintainer sebagai bagian dari coordinated disclosure, bukan ke web terbuka.

GBT-006TINGGICVSS 8.2TERVERIFIKASI — RANTAI PENUHKOMPONEN
config.php csrf_exclude_uris · controllers/Kelasmateri.php — saveMateri()/uploadFile()

CSRF Dimatikan di Endpoint Materi — Upload Webshell Paksa

RINGKASAN

Tiga endpoint yang mengubah state — saveMateri, uploadfile, deletefile — dikecualikan dari proteksi CSRF demi kemudahan progress bar AJAX. Halaman yang dikontrol penyerang karenanya bisa mengarahkan browser guru yang sedang login untuk mengunggah file, dan lebih parah lagi: saveMateri memproses konten materi sebagai HTML, mendekode setiap data URI <img> base64, lalu menuliskannya ke disk dengan ekstensi file yang diambil dari MIME type yang dikontrol penyerang. application/php lolos sebagai file .php — webshell, tersimpan dan tersaji.

DAMPAK

Satu kunjungan ke halaman jebakan saat login sebagai guru atau admin menyerahkan webshell PHP di server kepada penyerang — RCE dengan satu klik korban. deletefile merangkap sebagai penghapusan file materi sembarangan.

REMEDIASI

Hapus ketiga URI dari csrf_exclude_uris dan buat handler-nya kompatibel CSRF (token via header AJAX); jangan pernah menurunkan ekstensi file dari MIME yang dikontrol klien — whitelist ekstensi gambar dan paksa img_*.jpg|png; matikan eksekusi PHP di dalam uploads/ sebagai defense in depth yang mutlak.

BUKTI — DIREDAKSI

Diredaksi demi keamanan. Langkah reproduksi dan hasil pengamatan sudah diverifikasi pada instance self-hosted, namun ditahan dari publikasi ini selagi patch tersebar — detail eksploitasinya diserahkan ke maintainer sebagai bagian dari coordinated disclosure, bukan ke web terbuka.

GBT-007SEDANGCVSS 6.1TERVERIFIKASI — POC DINAMISKOMPONEN
controllers/Pengumuman.php — save() · views/pengumuman/data.php:180

Stored XSS pada Pengumuman

RINGKASAN

Field teks pengumuman disimpan tanpa filter XSS dan dirender kembali dengan output <?= ?> mentah — tidak ada html_escape. Tag <script> yang diposting bertahan hidup sampai dinding pengumuman, tempat semua role di sekolah membacanya.

DAMPAK

Eksekusi script di browser setiap pembaca — admin, guru, siswa. Pencurian cookie diredam HttpOnly, tapi aksi UI atas nama korban, defacement, dan phishing tetap di atas meja.

REMEDIASI

html_escape() saat output; jika rich text benar-benar diperlukan, sanitasi sisi server dengan HTML purifier alih-alih mempercayai input.

BUKTI — DIREDAKSI

Diredaksi demi keamanan. Langkah reproduksi dan hasil pengamatan sudah diverifikasi pada instance self-hosted, namun ditahan dari publikasi ini selagi patch tersebar — detail eksploitasinya diserahkan ke maintainer sebagai bagian dari coordinated disclosure, bukan ke web terbuka.

GBT-008SEDANGCVSS 5.3TERVERIFIKASI — PENGAKTIFKOMPONEN
application/config/config.php:45 · index.php:39 · installer chmod 777

Encryption Key Hardcoded + Default Development

RINGKASAN

Tiga pengaktif dalam satu temuan. Encryption key di-hardcode di repo publik — nilai yang sama di setiap instalasi, dan kunci langsung untuk memalsukan payload GBT-003. ENVIRONMENT dirilis sebagai development, sehingga db_debug menyajikan error SQL lengkap — nomor error, path file, baris model — langsung ke pengguna. Dan panduan instalasi menginstruksikan chmod 777 pada database.php, membiarkan pintu GBT-002 terbuka lebar di deployment nyata.

DAMPAK

Bukan pelanggaran langsung secara sendirian, tapi menggandakan setiap temuan lain: kripto palsu, rekon verbose, dan config yang writable adalah dinding penyangga rantai Critical.

REMEDIASI

Generate encryption key acak saat instalasi; rilis ENVIRONMENT=production sebagai default; tulis ulang instruksi instalasi dengan permission yang waras.

BUKTI — DIREDAKSI

Diredaksi demi keamanan. Langkah reproduksi dan hasil pengamatan sudah diverifikasi pada instance self-hosted, namun ditahan dari publikasi ini selagi patch tersebar — detail eksploitasinya diserahkan ke maintainer sebagai bagian dari coordinated disclosure, bukan ke web terbuka.

GBT-009RENDAH / INFOCVSS REVIEW STATISKOMPONEN
controllers/Compare.php · Dbmanager::hapusBackup() · input->post(..., false) pattern

Aneka Temuan Code Review — Dev Tool Tanpa Auth dan Temannya

RINGKASAN

Temuan review statis yang tidak memerlukan bukti dinamis: utilitas dev Compare yang bisa membuat dan drop tabel tanpa autentikasi (berbahaya di mana pun DB group-nya ada); Dbmanager::hapusBackup() menghapus file tanpa validasi basename(); pola massal input->post(..., false) yang mematikan filter XSS pada field konten di banyak controller; csrf_cookie yang tidak HttpOnly; serta max-size upload yang dikontrol klien.

DAMPAK

Masing-masing adalah celah pengerasan alih-alih pelanggaran yang didemonstrasikan — tapi bersama-sama mereka menggambarkan aplikasi yang tidak pernah menjadikan keamanan bagian dari siklus pembuatannya.

REMEDIASI

Hapus utilitas dev dari rilis produksi; basename() plus whitelist pada penghapusan backup; aktifkan kembali filter di field konten dengan encoding sisi output; batas ukuran upload sisi server.

BUKTI — DIREDAKSI

Diredaksi demi keamanan. Langkah reproduksi dan hasil pengamatan sudah diverifikasi pada instance self-hosted, namun ditahan dari publikasi ini selagi patch tersebar — detail eksploitasinya diserahkan ke maintainer sebagai bagian dari coordinated disclosure, bukan ke web terbuka.

03  AKAR MASALAH

Lima pola menjelaskan kesembilan temuan:

TemuanAkar masalah
GBT-001 · GBT-002Installer tidak pernah dikunci setelah instalasi — semua method berbahaya di Install/Update tetap bisa dipanggil pada instance yang berjalan
GBT-003Endpoint internal "update database" tanpa auth, plus secret (encryption key) yang dibagikan ke publik via repo
GBT-004 · GBT-005Konkatenasi string ke klausa WHERE pada model — anti-pattern CodeIgniter klasik
GBT-006Pengecualian CSRF demi progress bar AJAX, tanpa kompensasi kontrol
GBT-007 · GBT-009.3Tidak ada strategi output-encoding terpusat

04  REMEDIASI

Prioritas satu — rilis sebagai hotfix v1.5.4. Kunci installer setelah instalasi, guard admin di setiap method Update, hapus pengecualian CSRF, dan parameter-binding pada jalur query yang terinjeksi (getPostForUser, getTahunById, getSemesterById) plus audit setiap pemanggilan get_where(... . $var).

Prioritas dua — rahasia per-instalasi. Generate encryption key saat instalasi, default environment ke production, matikan eksekusi PHP di dalam uploads/, dan html_escape saat output di views.

Prioritas tiga — cegah kekambuhan. Regression test keamanan untuk setiap endpoint install dan update, plus audit menyeluruh atas setiap sink input-ke-query di codebase.

Laporan dikemas untuk coordinated disclosure — advisory ke maintainer dulu, jendela patch, lalu kredit publik. Linimasa yang disarankan:

HariAksi
0Laporan dikirim ke maintainer (security advisory GitHub / email)
1–7Konfirmasi & triage bersama maintainer
≤ 14Patch rilis (v1.5.4) untuk GBT-001..003
30–90Public disclosure dengan kredit (koordinasi)

05  ETIKA

Semua berjalan pada instance self-hosted yang dibangun dari repositori publik. Tidak ada sistem produksi yang disentuh, tidak ada target pihak ketiga yang diuji, dan tidak ada data sekolah nyata yang pernah terancam oleh kerja ini. Artefak eksploitasi — webshell dan tabel uji — dihapus begitu verifikasi selesai. Tujuan latihan ini adalah patch-nya, bukan shell-nya.

ENTRI BERTAMBAH SEIRING KERJA SELESAI.