Server Side Template Injection (SSTI)
السلام عليكم ورحمة الله وبركاته
أمور يجب عليك معرفتها قبل قراءة هذه المقالة:
- معرفة ضرورية: ثغرة SSTI (Server-Side Template Injection) ، أرشح لك هذه المقالة العربية
- ليست ضرورية لكن جيدة: معرفة بسيطة في Docker حيث إن المعمل الذي سنقوم بالتعامل معه عبارة عن Docker Container ، أرشح لك وبشدة هذه السلسلة العربية
في هذه المقالة لن نتطرق لشرح الثغرة نفسها، إنما سنحاول التركيز على الجانب التطبيقي لهذة الثغرة. تحديدًا سنقوم بنقاش هذه النقاط:
- تثبيت التطبيق المُصاب
- إكتشاف الثغرة وتحديد الـ parameter المُصاب
- إكتشاف نوع الـTemplate Engine المستخدم في تطبيق الويب
- إستغلال الثغرة
التطبيق المُصاب الذي سيتم الشرح عليه تجده هنا
لنبدأ بسم الله
تثبيت التطبيق المُصاب
بعد تثبيت Docker في جهازك قم بتنفيذ التعليمة التالية والتي ستقوم بعمل fetch لـلـ docker container
sudo docker pull dockerbucket/ssti_env
والتعليمة التالية ستقوم بتشغيل الـ Docker container وبدء التطبيق على المنفذ 60
sudo docker run -p 60:60 dockerbucket/ssti_env python /root/vulncode.py
إكتشاف الثغرة وتحديد الـ parameter المُصاب
بعد تشغيل التطبيق سنجد أن واجهة التطبيق تحتوي على خانة بحث
كما ذكر @u0pattern_cs في مقالته بامكاننا اتباع المنهجية المذكورة في الصورة التالية لاكتشاف ثغرة الـ SSTI
سنقوم دائمًا بتتبع السهم الأخضر، إلا في حالة الفشل سنقوم باتباع السهم الأحمر
سنرى أن أول payload يفترض أن نبدأ بها هي
${3*2}
سنحاول تمرير عملية حسابية بسيطة 2 * 3 لنختبر هل سيقوم التطبيق بتنفيذها؟ في حال كان التطبيق مُصاب سيقوم بتنفيذ العملية.
الـ ${ } هي لملائمة الـ Syntax فقط
وقبل تمرير الـ request للخادم لو قمنا بعمل interception سنجد أننا نقوم بالحقن في المتغير username
بعد ارسال الطلب للخادم سنجد الـ response الآتي :
حتى الآن يبدو أن التطبيق غير مُصاب ، لأنه كما نرى الخادم قام بارجاع نفس الـ payload بدون تنفيذ العملية الحسابية
لننتقل للاختبار التالي، سنقوم الآن بتمرير الـ payload التالية
بعد ارسال الطلب للخادم سنجد الـ response الآتي :
ممتاز!! قام الخادم باجراء العملية الحسابية ولتأكيد وجود الثغرة لا يزال أمامنا حالة اختبار اخرى وهي تمرير الـ payload التالية
بعد تمرير الطلب سنجد الرد الآتي من الخادم
من النتيجة أعلاه نستطيع التأكيد أن التطبيق مُصاب بثغرة SSTI حيث أن التطبيق لا يزال يقوم بتنفيذ التعليمات التي نقوم بتمريرها ( التطبيق قام بطباعة 2 ثلاثة مرات )
وتحديدًا الـ parameter المُصاب هو username
إكتشاف نوع الـTemplate Engine المستخدم في تطبيق الويب
بعد أن تأكدنا أن التطبيق مُصاب، نحن الآن بصدد تحديد نوع الـ Template Engine المُستخدم في تطبيق الويب، ونطاق الاختبار ( Test Cases) أصبح كالآتي :
تقترح علينا المنهجية التي نتبعها أن التطبيق قد يكون Jinja2 ( خاص بـ Python ) أو Twig ( خاص بـ PHP )
سنبدأ أولًا في اختبار حالة Jinja2 ولأنه مخصص للغة بايثون سنقوم بتمرير الـ payload التالية :
يبدو أن الأمور بدأت تتعقد قليلًا الأن ، لنتوقف لبضع دقائق ونحاول نفهم ما الذي تقوم هذه الـ payload بتنفيذه،
وحتى نفهم هذا نحن بحاجة للعودة الى الـ Python Environment والتعامل معاها مباشرة،
الصورة التالية توضح أني قمت ببدء interactive shell مع الـ docker container المثبت به التطبيق، ومن ثم قمت ببدء python2 console
الأن نحن في داخل بيئة بايثون ، لنبدأ بتجزئة الـ payload وتنفيذ كل جزء على حدى، حتى نفهم العائد النهائي منها
- ‘’ : عبارة عن string
- __class__ : الـ __class__ عبارة عن attribute تقوم باعادة اسم الـ class الذي ينتمي له الـ object وفي حالتنا العائد سيكون str class كما نرى
- __mro__ : الـ __mro__ ستقوم باعادة قائمة الـ inherited classes لهذا الـ object وفي حالتنا نرى أننا قمنا بتمرير الـ index رقم 2 ، والغرض من ذلك أننا نريد استرجاع عنصر معين من هذه القائمة ( وهو object class )
- __subclasses__() : الـ __subclassess__() عبارة عن method ستقوم بارجاع قائمة بالـ classes التي تم عمل loaded لها في بيئة التطبيق الحالية كما نرى في الصورة التالية
اذًا ما الذي تقوم بفعله الـ payload كاملة؟ بشكل مُختصر جدًا تقوم بعمل dump للـ classes names التي نستطيع الوصول لها ضمن بيئة التطبيق
ولماذا إذًا كل هذا التفصيل والتعقيد ؟ سنرى لاحقًا أن فهم آلية عمل مثل هذه الـ payload سيساعدنا في بناء payloads أخرى معقدة لحالات معينة،
اضافة الى أنه قد تختلف الـ classes التي نستطيع الوصول لها من تطبيق الى تطبيق،
ففهم كيف نتحرك ضمن الـ Python Inheritance Hierarchy سيساعدنا في الوصول الى أي class ومن ثم نداء دواله الخاصة
قبل أن نستكمل أود فقط الاشارة بأن هذه الـ payload مخصصة فقط لـ python2 وفي حال أردت استخراج أسماء الـ classes في بيئة python3 سنمرر الـ index رقم 1 للـ __mro__ كالآتي :
بعد فهمنا للـ payload قمنا بتمريرها للتطبيق كالآتي ، ونلاحظ أننا نجحنا في عمل dumping للـ classes names
نلاحظ بعد استخراجنا لقائمة الـ classes أنه يوجد من ضمنها classes خاصة بـ Jinja2 ..
اذًا في هذه المرحلة نستطيع القول الأن أن الـ Template Engine الذي نتعامل معه هو Jinja2 وليس Twig
الأن لنستكمل آخر جزء في هذه المقالة
إستغلال الثغرة
قراءة /etc/passwd
نستطيع استخدام دالة read() من File class في بايثون حتى نقرأ ملف ما، الـ payload التي تقوم بالوصول للـ File class كالآتي
لاحظ أننا مررنا الـ index رقم 40 لدالة subclasses .. هذا الـ index خاص بـ File class ( لا أعتقد أنه قد يكون رقم ثابت في جميع التطبيقات، لذلك عملية استخراج أسماء الكلاسات مهمة)
كيف حصلنا على الـ index ؟ من خلال قائمة الـ classes التي عملنا لها dump .. وبعد ذلك حللنا المخرج وعرفنا الـ index الخاص بالكلاس
والـ payload التي تمكننا من قراءة ملف الـ /etc/passwd كالآتي
بعد تمرير هذه الـ payload للتطبيق نجد أننا استطعنا قراءة الملف بنجاح
تنفيذ ls
في بايثون حتى نستطيع تنفيذ bash commands نستعين بمكتبة os تحديدًا دالة popen() (هذه طريقة من ضمن طرق أخرى متاحة)
لكن بعد مراجعة قائمة الـ classes التي استخرجناها من التطبيق لم نجد هذه المكتبة .. فكيف سنصل لها ؟
ضمن بيئة بايثون الطبيعية عادةً نقوم باستخدام import ونستدعي المكتبة .. فهل بالامكان تطبيق نفس هذه الفكرة هُنا ؟
الاجابة نعم .. وبتفصيل أكثر اطلع على هذا المشروع الذي قام بشرحه أحد الباحثين لطلابه ( المشروع بالمناسبة يقوم بحل نفس التطبيق المُصاب)
بشكل مختصر جدًا الفكرة التي اقترحها الباحث هي استخدام دالة catch_warnings() حتى نصل الى الـ import statement ومن ثم نستطيع عمل import لمكتبة os
الـ payload التي تلخص الخطوات التي اقترحها الباحث كالآتي
ولو قمنا بتمريرها سنجد أننا استطعنا تنفيذ الـ ls
Reverse Shell
بامكاننا استخدام نفس الطريقة السابقة لكن عوضًا عن تمرير ls لدالة popen() سنقوم بتمرير هذا الـ one-line python script
وستكون الـ payload النهائية كالآتي
وبعد تمريرها سنحصل على الـ shell session
ختامًا، أود الإشارة بأن هذه المقالة تمّت كتابتها خلال دراسة هذه المواضيع، فكل ما تم ذكره هنا قد يحتمل الخطأ، لكن بالإمكان العودة إلى المراجع التي إستندت عليها هذه المقالة
وإن أصبت فمن توفيق الله وحده، وإن أخطأت فمن نفسي والشيطان
المراجع:
مقالات:
- https://3alam.pro/1337r00t/articles/ssti
- https://bowneconsultingcontent.com/pub/EH/proj/ED105.htm
دورات:
- AWAE, Offensive Security