
بررسی SQL Injection
در این بخش از دوره آموزشی OWASP-WSTG به هفتمین بخش از استاندارد WSTG با شناسه WSTG-INPV-05 می پردازیم که مربوط به بررسی SQL Injection می باشد. لازم به ذکر است با توجه به طولانی بودن مبحث تزریق SQL، این بخش از استاندارد OWASP در چندین قسمت قرار داده می شود.
Fingerprinting the Database
حتی اگر زبان SQL یک استاندارد باشد، هر DBMS ویژگیهای خاص خود را دارد و در بسیاری از جنبهها مانند دستورهای خاص، توابع بازیابی دادههایی مانند نام کاربران و پایگاهداده، ویژگیها، کامنتها و غیره با یکدیگر تفاوت دارند.
هنگامی که تست نفوذگر به سمت بهرهبرداری از SQL Injection پیشرفتهتر حرکت میکنند، باید بدانند که نوع پایگاهداده استفاده شده در برنامه چیست.
Errors Returned by the Application
اولین راه برای فهمیدن اینکه چه پایگاهداده ای توسط برنامه مورد استفاده قرار گرفته است، مشاهده خطای بازگشتی توسط برنامه است. در زیر چند نمونه از پیغامهای خطا آورده شدهاست:
MySql:

استفاده از عبارت Union Select به همراه version() نیز میتواند به شناخت پایگاهداده مورد استفاده، کمک کند.

Oracle:

MSSQL Server:

PostgreSQL:

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

Exploitation Techniques
Union Exploitation Technique
عملگر Union در تزریق SQL استفاده میشود تا به یک پرس و جو، که عمدا توسط تست نفوذگر جعل شدهاست، به پرس و جوی اصلی متصل شود. نتیجه پرس و جوی جعلی به نتیجه پرس و جوی اصلی متصل خواهد شد که به آزمونگر اجازه میدهد مقادیر ستونهای جداول دیگر را به دست آورد. به عنوان مثال فرض کنید که پرس و جوی اجرا شده از سرور به شرح زیر است:

ما مقدار $id زیر را تعیین خواهیم کرد:

ما پرس و جوی زیر را خواهیم داشت:

که به نتیجه جستجوی اصلی با تمام ارقام کارت اعتباری در جدول CreditCardTable خواهد پیوست. برای دور زدن پرس و جوهایی که از کلمه کلیدی DISTINCT استفاده میکنند، کلمه کلیدی ALL لازم است. علاوه بر این، متوجه میشویم که فراتر از تعداد کارتهای اعتباری، دو مقدار دیگر را انتخاب کردهایم. این دو مقدار ضروری هستند زیرا این دو پرس و جو باید تعداد برابری از پارامترها / ستونها داشته باشند تا از خطای نحوی جلوگیری شود.
در ابتدا یک تست نفوذگر نیاز به بهرهبرداری از آسیبپذیری تزریق SQL با استفاده از چنین تکنیکی دارد تا تعداد صحیح ستونها را در عبارت SELECT پیدا کند.
برای رسیدن به این هدف، آزمونگر میتواند از دستور ORDER BYاستفاده کند و به دنبال آن عددی که نشاندهنده تعداد ستونهای پایگاهداده انتخابشده باشد، انتخاب شود:

اگر پرس و جو با موفقیت اجرا شود، آزمونگر میتواند فرض کند، در این مثال، ۱۰ ستون یا بیشتر در عبارت SELECT وجود دارد. اگر پرس و جو با شکست مواجه شود، باید کمتر از ۱۰ ستون توسط پرس و جو برگشت داده شود. اگر یک پیغام خطا در دسترس باشد، احتمالا به صورت زیر خواهد بود:

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

اگر پرس و جو با شکست مواجه شود، آزمونگر احتمالا پیامی مانند این را خواهد دید:

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

پس از جمعآوری موفق اطلاعات، بسته به برنامه کاربردی، ممکن است فقط اولین نتیجه را به آزمایشکننده نشان دهد، زیرا برنامه تنها خط اول مجموعه نتایج را بررسی میکند. در این مورد، ممکن است از یک عبارت LIMIT استفاده شود یا آزمایشکننده میتواند یک مقدار نامعتبر تنظیم کند و فقط درخواست دوم را معتبر کند.

Boolean Exploitation Technique
تکنیک بهرهبرداری مبتنی بر Boolean زمانی بسیار مفید است که تست نفوذگر یک تزریق SQL کور یا Blind SQL Injection را پیدا کند که در آن هیچ چیز در مورد نتیجه یک عملیات معلوم نیست. برای مثال، این رفتار در مواردی اتفاق میافتد که برنامهنویس یک صفحه خطای سفارشی ایجاد کردهاست که هیچ چیزی را در ساختار نامه پرس و جو یا پایگاهداده آشکار نمیکند. (صفحه یک خطای SQL را برنمیگرداند، ممکن است فقط یک HTTP ۵۰۰، ۴۰۴، یا redirect را بازگرداند).
با استفاده از روشهای استنباطی، میتوان از این مانع نیز عبود نموده و در نتیجه مقادیر را از پایگاه داده بازیابی نمود. این روش شامل انجام یک سری پرس و جوهای مبتنی بر Boolean در برابر سرور، مشاهده پاسخها و در نهایت استنتاج معنای این پاسخها است. مانند همیشه، وب سایت www.example.com را در نظر میگیریم و فرض میکنیم که صفحه ای از این وب سایت شامل یک پارامتر به نام id بوده که دارای آسیبپذیری تزریق SQL میباشد. این بدان معنی است که اجرای درخواست زیر یک پیام خطا را برای ما به همراه دارد که البته سفارشی شده است:

ما فرض میکنیم که پرس و جوی اجرا شده بر روی سرور به صورت زیر است:

که از طریق روشهایی که قبلا به آن اشاره شد قابل بهرهبرداری است. چیزی که ما میخواهیم به دست آوریم مقادیر فیلد نام کاربری است. آزمونهایی که ما اجرا خواهیم کرد به ما این امکان را میدهند که مقدار فیلد نام کاربری را به صورت کاراکتر به کاراکتر استخراج کنیم. این امر از طریق استفاده از برخی توابع استاندارد که عملا در هر پایگاهداده وجود دارند، امکان پذیر است. در این بخش برای مثالهای خود از شبه توابع زیر استفاده خواهیم کرد:
SUBSTRING (text, start, length): این تابع مقداری را گرفته و کاراکترهای آن با طول مشخص را باز میگرداند. در مثال زیر از این تابع استفاده شده است که بیانگر این است که آیا اولین کاراکتر از نام پایگاه داده برابر با a است یا خیر.
substr(databse(),1,1) =’a’
ASCII (char): مقدار اسکی کاراکتر ورودی را به ما میدهد. همانند مثال بالا، در مثال زیر تنها مقدار اصلی با مقدار ASCII آن جایگزین شده است تا امکان بررسی کاراکترها، ساده تر باشد:
ascii(substr(databse(),1,1))=100
LENGTH (text): تعداد کاراکترها را در متن ورودی برمی گرداند. در مثال زیر مشخص می شود که آیا طول نام پایگاه داده برابر 4 است یا خیر.
length(database())=4
از طریق چنین توابعی، ما آزمونهای خود را روی کاراکتر اول اجرا خواهیم کرد و زمانی که مقدار اول را کشف کردیم، به عدد مربوط به مقدار را به 2 تغییر می دهیم. و این کار را تا زمانی که کل مقدار مورد نظر را کشف کنیم، ادامه می دهیم. این آزمایشها با توجه به استفاده از توابع مذکور، امکان مقایسه عددی را برای ما فراهم مینماید تا با سرعت بیشتری به مقادیر اصلی دست پیدا کنیم. با توجه به استفاده از عبارت ASCII، می بایست نتایج با جدول ASCII مطابقت داده شود تا کاراکتر اصلی را شناسایی کنیم. به عنوان مثال، ما از مقدار زیر برای مقدار استفاده خواهیم کرد:

این امر پرس و جوی زیر را ایجاد میکند:

مثال قبلی نتیجه را در صورتی و تنها در صورتی باز میگرداند که اولین کاراکتر فیلد نام کاربری برابر با مقدار اسکی ۹۷ باشد. اگر یک مقدار غلط به دست آوریم، آنگاه شاخص جدول اسکی را از ۹۷ به ۹۸ افزایش میدهیم و درخواست را تکرار میکنیم. همچنین اگر مقدار اصلی کاراکتر را به دست آوریم، سپس می بایست به دنبال کاراکتر بعدی باشیم. به منظور اینکه که بفهمیم کدام پاسخ صحیح و کدام نادرست است، می بایست پرس و جویی ایجاد کنیم که همیشه غلط میباشد. این امر با استفاده از مقدار زیر برای id امکان پذیر است:

که پرس و جوی زیر را ایجاد میکند:

پاسخ بهدستآمده از سرور (که کد HTML است) مقدار نادرست برای آزمونهای ما خواهد بود. این برای بررسی اینکه آیا مقدار بهدستآمده از اجرای پرس و جوی استنباطی برابر با مقدار بهدستآمده از آزمون اجرا شده قبل است یا خیر، کافی است. گاهی اوقات، این روش جواب نمیدهد. اگر سرور دو صفحه متفاوت را در نتیجه دو درخواست وب متوالی یکسان برگرداند، ما قادر به تشخیص مقدار درست از مقدار غلط نخواهیم بود. در این موارد خاص، استفاده از فیلترهای خاصی که به ما اجازه حذف کدی که بین دو درخواست تغییر میکند و به دست آوردن الگو را میدهد، ضروری است. سپس، برای هر درخواست استنباطی اجرا شده، الگوی نسبی را از پاسخ با استفاده از همان تابع استخراج میکنیم و یک کنترل بین دو الگو را به منظور تصمیمگیری در مورد نتیجه آزمون انجام میدهیم.
در بحث قبلی، ما به مساله تعیین شرایط خاتمه برای آزمونهای بیرونی، یعنی زمانی که باید به روش استنباطی پایان دهیم، رسیدگی نکرده ایم. یک تکنیک برای انجام این کار، استفاده از ویژگیهای تابع SUBSTRING و تابع LENGTH میباشد.
در این بخش مقدار زیر را برای فیلد id درج خواهیم کرد:

که در آن N تعداد کاراکترهایی است که تا کنون تجزیه و تحلیل کردهایم (بدون احتساب مقدار صفر) . در این صورت، پرس و جو به شکل زیر خواهد بود:

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