سرفصل‌های آموزشی
آموزش OWASP TOP 10
آسیب پذیری SQLi با دستور UNION

آسیب پذیری SQLi با دستور UNION

در این مقاله، به معرفی دستور 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