در این بخش از دوره آموزشی SEC542 از موسسه SANS با ادامه مبحث حمله Blind SQL Injection همراه شما بوده و جزئیات بیشتری از این حمله را مطرح می نماییم.
Custom Errors and SQLi
اولین تفاوت صفحههای sqli.php و bsqli.php اینست که صفحه اول خطاهای دیتابیس را نشان میداد در حالی که صفحه دوم خطایی نشان نمیداد. با این حال هر دو صفحه از یک کوئری دیتابیس استفاده میکنند:
mysql_query (“SELECT * FROM Customers WHERE lname = ‘”.$_GET[“name”].”’;”)
تفاوت ساده این دو صفحه در این است که یک کد اضافه PHP در صفحه sqli.php افزوده شده که پیامهای خطا را بازمیگرداند. این کد die(mysql_error()) میباشد.
بنابراین در هر دو صفحه کوئری یکسان و آسیبپذیری یکسان وجود دارد اما رفتار صفحات و نحوه کشف آسیبپذیری از آنها متفاوت است. حال با وجود پیامهای customize شده چگونه میتوانیم تشخیص دهیم که ورودی ما در دیتابیس ترجمه و تفسیر میشود؟ باید به دنبال راهی برای اثبات این قضیه باشیم.
بدون خطاهای دیتابیس
بدون خطای دیتابیس چگونه میتوانیم بفهمیم آسیبپذیری SQLi وجود دارد یا خیر؟ باید راهی وجود داشته که به وسیله آن بفهمیم آیا ورودی ما در کوئری دیتابیس قرار گرفته و اجرا میشود یا خیر.
صفحه bsqli.php را در نظر بگیرید. ورودیهای ما به شکل زیر میباشند:
Dent – دیتا برگردانده شد.
Dent’ – پیام “Employee not found” برگردانده شد.
شاید بتوانیم عبارتی پیدا کنیم که اجرا شدن آن در دیتابیس نتیجه یکسانی با ورودی Dent بازگرداند. اگر این عبارت، همان نتیجه رشته Dent را بازگرداند، امکان دارد که پارامتر مورد نظر آسیبپذیری SQLi داشته باشد.
رشتههای معادل در injection
بیایید چند رشته بسازیم که معادل رشته Dent باشد. دو تکنیک موثر برای ساخت رشتههای معادل، کامنت کردن و بهم چسباندن (concatenation) است.
کامنت کردن یعنی تزریق کامنتهای inline یا رایجتر از آن، تزریق کامنت به انتهای injection است. تکنیک concatenation به ما اجازه میدهد تا رشته خود را از تکههای کوچکتر ساخته و بگذاریم دیتابیس این تکهها را بهم بچسباند.
تزریق کامنت
دریافت خطای دیتابیس برای تست نفوذکنندگان بسیار جذاب است… البته در ابتدای کار. سپس به نقطهای میرسیم که باید خطا را برطرف کنیم. کامنتها (– , /* */ , #) مانند یک ابزار به ما کمک میکنند تا خطاهای نحوی SQL را رفع کنیم. بطور کلی ما از کامنتها به عنوان پسوند در injection استفاده میکنیم.
منطقی به نظر میرسد که با تزریق پیلود به وسط یک کوئری SQL، کوئری به مشکل برخورده و بهم میریزد. اما قرار دادن یک کامنت مانند – یا # در انتهای پیلود، تاثیر مابقی دستور SQL (از نقطه تزریق به بعد) را خنثی میکند. گرچه این روش خیلی حرفهای به نظر نمیرسد اما خطاهای SQL را برطرف کرده و injection با موفقیت انجام میشود.
نکاتی در مورد یافتن SQL Injection
نکته: ممکن است نیاز داشته باشید یک space بعد از کامنت (–) خود اضافه کنید تا دستور به درستی کار کند.
De’/**/’nt = De’ ‘nt = Dent’;#
حال بیایید به نتایج injection خود برای رشتههای معادل Dent نگاه کنیم. ما سه رشته را با تکنیک کامنت و یکی را به روش concatenation تزریق کردیم:
Dent’;#
Dent’;–
De’/**/’nt
De’ ‘nt
همه رشتههای بالا در صورت ارسال، نتیجه یکسانی با رشته اولیه Dent را برمیگردانند. ما با یک SQLi روبرو هستیم.
تست استنتاجی Binary/Boolean
در تست قبلی، یک پیام خطای SQL یافتیم که ما را به پیام “No employee found” هدایت کرد. در عین حال رشته Dent و رشتههای معادل آن مانند De’ ‘nt ، به ما دیتا برگرداندند.
حالا بیایید یک تکنیک کلیدی دیگر که برای نیازهای SQLi بسیار مفید است را بررسی کنیم. این تکنیک، تکنیک استنتاج یا نتیجهگیری است. از این تکنیک برای injection های blind استفاده میکنیم.
در ادامه، بعضی از ورودیهایی که به ما دیتای کارمندان را برمیگرداند را مشاهده میکنید:
Dent’ AND 1;#
Dent’ AND 1=1;#
و حالا، ورودیهایی که پیام “Employee not found” را برمیگرداند:
Dent’ AND 0;#
Dent’ AND 1=0;#
اینکه خروجی دو مقدار “AND 1=0” و “AND 1=1” متفاوت است، به ما کمک زیادی در تست میکند. این قدرت شروط Boolean/Binary/True|False است.
افزایش Blindness در Blind SQL Injection
اگرچه کشف و اکسپلویت آسیبپذیری با پیامهای خطای customize شده سخت به نظر میرسد اما میزان کوری در دیدن نتایج کوئریهای SQL باز هم میتواند افزایش یابد. در حملات Blind SQL Injection ، روش استنتاجی که با تشخیص مقادیر TRUE از FALSE کار میکند، سلاح بسیار قدرتمندی برای ما محسوب میشود.
injection زیر را در نظر بگیرید:
Prefix: Dent’ AND
Evaluation: substr((select table_name from information_schema.tables limit 1),1,1) > “a”
suffix: ;#
ما هنوز صحبتهای خود را در مورد SQL تکمیل نکردهایم اما کد بالا را بخش به بخش بررسی میکنیم. تا الان راجع به پیشوند (prefix) و پسوند (suffix) یاد گرفتهایم. ارزیابی (evaluation) کمی متفاوت است. این عبارت یک کوئری ساده است که به دلیل وجود دستور select در آن، کمی پیچیده به نظر میرسد.
اما همین select با اجرای یک دستور قوی، نام جداول دیتابیس را مشخص میکند. درواقع این دستور چک میکند که آیا اولین حرف از نام اولین جدول، حرفی بزرگتر از حرف a در الفبا است یا خیر. اگر حرف مورد نظر از حروف بعدی a باشد، شرط برقرار بوده و 1 برگردانده میشود، در نتیجه کوئری ما به (Dent’ AND 1;#) تغییر مییابد. با یک مثال سادهتر بقیه موارد را بررسی میکنیم.
تابع substr() اجازه انتخاب بخشی از رشته موجود را میدهد. این که کدام بخش از رشته، مورد نظر است، توسط اعداد ارائه شده به تابع، تعیین میشود. این اعداد همان offset و limit هستند.
substr(“sec542”,2,1) < “m” is TRUE
عبارت بالا اگر select شود، مقدار TRUE را بازمیگرداند. دلیل آن این است که حرف دوم از رشته “sec542” حرف “e” است که در الفبا قبل از حرف “m” قرار میگیرد.
استنتاجهای زمانی در Blind SQL Injection
تکنیکهای استنتاجی زمانی قدرت واقعی خود را نشان میدهند که هیچگونه خطا یا خروجی از SQLi نداریم. در این موارد تکنیکهای استنتاجی شانس دیگری برای کشف و اکسپلویت SQLi به ما میدهند.
برای تست این تکنیک، از مثال قبل استفاده میکنیم. در اینجا هنوز هم میتوانیم از مفهوم AND 1 در مقابل AND 0 استفاده کنیم ولی باید اپلیکیشن را وادار کنیم که درست یا غلط بودن کوئری را به ما بگوید. بدون مشاهده خروجی، این کار سخت میشود. اما تصور کنید بتوانیم به نحوی بر روی سرور دیتابیس تاثیری بگذاریم، آنگاه شاید بتوانیم کاری کنیم که دیتابیس، اپلیکیشن را طوری فریب بدهد که اپلیکیشن نتیجه ارزیابی کوئری را در اختیار ما قرار دهد.
در این تکنیک، با استفاده از SQLi، پیلودی inject میکنیم که اگر نتیجه آن TRUE بود، بر پاسخگویی اپلیکیشن تاثیر محسوسی بگذارد.
دو متد قابل استفاده برای این کار به صورت زیر میباشند:
Sleep(10) – MySQL
WAITFOR DELAY ‘0:0:10’ – MSSQL
مثالهای بالا، 10 ثانیه تاخیر در پاسخگویی اپلیکیشن ایجاد میکنند. روشهای خلاقانه دیگری نیز برای به تاخیر انداختن زمان پاسخگویی سرور وجود دارد.
مطالب این بخش توسط سرکار خانم فهیمه رضایی تهیه شده است.