امروز میخوام مسیری که برای پیدا کردن یه XSS توی ویدیو پلیر آروان طی کردم رو بگم، البته بخاطر اسکُپ محدود، به کوکیهای پنل آروان دسترسی نداریم ولی بنظرم مسیرش ارزش به اشتراک گذاشتن رو داشت.
خب قضیه از اینجا شروع شد که صفحه ویدیو پلیر، یه URL دریافت میکرد و از اونجایی که کوئری استرینگهای ورودی Html، سمت مرورگر بررسی/اجرا میشه، از خود بیخود شدم ببینم چطوری داره کار میکنه.
فرمت URL ورودی به این شکله:
طبق تصویر و لاگ مرورگر، ویدیوپلیر یه درخواست XHR به این آدرس میزنه و کانفیگ مربوط به ویدیو رو لود میکنه تا ویدیو رو نمایش بده. محتویات فایل کانفیگ آدرس بالا هم به اینصورت هست:
چالش اول:
آیا ما میتونیم آدرس دلخواه خودمون رو بجای آدرس dm.arvanvod.com به ورودی بدیم؟ امتحان میکنیم:
با تغییر آدرس کانفیگ، متوجه شدم که توی کنسول پیغام خطا میاد که نمیتونه ویدیو رو لود کنه و میگه ممکنه از CORS باشه، اما درخواست XHRای ارسال نشده! درخواست CORS سمت مرورگر بلاک میشه بنابراین اگر مشکل از CORS باشه، باید درخواست XHR ارسال بشه. پس نتیجه میگیریم احتمالا یه مکانیزمی برای آدرس ورودی وجود داره.
از اینجا چالش شروع میشه… ولی خوش شانسیمون اینجاست که همچی توی مرورگر داره اتفاق میفته پس مجبور نیستیم کور کورانه بریم جلو. سورس کد جاوا اسکریپت پلیر توی این آدرس وجود داره:
https://player.arvancloud.com/arvanplayer.min.js
که مشخصا Minify شده و خوندن سورس کد رو خیلی سخت میکنه. اما اگه یکم زرنگ بازی دربیاریم و توسعه دهنده سوتی داده باشه، میتونیم این آدرس رو بررسی کنیم تا به کد اصلی (minify نشده) برسیم:
https://player.arvancloud.com/arvanplayer.js
ولی خب ضایع میشیم و میبینیم که اونقدرا هم زرنگ نیستیم.
پس راه سخت رو انتخاب کردم و بعد از Beautify کردن سورس کد شروع به بررسی کد کردم. استفاده از ابزارهای Beautifier صرفا کد رو Indent میکنن و خوانایی رو کمی بهبود میدن ولی اسم متغیرها رو نمیتونن برگردونن. شانسی که اینجا داشتم این بود که کد Minify شده بود ولی Obfuscate نشده بود. قطعا منطقی نبود توی یه فایل +۲۰ هزار خطی دنبال یه تابع ولیدیشن باشم پس دنبال “arvanvod.com” گشتم و به این خط کد رسیدم:
اینجا متوجه شدم که علاوه بر دامنه arvanvod.com، دامنههای دیگه ای هم وایت لیست شدن. اما هیچکدومشون آزاد نبودن و تا جایی که میدونستم روشون کنترلی نداشتم. پس در ادامه با توجه به اینکه از کلید hosts در آبجکت ST استفاده شده، دنبال ST.hosts توی سورس کد گشتم که تنها جایی که استفاده شده بود این تابع بود:
خب به کد تابعی که کار ولیدیشن رو انجام میده رسیدیم ولی هنوز خیلی خوانا نیست. با کمی وقت گذاشتن و بررسی متغیر ها، کد رو کمی بهبود دادم و به یه کد (تقریبا) تمیز تر تبدیلش کردم:
اینجا کاری که داره انجام میشه به اینصورت هست که آدرس ورودی Parse میشه، و بعد host ازش بیرون کشیده میشه، و بصورت رشته ای چک میشه که آیا آدرس ورودی با یکی از مقادیر ST.hosts تموم شده یا نه.
توی سناریو ما dm.arvanvod.com با arvanvod.com تموم شده پس دامنه معتبر هست، اما please.com معتبر حساب نمیشه. اینجا توسعه دهنده میخواسته علاوه بر دامنه arvanvod.com، زیر دامنه های arvanvod.com رو هم قبول کنه، پس بجای اینکه دامنه هارو مستقیما باهم مقایسه کنه، جوری شرط گذاشته که زیر دامنه ها هم معتبر حساب بشن. اما یه حالت بررسی نشده! اگر من یه دامنه مثل lolarvanvod.com ثبت کنم، از این فیلتر رد میشم! پس چک نکردن یه نقطه باعث میشه بتونیم ورودی دلخواه رو به پلیر بدیم و آدرس کانفیگ خودمون رو وارد کنیم.
پس پیلود نهایی ما میشه:
https://lolarvanvod.com/malicious-config.json
درسی که از این تیکه کد گرفتم این بود که همیشه خودم رو جای توسعه دهنده بزارم و سعی کنم کد رو بفهمم، نه صرفا هدف توسعه دهنده رو. چالش اول حل شد، اما حالا با کانفیگ شخصی سازی شده خودمون چطوری به XSS برسیم؟
چالش دوم:
توی این چالش هدفمون اینه که یه کانفیگی پیدا کنیم که بشه باهاش یه XSS اجرا کرد. توی عکس دوم پست (که نمونه کانفیگ وجود داره) تقریبا تمام ورودی هارو تغییر دادم اما به نتیجه نرسیدم. اینجا حدس زدم که احتمالا یه سری کانفیگ دیگه هم ویدیو میتونه قبول کنه ولی اینجا نداریمش! پس توی سورس کد دوباره گشتم و به یه سری کانفیگ دیگه مثل تبلیغات، زیرنویس و … رسیدم. از اونجایی که حجم کانفیگ ها نسبتا زیاد بود، تصمیم گرفتم بجای اینکه سعی کنم کانفیگ هارو دونه دونه تست کنم، از آخر به اول بیام. دقیقا مثل وقتی که برای حل کردن بازی Maze از مبدا شروع میکنیم تا راه خروج رو پیدا کنیم، من دنبال راه خروج گشتم و سعی کردم ببینم مبداش کجاس.
توی این حالت کافی بود دنبال جاهایی بگردم که innerHTML استفاده شده و ورودیش از کانفیگ میاد. بعد کمی وقت گذاشتن متوجه شدم بخش دریافت زیر نویس، وقتی میخواد زیرنویس رو لود کنه از innerHTML استفاده میکنه.
برای نفوذ کافی بود یه فایل زیرنویس درست کنم که داخل متنش از ثانیه صفر، یه متن با کد HTML\JS اجرا بشه. اگر همین کار رو میخواستم از طریق پنل انجام بدم، تگ های HTML توی زینویس حذف میشدن. ولی چون اینجا میتونم زیرنویس دستی خودم رو بدم، اینکار از طریق فیلتر نمیشه و میتونم ورودی دلخواه بدم.
و توی فایل زیرنویس، همچین کدی رو اضافه کردم:
و به این صورت به XSS بدون نیاز به User Interaction رسیدم:
البته همونطور که اول گفتم کوکی های پنل در دسترس چون پلیر روی یه زیر دامنه دیگه قرار داشت، ولی به عنوان باگ Medium شناسایی شد و بهش بانتی تعلق گرفت.
عالی بود لذت بردیم موفق و پیروز باشید
چقدر لذت میبرم وقتی یه موضوعی رو اینقدر ماهرانه به چالش میکشی . دمت گرم یاشار عالییییی ❤️❤️❤️
یاشار نبوده که ساده اون نویسنده بالا هست اونو ببین
آفرین خوب بود. از کروم کنسول و امکانات دیباگ اون هم برای پیگیریِ روندِ اجرای جاوااسکریپت میتونید استفاده کنید.
برای مثال میشه روی XHR به صورت کلی breakpoint قرار داد و هر زمان که فرانت خواست یه XHR request بفرسته ما اتچ بشیم به اون نقطه از کد. یه مقدار البته frame ها رو باید بالا پایین کنی تا برسی به نقطهی اصلی و در مجموع خوبه.
جالب بود
اینجور گزارش ها به نظر من خیلی دقت و پیگیری نیاز داره و هر کسی هم نمی تونه این نوع باگ ها را پیدا و exploit کنه چون علم زیادی میخواد
درود بر شما
باز هم رایتاپ بنویس
ایولا کارت درسته
عالی
عالی بود خیلی لذت بردم:))
از خوندن این رایتآپ لذت زیادی بردم دسخوش 🙂