نگاره‌هایی پیرامون امنیت، شبکه و رمزنگاری

28 سپتامبر 2020

توی این پست قصد دارم راجع به یک آسیب‌پذیری که با علی پیدا کردیم براتون بگم. با اکسپلویت آسیب‌پذیری که در پیاده‌سازی نادرست SSO بود، ‌تصاحب هر حساب کاربری تنها با یک کلیک قابل انجام بود. Single Sign-on یک مدل احراز هویت است که در آن کاربران یک سازمان با یک شناسه عملیات ورود رو فقط یک بار انجام میدن و از اون به بعد در سرویس‌های اون سازمان لاگین هستند. برای مثال شرکت گوگل رو در نظر بگیرید، کاربر با وارد شدن به حساب گوگل، می‌تواند به سرویس‌های متعددی از این شرکت دسترسی پیدا کند. مهم‌ترین چالش SSO، وجود دامین‌های متفاوت است. به‌همین منظور یک فاز انتقال احراز هویت باید وجود داشته باشد که اگه موارد امنیتی رعایت نشده باشد، می‌تواند منجر به ایجاد آسیب‌پذیری شود – چطوری من می‌تونستم هر حساب ویرگول رو هک کنم؟

وب‌سایت‌های Kolesa (یک شرکت قزاقستانی که برنامه بانتی داره و در ازا کشف آسیب‌پذیری، جایزه پرداخت می‌کنه) از مدل SSO برای وب‌سایت‌های خود استفاده می‌کند. سامانه‌های این شرکت برای انتقال احراز هویت از JSONP استفاده می‌کنند. و اما JSONP چیست؟

JSONP یک روش برای ارسال ابجکت JSON به یک دامین دیگه است.

ولی چرا این روش توی سازمان‌هایی که سامانه‌های متعددی دارند طرفدار داره؟ بخاطر دور زدن سازوکار SOP در مرورگرها. قابلیت‌های JSONP عبارت‌است از:

  • قابلیت بارگزاری یک ابجکت JavaScript
  • از XMLHttpRequest استفاده نمی‌کند
  • امنیت بسیار کمتر
  • دور زدن سازوکار SOP

سازوکار Single Sign-On

بر اساس نتایج فاز شناخت مشخص شد که سرور احراز هویت سازمان در آدرس زیر قراد دارد:

https://id.kolesa.kz

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

  • https://market.kz
  • https://krisha.kz
  • https://kolesa.kz

جریان کاری عمومی مدل احراز هویت SSO به شکل زیر است:

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

JSONP برای انتقال توکِن در SSO

در ‌Kolesa،‌ از JSONP برای انتقال توکِن برای انتقال احراز هویت‌های بین دامین‌های مختلف استفاده میشد. اگه کاربر در یکی از سه سایت بالا لاگین می‌بود، با یک درخواست JSONP عملیات احراز هویتش انجام می‌شد. دلیل استفاده از JSONP راحتی در پیاده‌سازی بوده است. در صوتی که Kolesa از JSONP استفاده نمی‌کرد، به دلیل اینکه دامین‌های این سازمان متفاوت است، باید CORS را برای انتقال توکِن احراز هویت استفاده می‌کرد که پیاده‌سازیش از JSONP دشوارتر است (البته میتونه مثل oAuth خود کاربر رو یک دور منتقل کنه به وب‌سایت لاگین شده و با توکن برگرده به سایتی که میخواد لاگین بشه، ولی این عملیات از رابط تجربه کاربری خوشایند نیست و تجربه خوبی برای کاربر نداره). جریان کاری انتقال توکِن در Kolesa به شرح زیر بود:

طبق جریان کاری بالا، وقتی که کاربری بخواهد در kolesa.kz احراز هویت کند، به سایت id.kolesa.kz منتقل می‌شود. بعد از وارد کردن اطلاعات ورود، کوکی ccid به ایشان اختصاص می‌یابد (کوکی احراز هویت). بلافاصله بعد از ورود، به همراه توکِن احراز هویت به سایت kolesa.kz منتقل می‌شود. در صورت درست بودن این توکِن، کوکی ssid (کوکی احراز هویت) به کاربر تخصیص می‌باید. شایان ذکر است که توکِن در یک کانال مخفی و امن میان دو سرور بررسی می‌گردد. بدین صورت کاربری که تا کنون در هیچ یک از سایت‌های Kolesa احراز هویت نشده، چنین مسیری را برای اولین بار طی می‌کند و نتیجه به‌وجود آمدن دو کوکی احراز هویت است. تا اینجا مشکل امنیتی وجود ندارد. مشکل زمانی شروع می‌شود که همان کابر بخواهد در ساتی دیگر (در اینجا krista.kz) از این سازمان ورود کند. در این مرحله، درخواست JSONP از طرف کاربر ارسال می‌شود. همان‌طور که در تصویر بالا مشخص است، درخواست به id.kolesa.kz ارسال می‌گردد. این درخواست توسط تَگ script ساخته می‌شود یعنی کوکی‌های احراز هویت به‌صورت خودکار توسط مرورگر نیز ارسال می‌گردد. در جواب JSONP درصورتی که کوکی‌ها معتبر باشد توکن احراز هویت وجود دارد. این توکن به توسط JavaScript به سایت krisha.kz ارسال می‌گردد و درصورت درست بودن توکِن (توسط یک کانال مخفی با id.kolesa.kz بررسی می‌شود)،‌ کوکی احراز هویت به کاربر داده می‌شود. این سازوکار در بسیاری از شرکت‌های بزگر دنیا وجود دارد (من می‌تونم یاهو و علی‌بابا چینی رو مثال بزنم). همان‌طور که میبینید، JSONP تنها SOP را دور می‌زند. در تصویر زیر، دلیل درخواست JSONP که در سایت krisha.kz هست را مشاهده می‌کنید:

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

HTTP/1.1 200 OK
Server: openresty/1.13.6.2
Date: Mon, 19 Aug 2019 16:43:26 GMT
Content-Type: text/javascript;charset=UTF-8
Connection: close
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Backend-Server: auth-be3.alaps.kz.prod.bash.kz
X-Bug-Bounty: Please report bugs and vulnerabilities to bugs@kolesa.kz
Content-Security-Policy: frame-ancestors 'self' http://webvisor.com https://webvisor.com
Strict-Transport-Security: max-age=31536000
Content-Length: 627
window.xdm = {
    sess: {
        is_authenticated: 1,
        token: 'xG3ROFWcb7pnXSnMr8MkaBvH01pLlCHqn0sPt0PVL6BBWYdQPdvA31tBi6dLB5njv5jhMW3y/cGBMRB9LC/69zv867wweaDhkxX6arGVzYDy2q+J52nkOQJ+62rR9wLPYJGyEpNGWeOBSp12vugXZUPq2RA6FMptbNkGQpJFjAclXSzduj7wJJgAUONMj3mkkElM1nWmIllrl5zDEz6s7077E4ibx//BvnfZ9AIC/9b2PB+QzVKOnSzzcr9wSXqta9TEDHvjopqbUd4UE2xSMRSj/zxPQlCba5632hcIXnzZB3A8fvahvf2Hm5ssuC+cwuKU8pAdE/qcGQSJKdhpYXxntGkQiLdEAliyCq+fahS4itb6HlFH/+H20RsZA+cjyaF7ntnW5tYY31vxJXovrR3oinaj9YDSzoCZYMDYPJMdk+HuZhRuxxEl8abuNlGD0aCt2GCPV7GY0J9Ma7AcPw=='
    }
};
(function ($) {
    "use strict";
$.xdm = window.xdm;
}(jQuery));

همان‌طور که مشخص است، آبجکت window.xdm.sess دارای is_authenticated و token است. در این لحظه، بر اساس آنچه پیشتر گفتیم، کاربر دارای توکِن احراز هویت است نه کوکی اهراز هویت. به‌همین منظور درخواست Ajax ارسال می‌شود (خب چون ‌Origin یکسان است، نیازی به CORS نیست):

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

آبجکت جاوااسکریپت آسیب‌پذیر

سوالی که مطرح میشه اینه که آیا هر Origin دلخواهی می‌تونه توکِن احراز هویت کاربر رو استخراج کنه؟ قطعا که می‌تونه چون JSONP دقیقا SOP رو از کار می‌ندازه پس مشکلی برای این قضیه وجود نداره:

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

مراحل اکسپلویت

مراحل اکسپلویت به شرح زیر است:

  1. ساختن یک صفحه که از طرف کاربر درخواست JSONP بفرستد
  2. مجبور کردن کاربر برای بازدید از صفحه مخرب (می‌تواند یک تکه کد مخرب در یک سایت پربازدید قرار بگیرد)
  3. ارسال توکِن احراز هویت کاربر توسط خودش به مهاجم
  4. تبدیل توکِن احراز هویت به کوکی احراز هویت و تصاحب حساب کاربری

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

https://gist.github.com/Voorivex/a4d15400b153712fce86e81c6876e455

ویدئو اثبات آسیب‌پذیری:

همچنین کلیه مطالب بالا رو تو یه فیلم با توضیحات کامل ضبط کردم، امیدوارم خوشتون بیاد:

علاقه‌مند به امنیت، بازی و تفریح. ۳۳ سال زندگی کردم، دوست دارم ۲۷ سال دیگه هم زندگی کنم. دو پارادوکس بزرگ زندگیم اینه که رشته تحصیلیم لیزر و اپتیک هست،‌ ربطی به کارم نداره، اسمم هم یاشار هست اما ترک نیستم.
زندگی من به سه قسمت تقسیم میشه، قسمت اول کار روزانه من هست که مثل بقیه مردم میرم سر کار. قسمت دوم سعی در براورده کردن علایق کاری خودم، مثل همین وبلاگ. قسمت سوم هم خانواده، مسافرت و تفریح. تلاش می‌کنم توی قسمت دوم، باگ‌بانتی کار کنم،‌ هم درآمد خوبی داره هم هیجان خاص خودش رو. اون قسمت‌هایی از تکنیک‌ها و کشفیات در فرایند باگ‌بانتی رو سعی می‌کنم توی این وبلاگ قرار بدم.
  • به اشتراک بگذارید:
  1. رضا گفت:

    خدا حفظتون کنه
    عالی

  2. حسین گفت:

    خیلی عالی ممنون

  3. محمد گفت:

    مشکل رو نمیشه با CSP حلش کرد؟

    • یاشار شاهین‌زاده گفت:

      بعید میدونم بتونه، چون ما چیزی تزریق نمی‌کنیم، مثالی داری بده مطالعه کنم. اینم شما مطالعه کن.

  4. sobhan گفت:

    بسیار عالی سایتتون عالیه?

  5. amir گفت:

    سلام این xssi نیست؟

  6. Sama گفت:

    خیلی عالی
    اگه اینجا کوکی ها httponly داشته باشن بازم میتونیم این کوکی رو به سرورمون ارسال کنیم؟