NoSQL injection — bu zaiflik bo'lib, unda hujumchi ilova NoSQL bazasiga yuboradigan so'rovlarga (query) aralasha oladi. NoSQL injection hujumchiga quyidagilar imkonini berishi mumkin:
NoSQL bazalar ma'lumotlarni an'anaviy SQL relatsion jadvallaridan farqli formatda saqlaydi va oladi. Ular SQL kabi yagona standart o'rniga keng ko'lamli so'rov tillaridan foydalanadi va relatsion cheklovlari kamroq.
NoSQL bazalar va ular SQL bazalaridan qanday farq qilishi haqida ko'proq ma'lumot uchun NoSQL bazalar sahifasiga qarang.
NoSQL injection ikki xil turga bo'linadi:
Ushbu mavzuda biz umuman NoSQL zaifliklarini qanday tekshirishni ko'rib chiqamiz, so'ngra eng ommabop NoSQL bazasi bo'lgan MongoDB'dagi zaifliklarni ekspluatatsiya qilishga e'tibor qaratamiz. Shuningdek, o'rgangan narsalaringizni amalda sinab ko'rishingiz uchun bir nechta laboratoriya (lab) taqdim etganmiz.
NoSQL injection zaifliklarini so'rov sintaksisini buzishga urinish orqali aniqlashingiz mumkin. Buning uchun har bir kiritmani (input) tizimli ravishda sinab ko'ring — agar ular ilova tomonidan yetarlicha tozalanmasa (sanitize) yoki filtrlanmasa, baza xatosini yoki boshqa aniqlanadigan xatti-harakatni keltirib chiqaradigan fuzz satrlari va maxsus belgilar yuboring.
Agar siz maqsadli bazaning API tilini bilsangiz, o'sha tilga tegishli maxsus belgilar va fuzz satrlaridan foydalaning. Aks holda, bir nechta API tillarini nishonga olish uchun turli fuzz satrlaridan foydalaning.
Mahsulotlarni turli toifalarda ko'rsatadigan xarid ilovasini tasavvur qiling. Foydalanuvchi Fizzy drinks toifasini tanlaganda, uning brauzeri quyidagi URL'ni so'raydi:
https://insecure-website.com/product/lookup?category=fizzyBu ilovani MongoDB bazasidagi product kolleksiyasidan tegishli mahsulotlarni olish uchun JSON so'rovini yuborishga majbur qiladi:
this.category == 'fizzy'Kiritma zaif bo'lishi mumkinligini tekshirish uchun category parametrining qiymatiga fuzz satrini yuboring. MongoDB uchun misol satr:
'"`{
;$Foo}
$Foo \xYZUshbu fuzz satridan foydalanib quyidagi hujumni tuzing:
https://insecure-website.com/product/lookup?category='%22%60%7b%0d%0a%3b%24Foo%7d%0d%0a%24Foo%20%5cxYZ%00Agar bu asl javobdan o'zgarishga olib kelsa, bu foydalanuvchi kiritmasi to'g'ri filtrlanmagan yoki tozalanmaganini ko'rsatishi mumkin.
NoSQL injection zaifliklari turli kontekstlarda yuzaga kelishi mumkin va siz fuzz satrlaringizni shunga mos ravishda moslashtirishingiz kerak. Aks holda, siz shunchaki validatsiya xatolarini keltirib chiqarishingiz mumkin, ya'ni ilova so'rovingizni hech qachon bajarmaydi.
Ushbu misolda biz fuzz satrini URL orqali kiritmoqdamiz, shuning uchun satr URL-kodlangan. Ba'zi ilovalarda payload'ingizni buning o'rniga JSON xususiyati orqali kiritishingiz kerak bo'lishi mumkin. Bu holda, ushbu payload '\"`{\r;$Foo}\n$Foo \\xYZ\u0000 ko'rinishiga keladi.
Ilova qaysi belgilarni sintaksis sifatida talqin qilishini aniqlash uchun alohida belgilarni kiritishingiz mumkin. Masalan, siz ' ni yuborishingiz mumkin, bu quyidagi MongoDB so'roviga olib keladi:
this.category == '''Agar bu asl javobdan o'zgarishga olib kelsa, bu ' belgisi so'rov sintaksisini buzib, sintaksis xatosiga sabab bo'lganini ko'rsatishi mumkin. Buni kiritmaga to'g'ri (valid) so'rov satrini yuborish orqali, masalan tirnoq belgisini ekranlash (escape) orqali tasdiqlashingiz mumkin:
this.category == '\''Agar bu sintaksis xatosiga olib kelmasa, bu ilova injection hujumiga zaif ekanligini bildirishi mumkin.
Zaiflikni aniqlagach, keyingi qadam — NoSQL sintaksisidan foydalanib boolean shartlarga ta'sir o'tkaza olishingizni aniqlash.
Buni tekshirish uchun ikkita so'rov yuboring: biri noto'g'ri (false) shart bilan, ikkinchisi to'g'ri (true) shart bilan. Masalan, siz ' && 0 && 'x va ' && 1 && 'x shartli iboralaridan quyidagicha foydalanishingiz mumkin:
https://insecure-website.com/product/lookup?category=fizzy'+%26%26+0+%26%26+'xhttps://insecure-website.com/product/lookup?category=fizzy'+%26%26+1+%26%26+'xAgar ilova o'zini boshqacha tutsa, bu false shart so'rov mantiqiga ta'sir qilishini, ammo true shart ta'sir qilmasligini ko'rsatadi. Bu ushbu turdagi sintaksisni kiritish server tomonidagi so'rovga ta'sir qilishini bildiradi.
Endi siz boolean shartlarga ta'sir o'tkaza olishingizni aniqladingiz, shu bilan zaiflikni ekspluatatsiya qilish uchun mavjud shartlarni bekor qilishga urinishingiz mumkin. Masalan, siz har doim true'ga teng bo'ladigan JavaScript shartini, masalan '||'1'=='1 ni kiritishingiz mumkin:
https://insecure-website.com/product/lookup?category=fizzy%27%7c%7c%27%31%27%3d%3d%27%31Bu quyidagi MongoDB so'roviga olib keladi:
this.category == 'fizzy'||'1'=='1'Kiritilgan shart har doim true bo'lgani uchun, o'zgartirilgan so'rov barcha elementlarni qaytaradi. Bu sizga istalgan toifadagi barcha mahsulotlarni, jumladan yashirin yoki noma'lum toifalarni ham ko'rish imkonini beradi.
NoSQL so'roviga har doim true'ga teng bo'ladigan shartni kiritganda ehtiyot bo'ling. Garchi bu siz kiritayotgan dastlabki kontekstda zararsiz bo'lishi mumkin bo'lsa-da, ilovalar bitta so'rovdagi ma'lumotni bir nechta turli so'rovlarda ishlatishi keng tarqalgan. Agar ilova undan, masalan, ma'lumotni yangilashda yoki o'chirishda foydalansa, bu tasodifiy ma'lumot yo'qotilishiga olib kelishi mumkin.
Toifa qiymatidan keyin null belgisini ham qo'shishingiz mumkin. MongoDB null belgisidan keyingi barcha belgilarni e'tiborsiz qoldirishi mumkin. Bu MongoDB so'rovidagi har qanday qo'shimcha shartlar e'tiborsiz qoldirilishini anglatadi. Masalan, so'rovda qo'shimcha this.released cheklovi bo'lishi mumkin:
this.category == 'fizzy' && this.released == 1this.released == 1 cheklovi faqat chiqarilgan (released) mahsulotlarni ko'rsatish uchun ishlatiladi. Chiqarilmagan mahsulotlar uchun, ehtimol, this.released == 0.
Bu holatda, hujumchi quyidagicha hujum tuzishi mumkin:
https://insecure-website.com/product/lookup?category=fizzy'%00Bu quyidagi NoSQL so'roviga olib keladi:
this.category == 'fizzy'\u0000' && this.released == 1Agar MongoDB null belgisidan keyingi barcha belgilarni e'tiborsiz qoldirsa, bu released maydoni 1 ga o'rnatilishi talabini olib tashlaydi. Natijada, fizzy toifasidagi barcha mahsulotlar, jumladan chiqarilmagan mahsulotlar ham ko'rsatiladi.
NoSQL bazalar ko'pincha so'rov operatorlaridan foydalanadi — ular so'rov natijasiga kiritilishi uchun ma'lumot qanoatlantirishi kerak bo'lgan shartlarni belgilash usullarini beradi. MongoDB so'rov operatorlariga misollar:
$where — JavaScript ifodasini qanoatlantiradigan hujjatlarga mos keladi.$ne — belgilangan qiymatga teng bo'lmagan barcha qiymatlarga mos keladi.$in — massivda ko'rsatilgan barcha qiymatlarga mos keladi.$regex — qiymatlari belgilangan muntazam ifodaga (regular expression) mos keladigan hujjatlarni tanlaydi.Siz NoSQL so'rovlarini manipulyatsiya qilish uchun so'rov operatorlarini kiritishingiz mumkin. Buning uchun turli operatorlarni bir qator foydalanuvchi kiritmalariga tizimli ravishda yuboring, so'ngra javoblarda xato xabarlari yoki boshqa o'zgarishlarni ko'rib chiqing.
JSON xabarlarida siz so'rov operatorlarini ichma-ich (nested) obyektlar sifatida kiritishingiz mumkin. Masalan, {"username":"wiener"} {"username":{"$ne":"invalid"}} ga aylanadi.
URL asosidagi kiritmalar uchun so'rov operatorlarini URL parametrlari orqali kiritishingiz mumkin. Masalan, username=wiener username[$ne]=invalid ga aylanadi. Agar bu ishlamasa, quyidagilarni sinab ko'rishingiz mumkin:
GET dan POST ga o'zgartiring.Content-Type sarlavhasini application/json ga o'zgartiring.So'rov metodini avtomatik ravishda o'zgartirish va URL-kodlangan POST so'rovini JSON'ga aylantirish uchun Content Type Converter kengaytmasidan foydalanishingiz mumkin.
Foydalanuvchi nomi va parolni POST so'rovi tanasida qabul qiladigan zaif ilovani tasavvur qiling:
{"username":"wiener","password":"peter"}Har bir kiritmani bir qator operatorlar bilan sinab ko'ring. Masalan, foydalanuvchi nomi kiritmasi so'rov operatorini qayta ishlashini tekshirish uchun quyidagi injection'ni sinab ko'rishingiz mumkin:
{"username":{"$ne":"invalid"},"password":"peter"}Agar $ne operatori qo'llanilsa, bu foydalanuvchi nomi invalid ga teng bo'lmagan barcha foydalanuvchilarni so'raydi.
Agar foydalanuvchi nomi ham, parol kiritmasi ham operatorni qayta ishlasa, quyidagi payload yordamida autentifikatsiyani chetlab o'tish mumkin bo'lishi mumkin:
{"username":{"$ne":"invalid"},"password":{"$ne":"invalid"}}Bu so'rov foydalanuvchi nomi ham, parol ham invalid ga teng bo'lmagan barcha login ma'lumotlarini qaytaradi. Natijada, siz kolleksiyadagi birinchi foydalanuvchi sifatida ilovaga kirasiz.
Muayyan hisobni nishonga olish uchun ma'lum foydalanuvchi nomini yoki taxmin qilgan foydalanuvchi nomingizni o'z ichiga olgan payload tuzishingiz mumkin. Masalan:
{"username":{"$in":["admin","administrator","superadmin"]},"password":{"$ne":""}}Ko'pgina NoSQL bazalarda ba'zi so'rov operatorlari yoki funksiyalari cheklangan JavaScript kodini ishga tushirishi mumkin, masalan MongoDB'ning $where operatori va mapReduce() funksiyasi. Bu shuni anglatadiki, agar zaif ilova ushbu operatorlar yoki funksiyalardan foydalansa, baza so'rovning bir qismi sifatida JavaScript'ni baholashi (evaluate) mumkin. Shuning uchun siz bazadan ma'lumot chiqarib olish uchun JavaScript funksiyalaridan foydalana olishingiz mumkin.
Foydalanuvchilarga boshqa ro'yxatdan o'tgan foydalanuvchi nomlarini qidirish imkonini beradigan va ularning rolini ko'rsatadigan zaif ilovani tasavvur qiling. Bu quyidagi URL'ga so'rov yuboradi:
https://insecure-website.com/user/lookup?username=adminBu users kolleksiyasining quyidagi NoSQL so'roviga olib keladi:
{"$where":"this.username == 'admin'"}So'rov $where operatoridan foydalangani uchun, siz ushbu so'rovga JavaScript funksiyalarini kiritishga urinib, u maxfiy ma'lumotni qaytarishini ta'minlashingiz mumkin. Masalan, siz quyidagi payload'ni yuborishingiz mumkin:
admin' && this.password[0] == 'a' || 'a'=='bBu foydalanuvchi parol satrining birinchi belgisini qaytaradi, bu esa sizga parolni belgima-belgi chiqarib olish imkonini beradi.
Shuningdek, ma'lumot chiqarib olish uchun JavaScript'ning match() funksiyasidan foydalanishingiz mumkin. Masalan, quyidagi payload parolda raqamlar bor-yo'qligini aniqlash imkonini beradi:
admin' && this.password.match(/\d/) || 'a'=='bMongoDB qat'iy sxemani talab qilmaydigan yarim tuzilgan (semi-structured) ma'lumotlarni boshqargani uchun, JavaScript injection yordamida ma'lumot chiqarib olishdan oldin kolleksiyadagi to'g'ri (valid) maydonlarni aniqlashingiz kerak bo'lishi mumkin.
Masalan, MongoDB bazasi password maydonini o'z ichiga oladimi yo'qligini aniqlash uchun quyidagi payload'ni yuborishingiz mumkin:
https://insecure-website.com/user/lookup?username=admin'+%26%26+this.password!%3d'Payload'ni mavjud maydon uchun va mavjud bo'lmagan maydon uchun yana yuboring. Ushbu misolda siz username maydoni mavjudligini bilasiz, shuning uchun quyidagi payload'larni yuborishingiz mumkin:
admin' && this.username!='admin' && this.foo!='Agar password maydoni mavjud bo'lsa, javob mavjud maydon (username) uchun javob bilan bir xil bo'lishini, ammo mavjud bo'lmagan maydon (foo) uchun javobdan farq qilishini kutasiz.
Agar turli maydon nomlarini sinab ko'rmoqchi bo'lsangiz, turli potentsial maydon nomlarini aylanib chiqish uchun so'zlar ro'yxati (wordlist) yordamida lug'at hujumini (dictionary attack) amalga oshirishingiz mumkin.
Muqobil ravishda, maydon nomlarini belgima-belgi chiqarib olish uchun NoSQL operator injection'dan foydalanishingiz mumkin. Bu sizga taxmin qilish yoki lug'at hujumini amalga oshirishga hojat qoldirmasdan maydon nomlarini aniqlash imkonini beradi. Buni qanday qilishni keyingi bo'limda o'rgatamiz.
Hatto asl so'rov sizga ixtiyoriy JavaScript'ni ishga tushirishga imkon beradigan operatorlardan foydalanmasa ham, siz ushbu operatorlardan birini o'zingiz kiritishingiz mumkin. So'ngra ilova ushbu operator orqali kiritgan JavaScript'ingizni bajaradimi yo'qligini aniqlash uchun boolean shartlardan foydalanishingiz mumkin.
Foydalanuvchi nomi va parolni POST so'rovi tanasida qabul qiladigan zaif ilovani tasavvur qiling:
{"username":"wiener","password":"peter"}Operatorlarni kirita olishingizni tekshirish uchun $where operatorini qo'shimcha parametr sifatida qo'shishga urinib ko'rishingiz mumkin, so'ngra bitta so'rovni shart false'ga teng bo'ladigan, boshqasini esa true'ga teng bo'ladigan qilib yuboring. Masalan:
{"username":"wiener","password":"peter", "$where":"0"}{"username":"wiener","password":"peter", "$where":"1"}Agar javoblar o'rtasida farq bo'lsa, bu $where bandidagi JavaScript ifodasi baholanayotganini ko'rsatishi mumkin.
Agar siz JavaScript'ni ishga tushirishga imkon beradigan operatorni kiritgan bo'lsangiz, ma'lumot maydonlari nomini chiqarib olish uchun keys() metodidan foydalanishingiz mumkin. Masalan, siz quyidagi payload'ni yuborishingiz mumkin:
"$where":"Object.keys(this)[0].match('^.{0}a.*')"Bu foydalanuvchi obyektidagi birinchi ma'lumot maydonini tekshiradi va maydon nomining birinchi belgisini qaytaradi. Bu sizga maydon nomini belgima-belgi chiqarib olish imkonini beradi.
Muqobil ravishda, siz JavaScript'ni ishga tushirishga imkon bermaydigan operatorlar yordamida ma'lumot chiqarib olishingiz mumkin. Masalan, ma'lumotni belgima-belgi chiqarib olish uchun $regex operatoridan foydalanishingiz mumkin.
Foydalanuvchi nomi va parolni POST so'rovi tanasida qabul qiladigan zaif ilovani tasavvur qiling. Masalan:
{"username":"myuser","password":"mypass"}Siz $regex operatori qayta ishlanishini quyidagicha tekshirishdan boshlashingiz mumkin:
{"username":"admin","password":{"$regex":"^.*"}}Agar ushbu so'rovga javob noto'g'ri parol yuborganingizda oladigan javobdan farq qilsa, bu ilova zaif bo'lishi mumkinligini ko'rsatadi. Siz $regex operatoridan foydalanib ma'lumotni belgima-belgi chiqarib olishingiz mumkin. Masalan, quyidagi payload parol a bilan boshlanishini tekshiradi:
{"username":"admin","password":{"$regex":"^a.*"}}Ba'zan baza xatosini keltirib chiqarish ilova javobida farqqa olib kelmaydi. Bunday vaziyatda siz shartli vaqt kechikishini keltirib chiqarish uchun JavaScript injection'dan foydalanib zaiflikni hali ham aniqlashingiz va ekspluatatsiya qilishingiz mumkin.
Vaqtga asoslangan NoSQL injection'ni amalga oshirish uchun:
{"$where": "sleep(5000)"} muvaffaqiyatli injection'da 5000 ms ataylab kechikish keltirib chiqaradi.Quyidagi vaqtga asoslangan payload'lar parol a harfi bilan boshlansa vaqt kechikishini keltirib chiqaradi:
admin'+function(x){var waitTill = new Date(new Date().getTime() + 5000);while((x.password[0]==="a") && waitTill > new Date()){};}(this)+'admin'+function(x){if(x.password[0]==="a"){sleep(5000)};}(this)+'NoSQL injection hujumlarining oldini olishning to'g'ri usuli qo'llanilayotgan aniq NoSQL texnologiyasiga bog'liq. Shuning uchun biz siz tanlagan NoSQL bazasi uchun xavfsizlik hujjatlarini o'qishni tavsiya qilamiz. Shunga qaramay, quyidagi umumiy ko'rsatmalar ham yordam beradi: