
يكتشف ويستغل ثغرات تهريب طلبات HTTP عبر التحويل من HTTP/2 إلى HTTP/1.1، باستخدام تقنيات تهريب الرؤوس الآلية لتحديد التناقضات في تحليل الطرف الخلفي.
يساعدك هذه الأداة في اكتشاف واستغلال تهريب طلبات HTTP في الحالات التي يمكن تحقيقها عبر تحويل HTTP/2 -> HTTP/1.1 بواسطة الخادم الأمامي.
المخطط كالتالي:
يريد المهاجم إيجاد طلب بحيث يراه الخادم الخلفي كطلبين منفصلين.
إذا كان اتصال HTTP/1.1 بين الأمامي والخلفي يستخدم keep-alive، فقد يرسل الأمامي طلبات من مستخدمين آخرين على نفس الاتصال. إذا تمكنا من "تسميم" الاتصال بطلب جزئي يأتي بعد طلب شرعي، يمكننا استرداد طلب مستخدم آخر.
تشمل السيناريوهات المحتملة الأخرى تجاوز حماية الخادم الأمامي وإعادة الكتابة، تسميم ذاكرة التخزين المؤقت أو خداع ذاكرة التخزين المؤقت.
لمزيد من المعلومات حول تهريب طلب HTTP، يُرجى الرجوع إلى أكاديمية أمان الويب من Portswigger.
في HTTP/2، جميع أسماء وقيم ترويسات HTTP تكون ثنائية. هذا يعني تقنيًا أنها يمكن أن تحتوي على مسافات إضافية أو حتى أسطر جديدة.
ينص RFC7540#10.3 على أن التطبيقات التي تحول طلبات HTTP/2 إلى HTTP/1 يجب أن تأخذ في الاعتبار القيود على مجموعة الأحرف التي تنشأ عن هذا التحويل؛ معظم التطبيقات ترفضها بالفعل. على الرغم من ذلك، نأمل في العثور على تطبيقات تسمح بمثل هذه الترويسات. ستفسد طلب HTTP/1.1 المحول إلى الخادم الخلفي.
نقطة أخرى هي أن بعض الإصلاحات الحديثة المتعلقة بتهريب طلب HTTP قد يتم تنفيذها فقط لمحللات HTTP/1.1.
بشكل عام، نأمل أن تكون هناك تطبيقات لـ HTTP/2 ليست على دراية كبيرة بالأبحاث الحديثة حول تهريب طلب HTTP في HTTP/1.1 ولا تتضمن التخفيفات المقابلة.
بشكل مدهش، نعم!
لقد وجدت إمكانية لتهريب ترويسة تحتوي على حرف مسافة عبر Cloudflare، مما يفتح الباب لتهريب بين Cloudflare والعميل (في حالة قبول وتقليم أسماء الترويسات من قبل برنامج عميل Cloudflare). إليك منشور المدونة.
هناك أيضًا تقرير مكافأة آخر عن ثغرة أمنية لم يتم نشره بعد. يستخدم حقيقة أن البرنامج المخصص لا يقوم بتصفية الأسطر الجديدة في ترويسات HTTP2، ويحدث التهريب بنسبة 100% (يمكنني رؤية طلبات المستخدمين الآخرين).
ومع ذلك، أفهم أن هذا النوع من الثغرات يجب أن يكون نادرًا بشكل محبط: على عكس HTTP/1.1، لا يوجد العديد من تطبيقات HTTP/2، ومعظمها مصنوع مع مراعاة الأمان، وبالتالي رفض الترويسات المشبوهة أو غير الصالحة.
تحتوي الأداة على أمر فرعي يحاول اكتشاف ما إذا كان الهدف معرضًا لهجوم تهريب طلب HTTP تلقائيًا. تم وصف الخوارزمية وراء هذه الميزة في هذا القسم.
لتنفيذ هجوم تهريب طلب HTTP، نحتاج بالفعل إلى "تهريب" ترويسة واحدة أولاً (إما Content-Length أو Transfer-Encoding). هذا يعني أننا بحاجة إلى إرسال ترويسة تتحكم في مكان انتهاء جسم الطلب ولا تتم معالجتها بواسطة الخادم الأمامي ولكن تتم معالجتها بواسطة الخادم الخلفي.
يتم تحقيق ذلك عادةً عن طريق تعديل الترويسة بطريقة ما: إضافة مسافات أو علامات تبويب في نهاية اسمها، استبدال القيمة بقيمة شبه مكافئة، إلخ.
الفكرة الأساسية لخوارزمية اكتشاف الثغرة هي اكتشاف ما إذا كان الخادم يقوم بالفعل بمعالجة ترويسة مهربة كما لو كانت Content-Length أو Transfer-Encoding. نقوم بذلك عن طريق إرسال عدة طلبات: بعضها بقيم صالحة والبعض الآخر بقيم غير صالحة للترويسة. ثم نحاول اكتشاف ما إذا كانت هناك طريقة لـ تمييز الردود من هاتين المجموعتين.
لهذا السبب لا يحتوي مخرجات الأداة على كلمات "معرضة/غير معرضة": إنها تقول فقط ما إذا كان يمكنها تمييز الردود التي جاءت على الطلبات من هاتين المجموعتين.
تعتبر الأداة مجموعتين من ردود HTTP قابلة للتمييز إذا تم استيفاء شرط واحد على الأقل من الشرطين التاليين:
يتم التعامل مع حالات انتهاء المهلة كقيمة رمز حالة فريدة لا تساوي أي قيمة أخرى؛ وبالتالي، تتجاوز الأداة المخطط الكلاسيكي "الكشف عن طريق التوقيت".
لنأخذ مثالاً. لنفترض أننا نحاول تهريب ترويسة transfer-encoding عن طريق استبدال الشرطة بشرطة سفلية.
إذا ظهر أن الخادم يستجيب برمز الحالة 400 في كل مرة نرسل فيها transfer_encoding:zalupa ويعلق عندما تكون transfer_encoding:chunked، يمكننا القول أن الخادم من المحتمل أن يعالج الترويسة كقيمة لترميز النقل. من الناحية النظرية، يمكن أن يكون هذا إما الخادم الأمامي أو الخادم الخلفي.
الحالة الأولى ليست مثيرة للاهتمام حيث يمكننا إرسال النسخة غير المهربة من الترويسة على أي حال، والثانية هي ما نبحث عنه. نظرًا لأن كل شيء يحدث عبر HTTP/2، يمكن تجنب الحالة الأولى في معظم الأوقات: يحدد خادم HTTP/2 نهاية جسم الطلب بطريقة أخرى لا علاقة لها بترويسات الطلب ولا يتوقع أبدًا أن يكون الجسم بتنسيق HTTP/1.1 المجزأ (ذلك الذي يحتوي على أطوال أجزاء سداسية عشرية).
التغييرات المحددة لتقنيات الكشف هي:
نرسل نسخة مهربة من Transfer-Encoding: chunked (على سبيل المثال، transfer_encoding:chunked) وأجسام مختلفة: صالحة هي 0\r\n\r\n وغير صالحة هي 999\r\n.
في حالة اختلاف الردود، يمكننا التأكد من أن الخادم الخلفي يتلقى ويعالج الترويسة المهربة. لا يوجد سبب للخادم الأمامي للقيام بذلك: HTTP/2 لا يستخدم تنسيق الأجزاء الذي أرسلناه، لذلك سيكون غير صالح.
نتوقع أن يتعطل الخادم الخلفي (أي، انتهاء مهلة الطلب) للطلبات غير الصالحة حيث ينتظر وصول المزيد من البيانات.
هذا هو أكثر أشكال الكشف موثوقية: إذا توقف الخادم عند قراءة الجسم، فمن المحتمل أن شيئًا ما قد حدث خطأ نظرًا لعدم استخدام ترميز نقل HTTP/1.1 في طلب HTTP/2.
نرسل نسخة مهربة من Transfer-Encoding وأجسام مختلفة مرة أخرى: 0\r\n\r\n كجسم صالح و X\r\n\r\n كجسم غير صالح.
الحالة هي نفسها المذكورة أعلاه، ولكن بدلاً من قراءة الجسم، نتوقع أن يقوم الخادم الخلفي بالتحقق من صحته على الأقل.
نرسل نسخة مهربة من ترويسة Content-Length بقيمتين 1 و -1.
كلا القيمتين غير صالحتين من وجهة نظر الخادم الأمامي: هناك آلية أخرى لتحديد طول الجسم في HTTP/2، ولم يتم إرسال جسم الطلب فعليًا في كلتا الحالتين. إذا كانت الردود مختلفة، نفترض أن الخادم الخلفي هو من قام بتحليل الترويسات.
هذه الطريقة هي الأقل موثوقية: قد يصدر الخادم الأمامي أخطاء مختلفة عندما يكون لـ Content-Length قيمة غير صالحة، وعندما لا يتطابق مع الطول الفعلي.
من خلال إرسال أزواج متعددة من الطلبات الصالحة/غير الصالحة، يمكننا تقليل فرصة الإيجابيات الكاذبة العشوائية. على الجانب الآخر، يمكننا التوقف مبكرًا إذا رأينا أنه لا توجد طريقة لفصل الردود على الطلبات "الصالحة" عن الردود على الطلبات "غير الصالحة".
تحاول الأداة استخدام تقنيات تهريب متعددة عن طريق تعديل الترويسة بطرق مختلفة. لا شيء منها جديد وغير واضح في نفس الوقت.
لتهريب ترويسة، نلحق بها مسافة. هذه هي الطريقة الأكثر شيوعًا وتقليدية. نأمل أن الترويسة لا تتم معالجتها بواسطة الخادم الأمامي ولكن يتم إرسالها كما هي إلى الخادم الخلفي، الذي يقوم بإزالة المسافة.
تحاول الأداة مجموعة متنوعة من الأحرف كمسافات: تتضمن , \t, \v, \x00 و Unicode.
لتهريب ترويسة، نستبدل الشرطة (-) بشرطة سفلية (_). إذا كان الخادم الخلفي مستوحى من CGI بطريقة ما، فقد يقوم بتحويل الترويسات مثل Header-Name إلى شكل HEADER_NAME؛ وبالتالي، ستصبح الشرطة شرطة سفلية على أي حال. أثناء تحديد كيفية تحليل الجسم، يفترض أن مثل هذا الخادم الخلفي يطلب قيمة CONTENT_LENGTH / TRANSFER_ENCODING من قاموس الترويسات الخاص به، وستكون موجودة.
هذا خاص بـ HTTP/2. نظرًا لأن HTTP/2 هو بروتوكول ثنائي، يمكننا محاولة إرسال أسطر جديدة في اسم الترويسة أو قيمتها. المعيار يحظر ذلك، لكننا نأمل في العثور على تطبيق لا يزال يقبل ذلك.
أثناء تحويل HTTP/2 -> HTTP/1.1، تنقسم الترويسة إلى ترويسةتين مختلفتين، مما يعني أن الطلب سيبدو مختلفًا للخادم الخلفي.
لتهريب ترويسة، نضع اسمها وقيمتها بعد سطر جديد: ترويسة باسم "Transfer-Encoding" وقيمة "chunked" تصبح واحدة باسم "fake" وقيمة "fake\r\ntransfer-encoding: chunked".
افترض أن الخادم الخلفي يستخدم لغة عالية المستوى ولا يقوم بالتحقق الكافي من الترويسات. في هذه الحالة، قد يقوم بتحويل الأسماء إلى الأحرف الكبيرة قبل القيام بأي شيء آخر ويقوم بذلك باستخدام دوال واعية بـ Unicode. لحسن الحظ، يحتوي TRANSFER-ENCODING على الحرف S، الذي يصبح ſ (\u017f) عند تحويله إلى أحرف كبيرة.
وبالمثل، يمكننا البحث عن خادم خلفي سيحول قيمة Transfer-Encoding إلى أحرف صغيرة: نرسل chunKed بدلاً من chunked مع \u212a بدلاً من K.