NoSQL injection
زمان مطالعه :5 دقیقه
Exploiting syntax injection to extract data, Exploiting NoSQL operator injection to extract data &...
Exploiting syntax injection to extract data
NoSQL injection | Web Security Academy (portswigger.net)
در بسیاری از پایگاههای داده NoSQL، برخی از توابع یا query operators میتوانند کد محدود JavaScript را اجرا کنند، مانند عملگر $where و تابع mapReduce() در MongoDB. این به این معناست که اگر یک برنامه آسیبپذیر از این operators یا توابع استفاده کند، database (پایگاه داده) ممکن است JavaScript را به عنوان بخشی از Query ارزیابی کند. بنابراین، ممکن است بتوانید از توابع JavaScript برای استخراج دادهها از database (پایگاه داده) استفاده کنید.
Exfiltrating data in MongoDB
فرض کنید یک برنامه آسیبپذیر به کاربران اجازه میدهد تا نام کاربریهای دیگر کاربران ثبتشده را جستجو کنند و نقش آنها را نمایش دهد. این عملیات یک درخواست به URL زیر ارسال میکند:
https://insecure-website.com/user/lookup?username=admin
این منجر به NoSQL queryزیر در مجموعه users میشود:
{"$where":"this.username == 'admin'"}
از آنجایی که Query از عملگر $where استفاده میکند، میتوانید تلاش کنید تا توابع JavaScript را به این Query تزریق کنید تا دادههای حساس را برگرداند. برای مثال، میتوانید payload زیر را ارسال کنید:
admin' && this.password[0] == 'a' || 'a'=='b
این Query اولین کاراکتر password string کاربر را برمیگرداند، که به شما امکان میدهد رمز عبور را کاراکتر به کاراکتر استخراج کنید.
همچنین میتوانید از تابع JavaScript match() برای استخراج اطلاعات استفاده کنید. به عنوان مثال، payload زیر به شما امکان میدهد تا شناسایی کنید که آیا رمز عبور شامل ارقام است یا خیر:
admin' && this.password.match(/\d/) || 'a'=='b
Identifying field names
از آنجا که MongoDB دادههای semi-structured (نیمهساختار یافته) ای را مدیریت میکند که نیازی به یک schema ثابت ندارد، ممکن است نیاز داشته باشید تا فیلدهای معتبر در مجموعه را شناسایی کنید قبل از اینکه بتوانید با استفاده از JavaScript injection دادهها را استخراج کنید.
برای مثال، برای شناسایی اینکه آیا پایگاه داده MongoDB شامل یک فیلد password است یا خیر، میتوانید payload زیر را ارسال کنید:
https://insecure-website.com/user/lookup?username=admin'+%26%26+this.password!%3d'
Payload را مجدداً برای یک فیلد موجود و یک فیلد غیرموجود ارسال کنید. در این مثال، شما میدانید که فیلد username وجود دارد، بنابراین میتوانید payloadهای زیر را ارسال کنید:
admin' && this.username!='
admin' && this.foo!='
اگر فیلد password وجود داشته باشد، انتظار دارید که پاسخ مشابه پاسخ برای فیلد موجود (username) باشد، اما متفاوت از پاسخ برای فیلد غیرموجود (foo) باشد.
اگر بخواهید نام فیلدهای مختلف را آزمایش کنید، میتوانید یک dictionary attack انجام دهید، با استفاده از یک wordlist برای چرخیدن در میان نامهای مختلف فیلدهای احتمالی.
Note
همچنین میتوانید از NoSQL operator injection برای استخراج نام فیلدهای کاراکتر به کاراکتر استفاده کنید. این به شما امکان می دهد تا بدون نیاز به حدس زدن یا انجام dictionary attack ، نام فیلدها را شناسایی کنید. در قسمت بعدی نحوه انجام این کار را به شما آموزش خواهیم داد.
Exploiting NoSQL operator injection to extract data
حتی اگر query اصلی از هیچ یک از operatorهایی که به شما امکان اجرای JavaScript دلخواه را میدهند استفاده نکند، ممکن است بتوانید یکی از این operatorها را خودتان تزریق کنید. سپس میتوانید از شرایط بولی (boolean conditions) برای تعیین اینکه آیا برنامه هر JavaScriptی که از طریق این عملگر تزریق میکنید را اجرا میکند، استفاده کنید.
Injecting operators in MongoDB
یک برنامه آسیبپذیر را در نظر بگیرید که نام کاربری و رمز عبور را در بدنه یک POST
request میپذیرد:
{"username":"wiener","password":"peter"}
برای تست اینکه آیا میتوانید operatorها را تزریق کنید، میتوانید عملگر $where را به عنوان یک پارامتر اضافی اضافه کرده و سپس یک درخواست را ارسال کنید که شرط آن به false ارزیابی شود، و درخواست دیگری که به true ارزیابی شود. برای مثال:
{"username":"wiener","password":"peter", "$where":"0"}
{"username":"wiener","password":"peter", "$where":"1"}
اگر تفاوتی بین پاسخها وجود داشته باشد، این ممکن است نشان دهد که عبارت JavaScript درعبارت $whereارزیابی میشود.
Extracting field names
اگر شما یک operator را تزریق کردهاید که به شما امکان اجرای JavaScript را میدهد، ممکن است بتوانید از متد keys() برای استخراج نام فیلدهای داده (name of data fields)استفاده کنید. برای مثال، میتوانید payload زیر را ارسال کنید:
"$where":"Object.keys(this)[0].match('^.{0}a.*')"
این اولین فیلد داده (data field)در user objectرا بررسی کرده و اولین کاراکتر field name را برمیگرداند. این به شما امکان میدهد که field name را کاراکتر به کاراکتر استخراج کنید.
Exfiltrating data using operators
در عوض، ممکن است بتوانید از operatorهایی که به شما امکان اجرای JavaScript را نمیدهند برای استخراج داده استفاده کنید. برای مثال، ممکن است بتوانید از اپراتور $regex برای استخراج داده کاراکتر به کاراکتر استفاده کنید.
یک برنامه آسیبپذیر را در نظر بگیرید که نام کاربری و رمز عبور را در بدنه یک POST request میپذیرد. برای مثال:
{"username":"myuser","password":"mypass"}
میتوانید با تست اینکه آیا request $regex پردازش میشود، شروع کنید:
{"username":"admin","password":{"$regex":"^.*"}}
اگر response به این request متفاوت از requestی باشد که هنگام ارسال رمز عبور نادرست دریافت میکنید، این نشان میدهد که برنامه ممکن است آسیبپذیر باشد. میتوانید از اپراتور $regex برای استخراج داده(extract data) کاراکتر به کاراکتر استفاده کنید. برای مثال، payload زیر بررسی میکند که آیا رمز عبور با a شروع میشود:
{"username":"admin","password":{"$regex":"^a*"}}
Timing based injection
گاهی اوقات ایجاد یک خطای database باعث تفاوت در response برنامه نمیشود. در این شرایط، ممکن است هنوز بتوانید با استفاده از تزریق JavaScript و ایجاد یک تأخیر زمانی شرطی (conditional time delay)، آسیب پذیری را تشخیص داده و Exploit کنید.
برای انجام timing-based NoSQL injection :
1- صفحه را چندین بار بارگذاری کنید تا زمان بارگذاری پایه (baseline loading time)تعیین شود.
2- یک timing based payload را در ورودی قرار دهید. یک timing based payload در هنگام اجرا، باعث تأخیر عمدی در پاسخ میشود. برای مثال،
{"$where": "sleep(5000)"} در صورت تزریق موفق، یک تأخیر عمدی 5000 میلیثانیهای ایجاد میکند.
3- تعیین کنید که آیا پاسخ کندتر بارگذاری میشود. این نشاندهنده تزریق موفق است.
timing based payload های زیر در صورت شروع شدن رمز عبور با حرف a، یک تأخیر زمانی را ایجاد میکنند:
admin'+function(x){var waitTill = new Date(new Date().getTime() + 5000);while((x.password[0]==="a") && waitTill > new Date()){};}(this)+'
admin'+function(x){if(x.password[0]==="a"){sleep(5000)};}(this)+'
Preventing NoSQL injection
روش مناسب برای جلوگیری از حملات NoSQL injection به تکنولوژی خاص NoSQL مورد استفاده بستگی دارد. به همین دلیل، توصیه میکنیم مستندات امنیتی
(security documentation)مربوط به NoSQL database انتخابی خود را مطالعه کنید. با این حال، راهنماییهای کلی زیر نیز کمک خواهد کرد:
· ورودی کاربر را Sanitized و اعتبارسنجی کنید، با استفاده از یک allowlist از characters پذیرفتهشده.
· ورودی کاربر را با استفاده از parameterized queries به جای ترکیب مستقیم ورودی کاربر در query وارد کنید.
· برای جلوگیری از operator injection، یک allowlist از keys پذیرفتهشده اعمال کنید.
فادیا مرادنژاد
مشاهده مقاله های بیشتر