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

بهره‌برداری موفق از آسیب‌پذیری Prototype Pollution با استفاده از DOM XSS


منبع: یه جایی تو توییتر!
منبع: یه جایی تو توییتر!


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

آسیب‌پذیری Prototype Pollution چیه؟

به نقل از گنده‌ی دنیای امنیت PortSwigger: این Prototype Pollution یه آسیب‌پذیری مربوط به زبان شیرین و دوست داشتنی JavaScript هست که به نفوذگر (همون هکر) اجازه می‌ده تا بیاد و خصیصه‌های (Properties) دلخواه خودش رو به والد اولیه یک شئ عمومی اضافه کنه. (اینا دیگه مربوط میشه به زبان برنامه‌نویسی، این واژه‌های عجیب و غریب تقصیر من نیست) البته که این آسیب‌پذیری به‌تنهایی قابل بهره‌برداری و سوءاستفاده نیست؛ در واقع با این کار نفوذگر می‌تونه خصیصه‌هایی از اشیاء رو تغییر بده که در حالت عادی بهشون دسترسی نداره. اگه برنامه، خدایی نکرده یادش بره که ورودی‌هایی که از کاربر می‌گیره رو درست و حسابی بررسی کنه، این قابلیت (دستکاری کردن والد اولیه یک شئ) می‌تونه با آسیب‌پذیری‌های دیگه ترکیب بشه و... :(
خدا بیامرزه.


منبع: پورت سوئیگر
منبع: پورت سوئیگر


خب تا اینجا فهمیدیم که این آسیب‌پذیری قابل بهره‌برداری نیست و باید با یک آسیب‌پذیری دیگه ادغام بشه. قبل از اینکه بریم جلوتر، نیازه تا یه کوچولو با بعضی مفاهیم جاوا اسکریپت آشنا بشیم تا بتونیم بهتر درک کنیم که این آسیب‌پذیری چجوری کار میکنه.

در جاوا اسکرپیت همه چیز یک شئ است!

یک شئ در جاوا اسکریپت، مجموعه‌ای از کلیدها و مقادیر اون‌هاست که بهش میگن Key-Value که این مقادیر میتونن از هر نوعی باشن (رشته، عدد، عبارت شرطی و... ). ما می‌توینم تو جاوا اسکریپت به راحتی آب خوردن یک شئ بسازیم، همونطور که این پایین می‌بینید:

let myObject = { prop1: &quotA simple string&quot, prop2: 100, prop3: true }

خب حالا برای دسترسی به این شئ که ایجاد کردیم می‌تونیم از دو روش استفاده کنیم؛ روش اول استفاده از نقطه برای دسترسی به شئ:

myObject.prop1 // -> will return &quotA simple string&quot

و روش دوم استفاده از براکت:

myObject['prop2'] // -> will return 100

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

الگوی برنامه نویسی شئ‌گرا

جاوا اسکریپت یک زبان برنامه‌نویسی چند وجهیه که می‌تونیم باهاش به روش‌های مختلف برنامه‌نویسی کنیم؛ این روش‌ها عبارتند از:

  • برنامه‌نویسی functional
  • برنامه‌نویسی object-oriented
  • برنامه‌نویسی procedural
  • برنامه‌نویسی prototype

تو این مورد ما بیشتر به برنامه‌نویسی object-oriented علاقه نشون می‌دیم چون این مدل از برنامه‌نویسی از اشیاء، کلاس‌ها و والدهای اولیه (Prototype) تشکیل میشه. قرار نیست که دوره جاوا اسکریپت برگذار کنیم! اما یکم درباره مفاهیم ارث‌‌بری و نمونه‌سازی والد اولیه میگیم.

دوباره برمی‌گردیم به ستون امنیت کره‌ی زمین (PortSwigger) که ایشون در این باره می‌فرمایند:

وقتی که خصیصه‌ای از یک شئ رو صدا می‌زنیم، موتور جاوا اسکریپت اول سعی می‌کنه تا اون خصیصه رو مستقیما از داخل خود همون شئ بخونه (شئ ای که داخلش هستیم و اونجا داریم خصیصه رو صدا می‌زنیم) اگه اون شی چنین خصیصه‌ای نداشته باشه جاوا اسکریپت شروع میکنه میره عقب و داخل والدهای اون شئ رو می‌گرده.

وقتی که یه شئ جدید می‌سازیم، جاوا اسکریپت با توجه به نوع شئ ساخته شده، به‌صورت خودکار اون رو متصل می‌کنه به یکی از والدهای اولیه از پیش ساخته شد (ارث‌بری می‌کنه):

اشاره به خصیصه‌های والد متصل شده
اشاره به خصیصه‌های والد متصل شده


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

اشاره به والد از طریق __proto__
اشاره به والد از طریق __proto__


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

myObject.__proto__.__proto__.__proto__


این مهمه که اشاره کنم اینجا این __proto__ که گفتیم، هم به‌عنوان Setter و هم به عنوان Getter عمل میکنه؛ یعنی هم می‌تونه به خصیصه دسترسی داشته باشه و هم می‌تونه اون‌ها رو تغییر بده (اینا هم باز مفاهیم برنامه‌نویسیه! ای بابا)

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


راهنمایی برای آلوده‌سازی موفق یک Prototype

مجدد از سلطان PortSwigger نقل قول می‌کنیم.

آلوده‌سازی Prototype وقتی اتفاق می‌افته که جاوا اسکریپت به‌صورت بازگشتی میاد و یک خصیصه از شئ قابل کنترل توسط کاربر رو با یک شئ که از قبل وجود داره ادغام می‌کنه بدون اینکه ورودی‌های کاربر رو اعتبارسنجی کنه. برای اینکه بتونیم به‌صورت موفق از این آسیب‌پذیری بهره‌برداری کنیم، نیازه تا چندتا مورد رو بررسی کنیم که حتما وجود داشته باشن:
* منبع آلوده‌سازی -- هر ورودی که به ما اجازه بده تا بتونیم والد یک شئ رو باخصیصه‌های دلخواه آلوده کنیم.
* سینک -- یه تابع جاوا اسکریپت یا یک عنصر DOM که می‌تونه کد جاوا اسکریپت رو اجرا کنه.
* گجت آسیب‌پذیر -- به هر خصیصه‌ای گفته میشه که تو کد ما، پاس داده میشه به یک سینک تا اجرا بشه، بدون اینکه اعتبارسنجی بشه.


نمونه واقعی از این آسیب‌پذیری که بهش برخوردم :)

خب حالا وقتشه که بریم سراغ سناریوی واقعی از این آسیب‌پذیری که تو یه برنامه خصوصی بهش برخوردم. داشتم روی سایت www.example.com کار می‌کردم و کلی هم آسیب‌پذیری داشت (XSS ،Iframe Injection) این موضوع باعث شد تا متوجه بشم این سایت اصلا ورودی‌های کاربر رو بررسی و اعتبارسنجی نمی‌کنه. بعد از اینکه به چندتا مورد برخوردم که اجازه می‌داد تا مستقیما HTML و JS تزریق کنم، به این فکر افتادم که آیا می‌تونم مستقیما از URL برای آلوده‌سازی Prototype استفاده کنم؟

خب با دستور جذاب زیر شروع کردم اما وف جلومو گرفت :(

?__proto__.zhero=zhero
وف بی ادب
وف بی ادب


خب یه سری روش رو امتحان کردم ولی هیچکدوم جواب نداد، حتی سعی کردم از Constructor استفاده کنم اما بازم به نتیجه نرسیدم و وف منو بلاک می‌کرد. بعدش اومدم از روش تزریق تو در تو استفاده کردم:

?__pro__proto__to__.zhero=zhero

اینم نمونه Constructor که به جای نقطه از براکت استفاده کردم:

constructor[prototype][zhero]=zhero


یادتون نره که حتما هر دو روش دسترسی به خصیصه‌های شئ رو تست کنید (استفاده از نقطه و استفاده از براکت) این مورد بعد از دور زدن وف خیلی می‌تونه مفید باشه.

وقتی که داشتم توی Web Archive سایت رو بررسی می‌کردم، متوجه شدم که تعداد زیادی آدرس هستن که داخلشون از علامت # استفاده شده و بعدش اطلاعات مفیدی اومده اینکه بعد از این علامت یکسری اطلاعات قرار گرفته، مشخص می‌کنه که این علامت فقط یک هشتگ ساده نیست و قطعا سایت داره از این علامت برای گرفتن اطلاعات خاصی استفاده می‌کنه و بعدش اون‌ها رو به توابعی پاس میده که این توابع با آدرس URL و پارامترهای اون کار داره؛ پس تصمیم گرفتم که این رو تست کنم.

#__proto__[zhero]=zhero

اگه ساختار آدرس‌های URL رو به خوبی بشناسید، می‌دونید که این علامت و اطلاعات اون سمت سرور ارسال نمی‌شه و مستقیما توسط DOM در مرورگر پردازش میشه، پس وف قطعا جلوی ما رو نمیگیره. خب حالا توی کنسول مرورگر تست کردم و دیدم که تزریق ما موفقیت‌آمیز بوده و والد شئ آلوده شده:

آلوده‌سازی موفق
آلوده‌سازی موفق


با توجه به نتایج، فهمیدیم که آلوده‌سازی انجام شده، یکم که بررسی کردم تونستم کد مسئول این همه نابسامانی رو پیدا کنم! (البته هنوز این کد بنده خدا کاری نکرده، ولی این کد همونیه که قراره ازش سوءاستفاده کنیم برای بهره‌برداری)

قطعه کد خطا کار!
قطعه کد خطا کار!


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

با توجه به چیزایی که بالاتر گفتیم، برای موفقیت‌آمیز بودن حمله، باید یکسری نکات رو رعایت کنیم؛ یکی از این نکات این بود که تزریق ما باید یه‌جایی مورد استفاده قرار بگیره تا بتونیم ازش سوءاستفاه کنیم؛ پس شروع کردم به بررسی کد و دیدم چیزی که تزریق کردیم داخل تگ style به عنوان یک attribute و مقدار اون قرار می‌گیره:

جایی که مقدار تزریق شده قرار گرفت
جایی که مقدار تزریق شده قرار گرفت


بعد از اینکه اینو دیدم گفتم خب چیکار میشه کرد؟ خیلی سادس؛ یه Event Handler می‌خوام که بتونم باهاش کد جاوا اسکریپت اجرا کنم. چی بهتر از ؟ پس کد زیر رو تزریق کردم:

#__proto__[]=alert(%22XSS by zhero_%22)

و نتیجه شد این:

بهره‌برداری از آسیب‌پذیری
بهره‌برداری از آسیب‌پذیری


ما تونستیم به آسیب‌پذیری XSS برسیم؛ البته که با تزریق کد می‌تونیم کارهای خفن‌تری بکنیم ولی برای اینکه ثابت کنیم آسیب‌پذیری وجود داره، به نظرم تا همینجا کافیه.



مرسی که وقت ارزشمندتون رو گذاشتید و این رایتاپ رو خوندید، حتما حتما حتما اگه انتقاد یا پیشنهادی داشتید خوشحال میشم بهم بگید تا بتونم محتواهای بعدی رو بهتر و قوی‌تر تهیه کنم البته تا یادم نرفته بگم که این رایتاپ تجربه شخصی خود من نیست و اون رو آقای Allam Rachid نوشته و من فقط از این آدرس برداشتمش و ترجه کردم براتون.
جاوا اسکریپتآسیب‌پذیریxssjavascript
زمانی که جوون بودم، کارشناس آزمون نفوذ و توسعه دهنده اسناد امنیتی بودم؛ الان که پیر شدم خستم، فقط می‌خوابم!
شاید از این پست‌ها خوشتان بیاید