در این بخش از دوره آموزشی SEC542 از موسسه SANS با ادامه مبحث حمله SQL Injection با شما هستیم و جزئیات بیشتری از این حمله را مطرح می نماییم.
SQL Injection Example: Code
در این بخش یک حمله SQL injection ساده را بررسی میکنیم. در اولین قدم، میخواهیم ببینیم کد سمت سروری که کوئریهای SQL را بصورت داینامیک تولید میکند به چه شکل است.
$sql = “
SELECT *
FROM Users
WHERE lname=’$_GET[“name”]’
“;
قسمتهای مهم کد بالا عبارتند از:
SELECT – کوئری SELECT برای بازیابی دیتا از جدول استفاده میشود.
*- نشان میدهد که تمامی ستونهای جدول باید برگردانده شوند.
FROM Users – جدول Users،جدول مورد نظر ما میباشد.
WHERE lname= – اعمال شرط بر روی ستون lname جهت فیلتر کردن دیتای بازگشتی
‘$_GET[“name”]’ – این قطعه داخل علامت کوتیشن گذاشته شده چراکه انتظار میرود خروجی آن یک رشته باشد. رشته مورد نظر از روی پارامتر name موجود در URL تولید میگردد.
; – این علامت، جمله را کامل میکند.
SQL Injection Example: Normal Input/Query
با ورود یک مقدار صحیح (نرمال)، کوئری به چه شکل در میآید؟ با هم بررسی میکنیم. در این جا نام Dent به عنوان مقدار پارامتر name به URL ارسال میشود.
Normal Input: Dent
URL:
http: //sec542.org/sqli.php?name=Dent
SQL Query:
SELECT *
FROM Users
WHERE lname=’Dent’;
همه چیز درست به نظر میرسد و انتظار داریم یک نتیجه نرمال به ازای ورود رشته Dent دریافت کنیم.
SQL Injection Example: Injected Input/Query
اگر یک کاراکتر کوتیشن به انتها اضافه کنیم، آیا در کوئری تغییری ایجاد میشود؟
Injected Input: Dent’
URL:
http: //sec542.org/sqli.php?name=Dent’
SQL Query:
SELECT *
FROM Users
WHERE lname=’Dent”;
افزودن همین یک کاراکتر، موجب میشود تا اجرای کوئری با خطا مواجه شده و آن خطا به ما نشان داده شود.
SQL Injection Example: Injected Input 2/Query 2
در ورودی بعدی، کاراکترهای ; — را به مقدار قبلی Dent’ اضافه می کنیم.
Injected Input: Dent’; —
URL:
http: //sec542.org/sqli.php?name=Dent’; —
SQL Query:
SELECT *
FROM Users
WHERE lname=’Dent’; –‘;
این ورودی، یک نام مجاز ارائه داده، سپس رشته را با کوتیشن بسته و در نهایت با استفاده از علامت “;” و کامنت کردن ادامه جمله، دستور را خاتمه داده است. بنابراین خروجی دستور SQL بالا، نتیجه نرمال به ازای ورود رشته Dent است.
‘ or 1=1; —
احتمالا شناختهشدهترین پیلود SQLi ، عبارت “’ or 1=1;– ” میباشد. اما دلیل این محبوبیت چیست؟
بیایید بصورت تکه تکه بررسی کنیم:
‘ – کوتیشن، هر رشته را میبندد.
Or 1=1 – این عبارت منطق اجرای کوئری را تغییر میدهد.
; — – این کاراکترها در انتهای پیلود، کوئری SQL را کامل نموده و بقیه کد را که موجب بروز خطا میشود، کامنت میکند.
نکته: برخی از RDBMS ها، بعد از کامنت (–) به یک space احتیاج دارند.
SQL Injection Example: ‘ or 1=1; — Injected
با قرار دادن پیلود محبوبمان در پارامتر name، کوئری چه تغییری میکند؟
Injected Input: ‘ or 1=1; —
URL:
http://sec542.org/sqli.php?name=’ or 1=1; —
SQL Query:
SELECT *
FROM Users
WHERE lname=” or 1=1; — ‘;
به زبان ساده، injection رشته را بسته، یک عبارت OR TRUE به کوئری افزوده، کوئری را تمام کرده و همه چیز را با یک کامنت خاتمه داده است. بنابراین خروجی دستور بالا، کل رکوردهای جدول Users خواهد بود.
حرکت متوازن کننده SQLi
در کشف و اکسپلویت آسیبپذیریهای مربوط به ورودی، باید با توجه به کد موجود، به دنبال پسوندها، پیشوندها و پیلودهای مناسب باشیم تا نتیجه دلخواه خود را دریافت کنیم.
حمله SQL injection نیز به همین صورت است. ما باید اطمینان حاصل کنیم که کد inject شده ما، به درستی اجرا میشود تا بتوانیم به نتیجه مطلوب برسیم.
اگرچه هدف ما در ابتدا، دریافت خطا از اپلیکیشن است اما در نهایت باید بتوانیم این خطاها را پشت سر گذاشته و ضعف موجود را اکسپلویت کنیم. آشکارترین قسمت در پیدا کردن پیلود مناسب و متوازن ساختن کوئری SQL، کوتیشنهایی هستند که به همراه رشتههای دیتا استفاده میشوند.
موازنه کوتیشنها
در SQLi ، رایجترین مکانی که ورودی ما در آن مینشیند، در یک رشته محدود شده به کوتیشن است. به همین دلیل است که تنها با افزودن یک کاراکتر کوتیشن، خطای نحوی (syntax error) در دیتابیس ایجاد میشود. با علم به این قضیه، باید به دنبال یافتن پیشوند و پسوند مناسبی باشیم که موجب ایجاد یک کوئری معنادار و موثر شود و تولید خطا نکند.
ورودیهای زیر و کوئریهای حاصل از آنها به ما کمک میکند تا نحوه کوتیشنگذاری مناسب با توجه به پیشوندها و پسوندها را بهتر درک کنیم.
مثال با استفاده از کامنت: Dent’;–
SELECT … WHERE lname=’Dent’;– ‘;
مثال بدون کامنت: Dent’ OR ‘a’=’a
SELECT … WHERE lname=’Dent’ OR ‘a’=’a’;
متوازن ساختن شماره ستونها
اگرچه به نظر میرسد که کوتیشنگذاری، واضحترین موازنهای است که باید انجام شود، اما جنبههای دیگری نیز وجود دارند که به همین میزان نیازمند توجه هستند.کوئریهای متعددی از SQL وجود دارند که در نقاط مختلف به ستونها اشاره میکنند. تعداد این ستونها در نقاط مختلف باید با هم برابر باشد.
این رفتار هم در دستور INSERT و هم در دستور UNION مشاهده میشود. برای استفاده از این دستورات در SQLi، پیلود injection ما باید با توجه به تعداد ستونها نوشته شود. در غیراینصورت با خطاهای نحوی دیتابیس مواجه خواهیم شد. به مثالهای زیر دقت کنید:
INSERT INTO planets_tbl (name,planet,heads) VALUES (‘Zaphod’,’Betelgeuse’,2);
SELECT id, username, password FROM user1_tbl WHERE username=’zaphod’ UNION SELECT id2, username2, password2 FROM user2_tbl;
در کوئری بالا، در SELECT اول سه ستون (id, username, password) و در SELECT دوم که با UNION به SELECT اول اضافه شده نیز سه ستون (id2, username2,password2) دیده میشود. اگر تعداد ستونها در این SELECT ها برابر نبود، آنگاه دیتابیس خطای syntax برمیگرداند.
در بخشهای بعدی درس، نحوه به دست آوردن تعداد ستونهای حاضر را بررسی میکنیم.
موازنه نوع داده
موازنه ستونها در واقع چیزی بیشتر از تعداد ستونها است. باز هم میخواهیم در مورد دو دستور INSERT و UNION صحبت کنیم اما این بار به جای تعداد ستونها، به محتویات داخل آنها میپردازیم. بطور کلی بیان شده است که انواع دادهها باید یکسان باشند اما واقعیت اینست که انواع دادهها تنها باید با هم سازگار باشند (یا قابل تبدیل باشند).
گرچه ممکن است غیرقابل درک به نظر برسد اما اعداد و رشتهها معمولا با هم سازگار هستند. در عین حال که به دنبال یافتن تعداد ستونهای مورد نیاز خود در یک injection هستیم، تلاش خواهیم کرد راهی پیدا کنیم تا نگذاریم این محدودیت مانع ادامه کارمان شود.
مطالب این بخش توسط سرکار خانم فهیمه رضایی تهیه شده است.