مهران سیفعلی‌نیا
مهران سیفعلی‌نیا
خواندن ۵ دقیقه·۲ سال پیش

درک بهتر آسیب‌پذیری LDAP Injection و چند مثال از آن

اِل دَپ که یک پروتکل برای دسترسی و مدیریت سرویس‌های مربوط به دایرکتوری‌ها هست، و به انگلیسی LDAP نوشته میشه، مخفف Lightweight Directory Access Protocol هست. (چه چیزا!) از این پروتکل معمولا برای سازماندهی احراز هویت و سطح دسترسی کاربران یک سازمان استفاده میشه. خب طبیعیه که مثل بقیه فناوری‌های مربوط به وب، این یکی هم پر از آسیب‌پذیری‌های امنیتی باشه؛ که یکی از محبوب‌ترین‌هاش به تزریق LDAP (LDAP Injection) معروفه که قراره اینجا درباره اون صحبت کنیم.

تزریق LDAP چیه دیگه؟

تزریق LDAP از پیاده‌سازی‌های نادرست یا طراحی‌های نا ایمن در سرویس‌های LDAP بوجود میاد (البته زیادم مهم نیست بدونیم چرا بوجود میاد D: ) سرویس‌های LDAP برای ذخیره و سازماندهی داده‌ها در ساختار سلسه مراتبی درختی استفاده میشن مثلا نام‌کاربری، رمزعبور و اطلاعات ارتباطی و... حالا تزریق LDAP به هکر اجازه میده تا با اضافه کردن کدهای مخرب بتونه Queryهای LDAP رو دستکاری کنه و به اطلاعات غیرمجاز دسترسی پیدا کنه یا فرآیندهای احراز هویت رو دور بزنه.

ساخت پیلودهای LDAP Injection

خب خب خب... حرف زدن بسته، بریم سراغ چندتا مثال تا بتونیم مفاهیم رو بهتر درک کنیم. برای اینکه بتونید بهتر و سریع‌تر این آسیب‌پذیری رو پیدا کنید و ازش سوءاستفاده مفید (!) کنید، بهتره که ساختار LDAP رو مطالعه کنید و اون رو بشناسید، چیز زیاد پیچیده‌ای نیست؛ یکسری کوئری برای فیلتر کردن داده‌ها و دسترسی به اون‌هاست. مثلا کد زیر رو در نظر بگیرید که برای احراز هویت کاربر استفاده میشه: (هرجایی از این مقاله در خصوص کوئری‌های LDAP سوالی داشتید حتما کامنت کنید، خوشحال میشم بتونم کمک کنم.)

(&(uid=<نام کاربری>)(userPassword={CLEAR}<رمزعبور>))


خب این کوئری دنبال یه کاربر خاص می‌گرده که نام‌کاربری و رمزعبور خاصی داره. (همون احراز هویت یا فرآیند وروده)

حالا اینجا نفوذگر (هکر) می‌تونه بیاد و با تزریق کد زیر به جای <نام کاربری> ، فیلتر مربوطه رو دور بزنه:

*)(uid=*))(|(uid=*


در نهایت، کوئری که سمت سرور پردازش میشه میشه چیزی شبیه این:

(&(uid=*)(uid=*))(|(uid=*)(userPassword={CLEAR}<رمزعبور>))


خب این کد داره بررسی می‌کنه که اگر نام‌کاربری هرچی بود! نتیجه رو به ما برگردونه. در ادامه هم برای اینکه قسمت رمزعبور ما رو اذیت نکنه، یه شرط OR بهش اضافه کرده؛ خلاصه کوئری داره میگه که

اگر نام‌کاربری هرچیزی بود و همچنین نام‌کاربری هرچیزی بود!!! یا اگر نام‌کاربری هرچیزی بود یا رمزعبور هم برابر بود با یه‌چیزی، بذار ما وارد حساب بشیم! (اگه نفهمیدی چی شد، احتمالا باید با درباره‌ی اوپراتورهای شرطی مطالعه کنی)

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

1- دریافت اطلاعات تمامی کاربران:

در نظر بگیرید که یه برنامه‌ای داره از LDAP برای جستجو کردن کاربری استفاده می‌کنه که ایمیل خاصی داره (بخش جستجوی کاربران بر اساس ایمیل) خب کوئری اصلی چیزی شبیه به اینه:

(&(objectClass=person)(mail=<ایمیل>))


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

*)(objectClass=person)|


با تزریق این پیلود، کوئری که سمت سرور پردازش میشه، نهایتا میشه این:

(&(objectClass=person)(mail=*)(objectClass=person))(|)

که این کوئری شئ person رو برای تمامی ایمیل‌ها برمی‌گردونه.


2- دور زدن سطح دسترسی گروه‌های مختلف:

حالا فکر کنید یک برنامه‌ای داره چک می‌کنه که آیا یک کاربر، عضو گروه خاصی هست یا نه؟ (برای اینکه بتونه از قابلیت‌های اون گروه استفاده کنه:

(&(objectClass=group)(cn=<نام گروه>)(member=<نام‌کاربری>))


اینجا هکر می‌تونه با تزریق در <نام گروه>بیاد و این چک کردن رو دور بزنه و به قابلیت‌های تمامی گروه‌ها دسترسی پیدا کنه:

*)(objectClass=*)(


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

(&(objectClass=group)(cn=*)(objectClass=*)(member=<نام‌کاربری>))

خب همونطور که مشخصه این کوئری کاربر مربوطه رو بدون در نظر گرفتن گروه و کلاسش، برمیگردونه.


3- ارتقاء سطح دسترسی:

یه برنامه دیگه رو در نظر بگیرید که با کوئری LDAP میاد و چک میکنه که آیا یک کاربر مدیره یا نه:

(&(objectClass=person)(uid=<نام‌کاربری>)(isAdmin=TRUE))


هکر می‌تونه با تزریق پیلود زیر به‌جای <نام‌کاربری>، سطح دسترسی خودش رو به ادمین ارتقاء بده:

*)(isAdmin=*)(


کوئری نهایی که به سرور ارسال میشه اینه:

(&(objectClass=person)(uid=*)(isAdmin=*)(isAdmin=TRUE))

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


راه‌های جلوگیری:

برای اینکه در برابر حملات تزریق LDAP ایمن باشیم، باید یکسری نکات رو رعایت کنیم:

1- اعتبارسنجی و بهینه‌سازی ورودی‌های کاربر: باید قوانین سختگیرانه‌ای برای اعتبارسنجی ورودی‌های کاربر پیاده‌سازی بشه، مثلا یک لیست سفید برای کاراکترهای مجاز تعریف بشه و کاراکترهای خطرناکی مثل * یا (& باید بهینه بشن یا اسکیپ بشن تا جلوی حملات تزریق LDAP گرفته بشه.

2- استفاده از کوئری‌های پارامترایز شده: درست مثل تزریق SQL، برای جلوگیری از تزریق LDAP یکی از راه‌های خیلی مفید می‌تونه استفاده از ساختار پارامترایز شده برای کوئری‌ها باشه. استفاده از ساختار پارامترایز، باعث میشه تا ورودی کاربر از کوئری اصلی جدا بشه و مطمئن میشه که ورودی کاربر به‌عنوان داده در نظر گرفته میشه نه به‌عنوان بخشی از کوئری. (برای اطلاعات بیشتر می‌تونید Parameterized Query رو مطالعه کنید)

3- استفاده از کتابخانه‌های امن برای LDAP: بعضی از کتابخانه‌هایی که برای LDAP استفاده میشن خودشون به‌صورت ایمن نوشته شدن و جلوی حملات تزریق رو می‌گین. مثلا کتابخونه System.DirectoryServices.Protocols در .NET یک نسخه امن از کتابخانه LDAP هست که جلوی خیلی از حملات رو می‌گیره.

4- حداقل دسترسی برای حساب‌های سرویس‌های LDAP: حساب‌هایی که سرویس‌های LDAP روی اون‌ها اجرا میشه، باید حداقل مجوزها و دسترسی‌ها رو داشته باشن تا جلوی حملات (خدایی نکرده) مخرب گرفته بشه و اگر حمله‌ای شد، هکر نتونه کار خاصی انجام بده.

5- ارسال این مقاله برای برنامه‌نویس‌ها و ادمین‌های سرویس‌های LDAP :)))


منبع اصلی: اینفوسک رایتاپس



ldapآسیب‌پذیریامنیت
زمانی که جوون بودم، کارشناس آزمون نفوذ و توسعه دهنده اسناد امنیتی بودم؛ الان که پیر شدم خستم، فقط می‌خوابم!
شاید از این پست‌ها خوشتان بیاید