WSTG-INPV-05 – بخش اول

بررسی SQL Injection

در این بخش از دوره آموزشی OWASP-WSTG به هفتمین بخش از استاندارد WSTG با شناسه WSTG-INPV-05 می پردازیم که مربوط به بررسی SQL Injection می باشد. لازم به ذکر است با توجه به طولانی بودن مبحث تزریق SQL، این بخش از استاندارد OWASP در چندین قسمت قرار داده می شود.

خلاصه

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

حمله تزریق SQL شامل درج یا “تزریق” یک پرس وجوی SQL کامل یا جزئی از طریق ورودی داده و یا از طریق کلاینت (‏مرورگر) ‏به برنامه کاربردی وب است.

حمله تزریق SQL موفق می‌تواند داده‌های حساس از پایگاه‌داده را بخواند، داده‌های پایگاه‌داده را اصلاح کند (‏insert/update/delete)‏، عملیات اجرایی را بر روی پایگاه‌داده اجرا کند (مانند خاموش‌کردن DBMS)‏، محتوای یک فایل موجود در سیستم فایل DBMS را بازیابی کند یا فایل‌ها را در سیستم فایل بنویسد و در برخی موارد، دستورها را به سیستم‌عامل صادر کند.

حملات تزریق SQL نوعی حمله تزریق است که در آن دستورهای SQL به ورودی سطح داده تزریق می‌شود تا اجرای دستورهای SQL از پیش تعریف‌شده را تحت‌تاثیر قرار دهند.

به طور کلی برنامه‌های کاربردی وب، Statement های SQL شامل ترکیب SQL نوشته شده توسط برنامه نویسان را با داده‌های فراهم‌شده توسط کاربر ترکیب می‌کند. به عنوان مثال:

در مثال بالا متغیر $id شامل داده‌های فراهم‌شده توسط کاربر است که در پرس وجوی SQLقرار می‌گیرد.

بر این اساس، کاربر می‌تواند ورودی تولید شده را دستکاری نموده و Statement های SQLخود را در ادامه پرس وجوی SQL اصلی قرار دهد. مثال زیر نشان دهنده دستکاری ورودی توسط کاربر است که وی به جای وارد نمودن شناسه مناسب، عبارت 10 or 1=1 را وارد می‌کند، که منطق عبارت SQL را تغییر می‌دهد.

حملات تزریق SQL را می‌توان به سه دسته زیر تقسیم کرد:

Inband: داده‌ها با استفاده از همان کانالی که برای تزریق کد SQL استفاده می‌شود، استخراج می‌شوند. این ساده‌ترین نوع حمله است که در آن داده‌های بازیابی شده به طور مستقیم در صفحه وب برنامه ارائه می‌شوند.

Out-of-band: داده‌ها با استفاده از یک کانال متفاوت بازیابی می‌شوند (‏به عنوان مثال، یک ایمیل با نتایج پرس و جو ایجاد شده و برای تست نفودگر فرستاده می‌شود)‏.

Inferential or Blind: هیچ انتقال واقعی داده وجود ندارد، اما تست نفوذگر قادر به بازسازی اطلاعات با ارسال درخواست‌های خاص و مشاهده رفتار حاصل از سرور DB است.

حمله تزریق SQL موفق، نیازمند این است که مهاجم یک پرس و جوی SQL صحیح از لحاظ Syntax ایجاد کند. اگر برنامه، پیام خطایی (ایجاد شده توسط یک پرس و جوی نادرست) را برگرداند، پس ممکن است برای یک مهاجم آسان‌تر باشد که منطق پرس و جوی اصلی را بازسازی کند و بنابراین درک کند که چگونه تزریق را به درستی انجام دهد. با این حال، اگر برنامه جزئیات خطا را پنهان کند، آنگاه تست نفوذگر باید قادر به مهندسی معکوس منطق جستجوی اصلی باشد.

در مورد تکنیک‌هایی برای بهره‌برداری از آسیب پذیری تزریق SQL پنج تکنیک مشترک وجود دارد. همچنین این تکنیک‌ها گاهی اوقات می‌توانند به صورت ترکیبی مورد استفاده قرار گیرند :

Union Operator: می‌تواند زمانی استفاده شود که آسیب پذیری تزریق SQL در یک عبارت SELECT رخ دهد، که ترکیب دو پرس و جو را در یک نتیجه یا مجموعه نتیجه ممکن می‌سازد.

Boolean: از شرط بولی برای بررسی اینکه آیا شرایط خاص درست هستند یا غلط استفاده می‌کند.

Error based: این تکنیک پایگاه‌داده را مجبور به ایجاد خطا می‌کند و اطلاعاتی را در اختیار مهاجم یا تست نفوذگر قرار می‌دهد تا با استفاده از آن فرآیند تزریق مناسبی داشته باشند.

Out-of-band: تکنیک مورد استفاده برای بازیابی داده‌ها با استفاده از یک کانال متفاوت است. (برای مثال، ایجاد یک اتصال HTTP برای ارسال نتایج به یک سرور وب دیگر).

Time delay: از دستورها پایگاه‌داده (‏برای مثال sleep) ‏برای به تاخیر انداختن پاسخ‌ها در پرس و جوهای شرطی استفاده می‌کند. زمانی مفید است که مهاجم هیچ گونه پاسخی (‏نتیجه، خروجی، یا خطا)‏ از برنامه نداشته باشد.

اهداف تست

• نقاط تزریق SQL را شناسایی کنید.
• شدت تزریق و سطح دسترسی که می‌توان از طریق آن به دست آورد را ارزیابی نمایید.

چگونه تست را انجام دهیم

Detection Techniques

اولین گام در این تست، درک زمانی است که برنامه با یک سرور DB به منظور دسترسی به برخی داده‌ها تعامل می‌کند. مثال‌های معمول از مواردی که یک برنامه باید با یک DB صحبت کند عبارتند از:

Authentication forms: هنگامی که احراز هویت با استفاده از یک فرم وب انجام می‌شود، احتمال این وجود دارد که Credential کاربر در برابر یک پایگاه‌داده که شامل تمام نام‌های کاربری و رمز عبور (‏یا هش‌های رمز عبور)‏ است، بررسی شوند.

Search engines: رشته ارسال‌شده توسط کاربر می‌تواند در یک پرس و جوی SQL استفاده شود که تمام سوابق مربوطه را از یک پایگاه‌داده استخراج می‌کند.

E-Commerce sites: محصولات و ویژگی‌های آن‌ها (‏قیمت، توصیف، در دسترس بودن و غیره) ‏به احتمال زیاد در یک پایگاه‌داده ذخیره می‌شوند.

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

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

همچنین دستوارت مربوط به Comment (‏مانند — یا /**/)‏ و دیگر کلمات کلیدی SQL مانند AND و OR می‌توانند به منظور تغییر پرس و جو استفاده شوند.

یک تکنیک بسیار ساده اما گاهی اوقات موثر، قرار دادن یک رشته در جایی که انتظار ورود یک عدد می‌رود است، به طوری که خطایی مانند خطای زیر را ایجاد نماید:

همه پاسخ‌های سرور وب را بررسی کنید و نگاهی به سورس کدهای HTML/JavaScript بیاندازید. گاهی اوقات خطا در داخل آن‌ها وجود دارد اما بنا به دلایلی (برای مثال خطای JavaScript، کامنت‌های HTML و غیره) به کاربر نمایش داده نمی‌شود.

یک پیام خطای کامل، مانند موارد موجود در مثال‌ها، اطلاعات زیادی را برای تست نفوذگر فراهم می‌کند تا یک حمله تزریق موفق را انجام دهد. با این حال، برنامه‌ها اغلب جزئیات خطا را ارائه نداده و تنها یک “خطای ۵۰۰ سرور” ساده یا یک صفحه خطای سفارشی را به ما نمایش می‌دهند. در این حالت ما باید از تکنیک‌های تزریق کور استفاده کنیم. در هر صورت، بسیار مهم است که هر بخش را به طور جداگانه بررسی کنیم:

توجه داشته باشید که تنها یک متغیر باید تغییر کند در حالی که بقیه ثابت باقی می‌مانند تا درک دقیقی از اینکه کدام پارامترها آسیب‌پذیر هستند و کدام نیستند، داشته باشیم.

Standard SQL Injection Testing

Classic SQL Injection

پرس و جوی SQL زیر را در نظر بگیرید:

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

پرس و جو به این صورت خواهد بود:

اگر فرض کنیم که مقادیر پارامترها از طریق متد GET به سرور فرستاده می‌شوند و اگر دامنه وب سایت آسیب‌پذیر www.example.com باشد، درخواستی که ما انجام خواهیم داد این خواهد بود:

پس از یک تجزیه و تحلیل کوتاه متوجه می‌شویم که پرس و جو یک مقدار (‏یا مجموعه‌ای از مقادیر)‏ را بر می‌گرداند زیرا شرایط همیشه درست است (OR 1=1)‏. به این ترتیب سیستم بدون دانستن نام کاربری و رمز عبور کاربر را تصدیق کرده‌است.
در برخی سیستم‌ها اولین ردیف جدول کاربر یک کاربر admin خواهد بود و در این حالت ممکن است در برخی موارد با دستور ساختار بالا، با این کاربر لاگین شویم.

مثال دیگری از پرس و جو به شرح زیر است:

در این حالت دو مشکل وجود دارد، یکی به دلیل استفاده از پرانتز و دیگری به دلیل استفاده از تابع مجموعMD5 است. اول از همه، مشکل پرانتز را حل می‌کنیم. این امر به سادگی شامل اضافه کردن تعدادی از پرانتزهای بسته است تا زمانی که یک پرس و جوی تصحیح شده به دست آوریم.

برای حل مشکل دوم، سعی می‌کنیم آن را Bypass کنیم. ما به پرس و جوی خود یک نماد کامنت اضافه می‌کنیم که به این معنی است که یک کامنت در حال شروع شدن است. به این ترتیب، هر چیزی که پس از این نماد قرار می‌گیرد، یک کامنت در نظر گرفته می‌شود. هر DBMS دستورات خاص خود را برای کامنت گذاری دارد، با این حال، یک نماد مشترک برای اکثریت بیشتر پایگاه‌های داده، /**/ است. در اوراکل این نماد – – است. با این حال، مقادیری که ما به عنوان نام کاربری و گذرواژه استفاده خواهیم کرد عبارتند از:

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

(با توجه به قرار گرفتن یک کامنت در مقدار $username، قسمت رمز عبور پرس و جو نادیده گرفته خواهد شد.)

درخواست در URL به شرح زیر خواهد بود:

این ممکن است تعدادی از مقادیر را برگرداند. گاهی اوقات، کد احراز هویت تایید می‌کند که تعداد رکوردها / نتایج بازگردانده شده دقیقا برابر با ۱ است. به منظور دور زدن این مشکل، قرار دادن یک دستور SQL که شرطی را تحمیل می‌کند که تعداد نتایج بازگشتی باید یکی باشد، کافی است. ‏به منظور رسیدن به این هدف، ما از LIMIT استفاده می‌کنیم، که در آن تعداد نتایج / رکوردهایی است که می‌خواهیم برگردانیم. با توجه به مثال قبلی، مقدار فیلدها نام کاربری و گذرواژه به صورت زیر تغییر خواهد کرد:

به این ترتیب، ما درخواستی مانند موارد زیر را ایجاد می‌کنیم:

SELECT Statement

پرس و جوی SQL زیر را در نظر بگیرید:

همچنین درخواست یک اسکریپت را در نظر بگیرید که پرس و جو را در بالا اجرا می‌کند:

زمانی که تست نفوذگر مقدار معتبری را امتحان می‌کند (‏برای مثال ۱۰ در این مورد) ‏، برنامه توصیفی از یک محصول را بر می‌گرداند. یک راه خوب برای آزمایش این که آیا برنامه در این سناریو آسیب‌پذیر است یا خیر، بازی با منطق برنامه با استفاده از اپراتورهای AND و OR است.

درخواست زیر را در نظر بگیرید:

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

Stacked Queries

بسته به API هایی که برنامه وب از آن‌ها استفاده می‌کند و DBMS (به عنوان مثال PHP + PostgreSQL، ASP + SQL Server) ممکن است اجرای چند پرس و جو در یک فراخوانی امکان پذیر باشد.

پرس و جوی SQL زیر را در نظر بگیرید:

یک راه برای بهره‌برداری از سناریوی بالا این است:

این روش می‌تواند بسیاری از پرس و جوها را در یک ردیف و مستقل از اولین پرس و جوها اجرا کند.

پایان بخش اول

درباره نویسنده: احسان نیک آور

ممکن است دوست داشته باشید