12 Path traversal
1- يعني ايه path traversal :
تخيل إن سيرفر الويب ده عبارة عن مبنى، والتطبيق اللي شغال عليه هو موظف الأرشيف. الموظف ده واخد تعليمات إنه يسلمك أي ورقة تطلبها من درج الصور بس. الطبيعي إنك تقوله "هاتلي الصورة اللي اسمها كذا"، فيفتح الدرج ويجيبها.
بس الموظف ده (اللي هو الكود بتاع التطبيق) بيثق في كلامك زيادة عن اللزوم ومبياخدش باله إنت بتقول إيه بالظبط. فبدل ما تطلب صورة، بتقوله: "اخرج من درج الصور، ارجع خطوتين لورا في الطرقة، وادخل مكتب المدير وهاتلي ملف المرتبات". الموظف الساذج هيمشي ورا كلامك وينفذ ده حرفياً!
برمجياً بقى الموضوع بيمشي إزاي؟
التطبيق بيكون متبرمج إنه يقرأ الملفات من مسار أساسي (Base Directory) مقفول عليه، مثلاً: /var/www/html/images/
المشكلة (أو الثغرة) بتحصل لما المبرمج ياخد الـ Input بتاعك (اسم الملف اللي إنت عاوزه) ويلزقه في المسار ده من غير ما يعمل عليه أي فحص أو فلترة (Validation).
زي ما إنت عارف في أنظمة التشغيل، الرمز ../ (أو Dot-Dot-Slash) معناه "ارجع فولدر واحد لورا" (Go up one directory).
-
الاستخدام الطبيعي: إنت بتطلب
avatar.png، فالسيرفر بيقرأ المسار كدة:/var/www/html/images/avatar.png(وهنا بيجيب الصورة عادي). -
الاستخدام الخبيث (Path Traversal): إنت بتكتب في الـ Input بتاعك
../../../etc/passwd. السيرفر بياخد الـ Input يلزقه ويبقى المسار كدة:/var/www/html/images/../../../etc/passwd
السيرفر هينفذ الـ ../ ثلاث مرات، ويرجع لورا لحد ما يوصل لأول السيرفر خالص اللي هو الـ Root Directory (/)، وبعدين يدخل على مجلد etc ويجيبلك ملف passwd (اللي فيه بيانات تخص النظام).
الخلاصة: الثغرة دي بتحصل بسبب انعدام الثقة (Improper Input Validation)، وبتسمحلك تكسر السجن (الفولدر) اللي التطبيق حاطك فيه، وتتفسح في سيرفر الضحية وتقرأ ملفات حساسة المفروض إنك متشوفهاش (زي الأكواد البرمجية، كلمات السر، أو ملفات الـ Configuration).
2- Reading arbitrary files via path traversal :
السيناريو الطبيعي (كيف يعمل التطبيق)
تخيل إن عندنا موقع تجارة إلكترونية (Shopping App) بيعرض صور للمنتجات. في كود الـ HTML، الموقع بيطلب الصورة بالشكل ده: <img src="/loadImage?filename=218.png">
هنا الموقع بيبعت طلب (Request) لصفحة اسمها loadImage وبيديها parameter اسمه filename قيمته 218.png. السيرفر متبرمج إنه يحتفظ بكل الصور جوه فولدر ثابت (Base Directory) المسار بتاعه هو: /var/www/images/.
عشان السيرفر يرجعلك الصورة، بياخد الكلمة اللي إنت كتبتها في الـ filename ويلزقها في المسار الأساسي، فبيقرأ الملف من هنا: /var/www/images/218.png
الهجوم (كيف يحدث الـ Exploit)
بما إن التطبيق ده مش بيعمل أي نوع من الفلترة أو الحماية (No defenses)، المهاجم يقدر يتلاعب بقيمة الـ filename. بدل ما يطلب صورة، هيبعت الـ Request بالشكل ده: [https://insecure-website.com/loadImage?filename=../../../etc/passwd](https://insecure-website.com/loadImage?filename=../../../etc/passwd)
بناءً على البرمجة بتاعة التطبيق، السيرفر هياخد الـ Payload ده ويلزقه في المسار الأساسي زي ما هو، فهيبقى المسار النهائي اللي السيرفر بيحاول يقرأه كده: /var/www/images/../../../etc/passwd
كيف يفهم نظام التشغيل هذا المسار؟
في أنظمة التشغيل، الرمز ../ بيُعتبر أمر سليم (Valid sequence) معناه "ارجع فولدر واحد لورا" (Step up one level). بما إن المهاجم كتب ../ تلات مرات ورا بعض، السيرفر هينفذها كالتالي:
-
أول
../هترجعه من فولدرimagesإلى فولدرwww. -
تاني
../هترجعه منwwwإلىvar. -
تالت
../هترجعه منvarإلى الـ Root Directory اللي هو الجدر الأساسي لنظام التشغيل (/).
من الـ Root ده، السيرفر هيدخل على فولدر اسمه etc ويقرأ ملف اسمه passwd. في أنظمة لينكس ويونيكس (Unix-based)، ملف الـ /etc/passwd ده ملف قياسي ومهم جداً لأنه بيحتوي على تفاصيل كل المستخدمين (Users) اللي متسجلين على السيرفر. وبنفس الطريقة دي، المهاجم يقدر يوصل لأي ملف تاني في السيرفر.
ماذا لو كان السيرفر يعمل بنظام Windows؟
في أنظمة لينكس بنستخدم الـ Forward Slash (/). لكن الجميل في ويندوز إنه بيفهم الطريقتين: الـ ../ و كمان الـ ..\ (Backslash). عشان كده، لو بتعمل Pen-Test على سيرفر ويندوز، الهجوم هيكون مشابه جداً بس هتستهدف ملفات خاصة بويندوز، والـ Payload هيكون كده: [https://insecure-website.com/loadImage?filename=](https://insecure-website.com/loadImage?filename=)..\..\..\windows\win.ini
معلومة لك كـ Penteser: ملف الـ
win.iniفي ويندوز، وملف الـ/etc/passwdفي لينكس، هما أكتر ملفين بنستخدمهم كـ Proof of Concept (PoC) عشان نثبت للعميل إن الثغرة موجودة فعلاً ونجحنا نقرأ ملفات النظام.
Common obstacles to exploiting path traversal vulnerabilities :
المبرمج هنا بياخد باله إن فيه هجوم اسمه Path Traversal بيعتمد على الرجوع لورا، فبيعمل فلتر (Blacklist) وبيقول للتطبيق: "لو لقيت اليوزر كاتب ../ أو ..\ في الـ Input، امسحهم أو ارفض الطلب (Block) خالص".
هنا إنت كـ Pentester مش محتاج تستخدم الـ ../ أصلاً عشان ترجع لورا. إنت بتلجأ لاستخدام المسار المطلق (Absolute Path).
يعني إيه Absolute Path؟
يعني بدل ما تقول للسيرفر "ارجع من الفولدر اللي إنت فيه خطوتين لورا وبعدين ادخل على ملف كذا" (Relative Path)، إنت بتديله العنوان الكامل للملف بداية من الجدر الأساسي (Root) بتاع السيرفر.
المثال العملي: بدل ما تبعت الـ Payload بالشكل ده (والسيرفر هيعمله Block): filename=../../../etc/passwd
هتبعته بالشكل ده: filename=/etc/passwd
ليه الطريقة دي بتنجح؟
في بعض الدوال (Functions) الخاصة بالتعامل مع الملفات في لغات البرمجة والـ APIs بتاعة نظام التشغيل، لما بتستقبل مسار بيبدأ بعلامة / (اللي بتعبر عن الـ Root)، بتفهمه فوراً إنه مسار مطلق.
النتيجة إن الدالة دي بتتجاهل تماماً الـ Base Directory اللي المبرمج كان حاطه في الكود (زي /var/www/images/)، وبتعتبر إن المسار اللي إنت دخلته هو المسار الوحيد والنهائي اللي المفروض تقرأ منه.
بكده، إنت قدرت توصل للملف الحساس من غير ما تكتب ولا ../ وتخطيت الفلتر اللي المبرمج كان معتمد عليه كلياً.
Nested Traversal Sequences :
دي بتنجح لما المبرمج يحاول ينظف الـ Input بتاعه باستخدام طريقة الفلترة العادية (معادلة مسح بسيطة)، بس بيعملها مرة واحدة بس (Non-recursive stripping).
إزاي المبرمج بيفكر؟
المبرمج بيكتب كود بيمشي على الكلمة اللي إنت دخلتها، وأول ما يشوف الـ ../ يقوم ماسحها (يعني بيبدلها بنص فاضي ""). هو فاكر إن بكده طالما مسح الـ ../ فالسيرفر في أمان.
إزاي إحنا بنفكر كـ Pentesters؟ (الـ Exploit)
إحنا بنستغل إن عملية المسح دي بتحصل مرة واحدة بس على الـ Input. فبدل ما نكتب ../ العادية، بنلغم الـ Input ونكتبه كدة: ....//
تعال نفصص الـ Payload ده (....//) ونشوف إيه اللي جواه: هو عبارة عن نقطتين (..)، بعدين جوة منهم ../ (دي المتداخلة أو الـ Nested)، وفي الآخر علامة slash (/).
لما الـ Filter بتاع المبرمج يشتغل، هيلمح الـ ../ اللي في النص دي ويقوم ماسحها. شوف بقى السحر اللي هيحصل لما يمسحها:
النص الأصلي: . . [ . . / ] /
بعد المسح: . . /
النتيجة النهائية: . . /
النص اللي اتبقى ورجع للتطبيق عشان ينفذه هو ../ سليم ١٠٠٪! يعني الفلتر بإيده هو اللي ركب الـ Payload بتاعنا ورجعه لأصله.
بالنسبة لويندوز؟
نفس الفكرة بالظبط بنطبقها على أنظمة ويندوز، بس بنلعب بالـ Backslash، فبنكتب الـ Payload كدة: ....\/
لما الفلتر يمسح الـ ..\/ اللي في النص، هيتبقى عندك ..\ سليم يرجعك خطوة لورا في فولدرات الويندوز.
قاعدة ذهبية لك كـ Pentester: طول ما الـ Filter بيعمل
strip(مسح) مشblock(رفض كامل للطلب)، الـ Nested payloads دي بتكون حل عبقري لتخطي الحماية.
sequences stripped with superfluous URL-decode :
هنا بقى إحنا بندخل في مستوى أعلى شوية من اللعب، لأننا مش بنتعامل مع فلتر المبرمج بس، إحنا أحياناً بنتعامل مع الـ Web Server نفسه (زي Apache أو Nginx أو Tomcat) أو بنتعامل مع ميكانيزم الـ Request نفسه زي الـ multipart/form-data (اللي بنستخدمه واحنا بنرفع ملفات).
السيرفرات دي أحياناً بتكون ذكية كفاية إنها تلقط الـ ../ وتمسحها قبل ما الـ Request يوصل للتطبيق (Application Layer) أصلاً. وعشان نتخطى العقبة دي، بنلجأ للـ Encoding (التشفير/الترميز).
خلينا نفصص التكنيكات دي:
١. الـ URL Encoding العادي
السيرفر بيفهم الحروف والأرقام عادي، بس لما بنحول الـ ../ للغة الـ URL (اللي هي Hexadecimal مسبوقة بعلامة %)، أحياناً السيرفر بيعديها من غير ما يفهم إن دي Traversal Sequence، ولما توصل للباك إند، التطبيق يترجمها وينفذها.
-
النقطة (
.) =%2e -
الـ Slash (
/) =%2f -
الـ Payload النهائي:
%2e%2e%2f(بدل../)
٢. الـ Double URL Encoding (الضربة المزدوجة)
لو السيرفر ذكي شوية، وبيعمل Decode (فك تشفير) للـ Request قبل ما يفلتره، الـ URL Encoding العادي هيتكشف. هنا بنستخدم الـ Double Encoding. الفكرة إننا بنعمل تشفير للرمز % نفسه (اللي هو بيساوي %25)، وبنسيب باقي الحروف زي ما هي.
-
الـ Payload المشفر مرة واحدة:
%2e%2e%2f -
لما نشفر علامة الـ
%: هتبقى%25 -
الـ Payload النهائي:
%252e%252e%252f
إيه اللي بيحصل في الكواليس؟
-
الـ WAF أو السيرفر بيستقبل
%252e%252e%252f، بيعملها Decode مرة واحدة فترجع%2e%2e%2f. -
السيرفر بيبص عليها ميلاقيش
../، فيقول "تمام، مفيش خطر" ويبعتها للتطبيق. -
التطبيق بياخد الـ
%2e%2e%2fويعملها Decode للمرة التانية فترجع لأصلها../والثغرة تتنفذ!
٣. التشفير غير القياسي (Non-standard encodings)
دي حتة Advanced شوية بتعتمد على أخطاء في طريقة قراءة السيرفرات لليونيكود (Unicode) أو ما يسمى بالـ Overlong UTF-8. أنظمة معينة زي إصدارات قديمة من Tomcat كانت بتقع في الثغرات دي. مثال عليها إنك تبعت السلاش (/) بأشكال غريبة السيرفر مش متوقعها بس الباك إند بيترجمها لسلاش عادي، زي:
-
..%c0%af -
..%ef%bc%8f
الأتمتة باستخدام Burp Suite 🛠️
طبعاً كـ Web Penetration Tester مش منطقي إنك تقعد تجرب كل التوليفات دي والتشفيرات دي يدوي (Manual). لو بتستخدم Burp Suite Professional، أداة الـ Intruder مريحة جداً في الحتة دي.
لما تيجي تعمل Fuzzing على الـ Parameter، هتلاقي جوه الـ Payloads قائمة جاهزة اسمها: Fuzzing - path traversal
القائمة دي متلغمة بكل أشكال الـ Encoding العادية والمزدوجة والـ Non-standard لكل أنظمة التشغيل، بتوفر عليك وقت وتخليك تركز في تحليل الـ Responses بدل ما تضيع وقتك في كتابة الـ Payloads!
validation of start of path :
في الحالة دي، المبرمج قرر يستخدم طريقة بنسميها Prefix Validation (التحقق من البداية). الكود بتاعه بيستخدم دالة برمجية (زي startsWith() في الجافا مثلاً) وبيقول للتطبيق: "لو الـ Input اللي اليوزر كاتبه بيبدأ بـ /var/www/images، إذن ده طلب شرعي وسيبه يمر، لو غير كده اعمله Block".
إزاي بنضحك على الفلتر ده؟
القاعدة هنا: إدي الفلتر اللي هو عاوزه عشان يعديك، وبعدين نفذ هجومك!
لما بتبعت الـ Payload بالشكل ده: filename=/var/www/images/../../../etc/passwd
العملية بتتم على مرحلتين، وكل مرحلة بتفهم النص بطريقة مختلفة:
-
على مستوى التطبيق (الـ Filter): التطبيق بيبص على أول الكلمة، بيلاقيها بتبدأ بـ
/var/www/images/. الفلتر بيكون سعيد جداً وبيقول "الطلب ده أمن ١٠٠٪" وبيسمح بمروره لنظام التشغيل. -
على مستوى نظام التشغيل (OS): نظام التشغيل بياخد المسار كامل عشان يفتحه. بالنسبة لنظام التشغيل، المسار ده معناه: "ادخل جوه
var، وبعدينwww، وبعدينimages، وبعدين ارجع ٣ خطوات لورا (فترجع للـ Root/)، وبعدين ادخل على فولدرetcواقرأ ملفpasswd".
الخلاصة: الثغرة دي بتنجح بسبب الاختلاف بين طريقة التطبيق في قراءة الـ String (بياخد حتة منه بس)، وطريقة نظام التشغيل في تنفيذ المسار (بينفذ المسار بالكامل بما فيه أوامر الرجوع للخلف).
validation of file extension with null byte bypass :
دي واحدة من أمتع تقنيات الـ Bypass، ورغم إنها كانت مشهورة جداً في الأنظمة القديمة (زي إصدارات PHP القديمة)، إلا إن فهمك ليها بيوريك إزاي المبرمجين والهاكرز بيفكروا في الـ "Under the hood" أو ما وراء الكواليس.
عشان تفهم الـ Null Byte (بايت الصفر)، لازم ننزل مع بعض لمستوى أنظمة التشغيل ولغة الـ C اللي مبني عليها أغلب الأنظمة دي.
يعني إيه Null Byte؟
الـ Null Byte هو بايت قيمته صفر (0x00 في الـ Hexadecimal)، ولما بنكتبه في المتصفح أو في الـ URL بنعمله Encoding بالشكل ده: %00.
أصل المشكلة (مشكلة لغة C)
في لغات البرمجة المنخفضة المستوى (Low-level) زي C و C++، مفيش حاجة جاهزة اسمها "نص" (String) زي الموجودة في Python أو Java. النص هناك عبارة عن مصفوفة من الحروف (Array of Characters) مرصوصة جنب بعض.
الكمبيوتر عشان يقرأ النص ده، محتاج يعرف النص بيخلص فين. هنا ابتكروا فكرة الـ Null Terminator. وهو عبارة عن Null Byte بيتحط دايماً في آخر النص عشان يقول لنظام التشغيل: "ستوب، النص خلص هنا، متقراش أي حاجة بعد كده".
إزاي الثغرة دي بتحصل؟ (اختلاف اللغات)
الثغرة (الـ Bypass) بتحصل بسبب سوء تفاهم بين لغة البرمجة اللي التطبيق مكتوب بيها (زي PHP أو Java)، وبين نظام التشغيل اللي بينفذ الأوامر الفعيلة (اللي مكتوب بـ C).
تعال نمشي مع الـ Payload بتاعك خطوة خطوة: filename=../../../etc/passwd%00.png
1. مرحلة الفلترة (مستوى التطبيق): التطبيق (PHP مثلاً) بيعتبر النص ده كله بلوك واحد. الفلتر بيبص على آخر النص بيلاقيه بينتهي بـ .png. الفلتر بيكون سعيد جداً وبيقول: "عظيم، اليوزر طالب صورة وامتدادها سليم، سمحوا للطلب ده يمر".
2. مرحلة التنفيذ (مستوى نظام التشغيل): التطبيق بياخد المسار ده ويبعته لنظام التشغيل عشان يفتح الملف. دوال نظام التشغيل (المكتوبة بـ C) بتبدأ تقرأ المسار حرف حرف من الشمال لليمين. أول ما توصل عند الـ %00، نظام التشغيل بيترجمها لـ Null Byte، وبيفهم إن دي نهاية النص.
3. النتيجة: نظام التشغيل بيتجاهل تماماً أي حاجة بعد الـ %00 (كأنها مش موجودة)، وبينفذ الأمر على الجزء اللي قرأه بس. فبيفتح الملف ده: ../../../etc/passwd وتقع الـ .png في الزبالة وكأنها لم تكن! وبكده إنت قدرت تقرأ الملف وتتخطى الفلتر.
ملاحظة ليك كـ Security Researcher: التقنية دي مبقتش تشتغل في الإصدارات الحديثة من لغات البرمجة (مثلاً تم قفلها في PHP بداية من إصدار 5.3.4، وفي Java من إصدار 7u40) لأنهم عدلوا الطريقة اللي اللغات دي بتتعامل بيها مع الـ Null Byte وبقوا يرفضوا أي مسار بيحتوي عليه. بس إحنا دايماً بنجربه في شغلنا لأننا كتير بنصطدم بأنظمة قديمة (Legacy Systems).