Retasan pertama saya: situs yang memungkinkan Anda mengatur kata sandi pengguna apa pun

Baru-baru ini, saya menemukan kerentanan menarik yang memungkinkan pengguna untuk mengatur situs tertentu untuk mengatur kata sandi apa pun. Keren ya

Itu lucu, dan saya pikir saya bisa menulis artikel yang menarik.

Anda menemukan itu.



Catatan: penulis artikel yang diterjemahkan bukan spesialis keamanan informasi, dan ini adalah perjalanan pertamanya ke dunia injeksi SQL. Dia meminta untuk "merendahkan kenaifannya."

Peringatan: penulis artikel yang diterjemahkan tidak akan mengungkapkan situs dengan kerentanan ini. Bukan karena dia memberi tahu pemiliknya tentang itu dan diikat oleh keheningan, tetapi karena dia ingin menjaga kerentanan untuk dirinya sendiri. Jika Anda mengetahui situs ini, harap tutup mulut (tsyts).

Anda tahu, inilah cara Anda terkadang membuka situs web di toolkit untuk pengembang, memeriksa kode yang diperkecil dan permintaan jaringan tanpa tujuan apa pun. Dan tiba-tiba Anda melihat ada sesuatu yang salah di sini. Sama sekali tidak seperti itu. Jadi saya melakukan sesuatu yang mirip dengan halaman profil pengguna di salah satu situs dan memperhatikan bahwa ketika Anda menghidupkan dan mematikan notifikasi tanda terima, halaman tersebut mengirimkan permintaan jaringan:

/api/users?email=no 

Dan saya berpikir: Saya ingin tahu apakah mereka membiarkan kebodohan? Mungkin saya harus mencoba injeksi SQL?

Saya mencari di internet untuk "xkcd little bobby tables" untuk menyegarkan ingatan saya tentang bagaimana melakukan injeksi SQL - saya tidak suka mereka - dan mulai bekerja.

Di tab jaringan Chrome, saya menyalin permintaan (Salin> Salin sebagai ambil) dan menempelkan hasilnya menjadi fragmen sehingga permintaan dapat diputar:

 fetch('https://blah.com/api/users', { credentials: 'include', headers: { authorization: 'Bearer blah', 'content-type': 'application/x-www-form-urlencoded', 'sec-fetch-mode': 'cors', 'x-csrf-token': 'blah', }, referrer: 'https://blah.com/blah', referrerPolicy: 'no-referrer-when-downgrade', body: 'email=no', // < -- The bit we're interested in method: 'POST', mode: 'cors', }); 

Sisa artikel ini ditujukan untuk meributkan garis body - ini adalah transportasi untuk mengirim instruksi ke server.

Pertama, saya mencoba mengubah nama belakang saya dengan menetapkan nilai pada kolom nama lastName , dengan fokus hanya pada namanya:

 { // ... body: `email=no', lastName='testing` } 

Tidak ada yang menarik terjadi. Kemudian saya melakukan hal yang sama dengan last_name , lalu saya mencoba keberuntungan saya dengan surname - dan oops! - halaman mengganti nama belakang saya dengan "pengujian".

Itu sangat menarik. Saya selalu menganggap suntikan SQL sedikit legenda buku. Fakta bahwa itu tidak benar - benar membuka dunia untuk kode yang memasukkan input pengguna langsung ke ekspresi SQL.

Sedikit filosofi
Baru-baru ini, saya mendekati banyak masalah dalam hidup saya dari sudut pandang hukum Sturgeon: "90% dari segalanya di sekitar adalah sampah." Saya menyadari bahwa jika Anda menganggap bahwa semuanya dilakukan dengan benar, maka Anda kehilangan banyak peluang. Saya pikir ketidakpercayaan yang baru ditemukan pada umat manusia ini telah memberi saya cukup kepercayaan untuk melakukan percobaan ini.

Untuk semua yang belum tahu, saya akan menjelaskan apa arti hasil yang saya temukan.
Saya percaya sesuatu yang serupa terjadi di server:

 const userId = someSessionStore.userId; const email = request.body.email; const sql = `UPDATE users SET email = '${email}' WHERE id = '${userId}'`; 

Saya yakin server mereka ditulis dalam PHP, tetapi saya tidak bisa bahasa ini, jadi saya akan menulis contoh dalam JavaScript. Juga, saya tidak terlalu pandai query SQL. Saya tidak tahu apakah tabel itu disebut user , atau users , atau user_table , dan itu tidak masalah.

Jika ID pengguna saya 1234, dan saya mengirim email = tidak, maka SQL berubah menjadi seperti ini:

 UPDATE users SET email = 'no' WHERE id = '1234' 

Dan jika Anda mengganti no dengan string no', surname = 'testing , maka SQL akan valid, tetapi rumit:

 UPDATE users SET email = 'no', surname = 'testing' WHERE id = '1234' 

Seperti yang Anda ingat, saya mengirim permintaan dari cuplikan kode di alat pengembang, sementara saya di halaman profil. Jadi mulai sekarang, Anda bisa memikirkan bidang nama keluarga di halaman ini (elemen HTML <input>) sebagai stdout kecil di mana Anda dapat menulis informasi dengan menetapkan nilai untuk akun pengguna saya di kolom surname di database.

Lalu saya bertanya-tanya apakah saya bisa menyalin data dari kolom lain ke kolom surname ?

Saya tidak mengerti apa yang harus dilakukan, apa yang harus dilakukan dengan SQL, dan selain itu, saya tidak tahu database mana yang digunakan di server. Jadi setelah setiap langkah, saya menghabiskan 20 menit mencari di internet, dan kemudian menggaruk-garuk kepala selama 20 menit, karena saya secara teratur memasukkan tanda kutip ke arah yang salah. Sungguh aneh bahwa saya tidak menghancurkan seluruh database.

Menyalin data dari satu kolom ke kolom yang lain ternyata sedikit lebih sulit, karena saya ingin mengirim permintaan seperti itu (seharusnya ada kolom password ):

 UPDATE users SET email = 'no', surname = password WHERE id = '1234' 

Perhatikan bahwa tidak ada kutipan dalam kode di sekitar password . Seingat Anda, perancang kueri super-modern akan terlihat seperti ini ...

 const sql = `UPDATE users SET email = '${email}' WHERE id = '${userId}'`; 

... yaitu, ketika Anda mencoba memberikan no', surname = password string yang dihasilkan tidak akan menjadi query SQL yang valid. Sebagai gantinya, saya membutuhkan seluruh string yang disuntikkan untuk menjadi bagian kedua dari permintaan, dan semua yang datang setelah itu harus diabaikan. Secara khusus, saya harus melewati WHERE dan; di akhir pernyataan SQL, serta komentar # sehingga informasi di sebelah kanannya diabaikan. Ya, saya jelaskan sangat.

Singkatnya, saya mengirim baris baru:

 { // ... body: `email=no', surname = password WHERE username = 'me@email.com'; #` } 

Dan baris berikut akan dikirim ke database:

 UPDATE users SET email = 'no', surname = password WHERE username = 'me@email.com'; # WHERE id = '1234' 

Harap dicatat bahwa basis data akan mengabaikan WHERE id = '1234' , karena bagian ini muncul setelah komentar # (melarang komentar dalam query SQL tampaknya merupakan cara yang baik untuk melindungi terhadap kode ceroboh).

Saya berharap kata sandi saya P @ ssword1 akan muncul dalam bentuk teks di bidang nama belakang, tetapi saya malah mendapatkan 00fcdde26dd77af7858a52e3913e6f3330a32b31.

Ini mengecewakan saya, meskipun itu tidak mengejutkan saya, dan saya terus mencoba menyalin hash kata sandi saya ke kolom kata sandi pengguna lain.

Izinkan saya menjelaskan untuk pemula: ketika Anda membuat akun di suatu tempat dan mengirim kata sandi baru P @ ssword1, itu berubah menjadi hash seperti 00fcdde26dd77af7858a52e3913e6f3330a32b31 dan disimpan dalam database. Melihat hash ini, tidak ada yang akan dapat menentukan kata sandi Anda (atau begitulah kata mereka).

Lain kali Anda masuk dan memasukkan kata sandi Kata sandi @ 1, server melakukan hash lagi dan membandingkannya dengan hash yang disimpan dalam database. Ini akan mengkonfirmasi kepatuhan tanpa menyimpan kata sandi Anda.

Ini berarti bahwa jika saya ingin memberi seseorang kata sandi P @ ssword1, saya harus mengatur kolom kata sandi pengguna ini ke 00fcdde26dd77af7858a52e3913e6f3330a32b31.

Ringan

Saya membuka peramban lain, membuat pengguna baru dengan surel yang berbeda dan pertama-tama memeriksa apakah saya dapat mengatur datanya. Diperbarui properti body untuk itu:

 { // ... body: `email=no', surname = 'WOOT!!' WHERE username = 'user-two@email.com'; #` } 

Saya mengeksekusi kode, memperbarui halaman pengguna ini, dan, ofiget, berhasil! Sekarang ia memiliki nama keluarga "WOOT !!" (nama gadis nenek saya).

Lalu saya mencoba mengatur kata sandi untuk pengguna ini:

  // ... body: `email=no', password = '00fcdde26dd77af7858a52e3913e6f3330a32b31' WHERE username = 'user-two@email.com'; #` } 

Dan tahukah Anda?!?!?

Itu tidak berhasil. Sekarang saya tidak memiliki akses ke akun kedua.
Ternyata saya membuat dua kesalahan, perhitungannya butuh beberapa jam. Para pakar keamanan informasi yang membaca artikel ini telah memahami apa yang mereka bicarakan dan mungkin menertawakan orang bodoh yang menulis "eksploit" -nya yang tercantum di halaman pertama Hacking for the Youngest.

Nuuuuuu, pada akhirnya saya mencari jaringan untuk "kata sandi hash" dan memperhatikan bahwa banyak hash lebih panjang dari 00fcdde26dd77af7858a52e3913e6f3330a32b31. Sepertinya dia sedang menanam di suatu tempat.
Saya mencoba memasukkan sepotong teks di bidang nama keluarga, dan menemukan batas 40 karakter (ada baiknya mereka mengatur atribut maxlength untuk <input> agar sesuai dengan batasan database).

Sekarang saya hanya tertarik pada 40 karakter pertama dari hash, yang bisa lebih lama. Saya mencari "sql substring", dan segera mengirim permintaan berikut ke server:

 { // ... body: `email=no', surname = SUBSTRING(password, 30, 1000) WHERE username = 'me@email.com'; #` } 

Dimulai dengan 30 untuk memastikan bahwa 10 karakter pertama tumpang tindih dengan 10 karakter terakhir 00fcdde26dd77af7858a52e3913e6f3330a32b31. Atau yang terakhir 9. Atau 11.

Penyimpangan liris
Saya pikir ketika saya mati dan pergi ke neraka, mereka akan memaksa saya untuk menonton semua kesalahan saya selamanya dalam gerakan lambat, satu demi satu. Tampilan close-up yang menunjukkan wajahku sementara aku, berulang kali, menyadari kebodohanku yang tiada akhir.

Kembali ke kenyataan: karakter tumpang tindih, dan menggabungkan garis, saya mendapat hash 64 karakter. Sekali lagi saya mencoba menyalinnya ke pengguna kedua:

  { // ... body: `email=no', password = '00fcdde26dd77af7858a52e3913e6f3330a32b3121a61bce915cc6145fc44453' WHERE username = 'user-two@email.com'; #` } 

Dan tahukah Anda?!?!
Anda dapat menebaknya, karena saya menyebutkan dua kesalahan.

Saya masih tidak bisa masuk ke akun kedua, tetapi sudah dekat dengan ini (akan menyenangkan bagi saya untuk mengetahuinya pada saat itu).

Saya mencari "kata sandi praktik terbaik basis data" dan menemukan / mengingat hal seperti "garam".

Menggunakan garam berarti bahwa jika Anda membuat hash untuk P @ ssword1 untuk satu pengguna, maka untuk pengguna lain kata sandi yang sama akan memberikan hash yang berbeda (garam lain digunakan). Tentu saja, satu kata sandi hash tidak akan berfungsi untuk dua pengguna, garamnya berbeda.

Tampaknya pintar, tetapi sekaligus bodoh. Dalam semua contoh di tabel, hanya ada kolom lain yang disebut garam. Apakah ini tidak berarti bahwa saya perlu menyalin data dari dua kolom, bukan satu? Bukankah itu terlihat seperti kunci kedua, yang cocok dengan kunci yang sama?

Saya mengubah kueri dengan harapan menyalin nilai dari kolom yang mungkin disebut garam,
ke kolom nama keluarga:

  { // ... body: `email=no', surname = salt WHERE username = 'myemail@email.com'; #` } 

Serangkaian karakter acak muncul di bidang nama keluarga, pertanda baik. Untuk mendapatkan garam 64 karakter, saya menggunakan SUBSTRING lagi.

Semuanya sudah siap. Saya memiliki hash kata sandi dan garam yang digunakan untuk membuatnya, Anda hanya perlu menyalinnya ke pengguna lain. Dan saya mengirim permintaan jaringan terakhir saya malam itu:

 fetch('https://blah.com/api/users', { credentials: 'include', headers: { authorization: 'Bearer blah', 'content-type': 'application/x-www-form-urlencoded', 'sec-fetch-mode': 'cors', 'x-csrf-token': 'blah', }, referrer: 'https://blah.com/blah', referrerPolicy: 'no-referrer-when-downgrade', body: `email=no', password = '00fcdde26dd77af7858a52e3913e6f3330a32b3121a61bce915cc6145fc44453', salt = '8b7df143d91c716ecfa5fc1730022f6b421b05cedee8fd52b1fc65a96030ad52' WHERE username = 'user-two@gmail.com'; #`, method: 'POST', mode: 'cors', }); 

Berhasil! Sekarang saya bisa masuk ke akun kedua dengan kata sandi dari akun pertama.
Bukankah itu gila?

* * *

Ada banyak trial and error, tetapi ketika saya memilih pengguna yang sebenarnya, saya pertama-tama akan mendapatkan garam dan hash dan menyimpannya bersama saya. Kemudian saya akan mengganti hash asinnya dengan hash saya, seperti yang dijelaskan dalam artikel, masuk dan langsung mengganti garam dan hash dengan nilai asli. Saya hanya perlu mengubah kata sandi orang lain untuk sepersekian detik saat saya masuk, sehingga mereka hampir pasti tidak akan menemukan saya.

Secara teori, tentu saja. Saya tidak akan pernah melakukan itu.

* * *

Anda mungkin bertanya-tanya apakah ini adalah cerita fiksi. Tidak dibuat-buat. Saya mengubah beberapa detail kecil untuk melindungi diri dari tuduhan, tetapi segala sesuatu seperti yang dijelaskan. Dan, tentu saja, pada kenyataannya, saya melaporkan kerentanan kepada pemilik situs.

Tetapi saya tidak bisa menahan diri untuk bertanya pada diri sendiri apakah ini hanya keberuntungan pemula. Ini benar-benar situs pertama di mana saya mencoba injeksi SQL, dan semuanya seolah disiapkan untuk saya, seolah-olah saya telah lulus ujian peretasan untuk anak-anak.

Situs yang saya jelaskan kecil, memiliki beberapa pengguna (34.718). Ini adalah layanan berbayar, jadi bagi peretas berpengalaman itu tidak menarik. Namun saya sadar bahwa ini mungkin.

Singkatnya, sekarang saya terpikat pada seluruh topik ini dengan keamanan informasi. Bagi saya, dua kegiatan favorit digabungkan di dalamnya: menulis kode dan hooliganisme. Jadi googling "keamanan informasi memberi gaji Australia", saya pikir saya menemukan diri saya pekerjaan baru.
Terima kasih sudah membaca!

PS: terjemahan artikel mencoba untuk mempertahankan gaya penulis sebanyak mungkin :)

Source: https://habr.com/ru/post/id468695/


All Articles