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

04 آگوست 2022

امروز میخوام مسیری که برای پیدا کردن یه 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 توی زینویس حذف میشدن. ولی چون اینجا میتونم زیرنویس دستی خودم رو بدم، اینکار از طریق فیلتر نمیشه و میتونم ورودی دلخواه بدم.

کانفیگ دستی با ورودی با آدرس دلخواه زیرنویس

و توی فایل زیرنویس، همچین کدی رو اضافه کردم:

محتویات فایل movie.srt

و به این صورت به XSS بدون نیاز به User Interaction رسیدم:

البته همونطور که اول گفتم کوکی های پنل در دسترس چون پلیر روی یه زیر دامنه دیگه قرار داشت، ولی به عنوان باگ Medium شناسایی شد و بهش بانتی تعلق گرفت.

1 پست نوشته شده
برنامه‌نویسی که امنیت دوست داره، شایدم برعکس
  • به اشتراک بگذارید:
برچسب‌ها:
  1. سپهر گفت:

    عالی بود لذت بردیم موفق و پیروز باشید

  2. تِفوُ گفت:

    چقدر لذت میبرم وقتی یه موضوعی رو اینقدر ماهرانه به چالش میکشی . دمت گرم یاشار عالییییی ❤️❤️❤️

  3. محمد گفت:

    آفرین خوب بود. از کروم کنسول و امکانات دیباگ اون هم برای پیگیریِ روندِ اجرای جاوااسکریپت می‌تونید استفاده کنید.
    برای مثال می‌شه روی XHR به صورت کلی breakpoint قرار داد و هر زمان که فرانت خواست یه XHR request بفرسته ما اتچ بشیم به اون نقطه از کد. یه مقدار البته frame ها رو باید بالا پایین کنی تا برسی به نقطه‌ی اصلی و در مجموع خوبه.

  4. hunter گفت:

    جالب بود