flask-debug配合文件上传getshell

前言

在8月25日的安恒杯月赛题出现了一道flask debug配合任意文件读取的题,当时没有搞出来,作为萌新没见过这种题目,所以赛后经过讲解,自己本地复现了一波,收益良多。

0x01

此漏洞主要是利用Flask在debug会生成一个pin码。

1
2
3
4
5
6
7
8
E:\1待处理\123\flaskfuxianaaa\venv\Scripts\python.exe -m flask run
* Serving Flask app "app.py" (lazy loading)
* Environment: development
* Debug mode: on
* Restarting with stat
* Debugger is active!
* Debugger PIN: 229-992-815
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

image.png

问题出在这个pin码的生成,在同一台机子上多次启动同一个Flask应用时,会发现这个pin码是固定的。

0x02


漏洞测试环境:
windows
python3.6

Flask 1.0.2

0x03

现在来分析pin码是如何生成的,本人是用pycharm单步debug,下面只给出重要代码段。

1
2
3
4
5
6
7
8
9
10
11
12
from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():
return 'Hellso World!'


if __name__ == '__main__':
app.run()

跟进app.run函数。
image.png
跟进run_simple函数
image.png
image.png
跟进DebuggedApplication类
image.png
跟进__init__函数,可以看到两个跟pin相关的参数
image.png
可以看到_get_pin函数钟有对pin赋值的地方。
image.png
跟进get_pin_and_cppkie_name函数
image.png
看到注释,可以肯定这个函数就是跟pin的生成有关,下面贴出源码。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def get_pin_and_cookie_name(app):
"""Given an application object this returns a semi-stable 9 digit pin
code and a random key. The hope is that this is stable between
restarts to not make debugging particularly frustrating. If the pin
was forcefully disabled this returns `None`.

Second item in the resulting tuple is the cookie name for remembering.
"""
pin = os.environ.get('WERKZEUG_DEBUG_PIN')
rv = None
num = None

# Pin was explicitly disabled
if pin == 'off':
return None, None

# Pin was provided explicitly
if pin is not None and pin.replace('-', '').isdigit():
# If there are separators in the pin, return it directly
if '-' in pin:
rv = pin
else:
num = pin

modname = getattr(app, '__module__',
getattr(app.__class__, '__module__'))

try:
# `getpass.getuser()` imports the `pwd` module,
# which does not exist in the Google App Engine sandbox.
username = getpass.getuser()
except ImportError:
username = None

mod = sys.modules.get(modname)

# This information only exists to make the cookie unique on the
# computer, not as a security feature.
probably_public_bits = [
username,
modname,
getattr(app, '__name__', getattr(app.__class__, '__name__')),
getattr(mod, '__file__', None),
]

# This information is here to make it harder for an attacker to
# guess the cookie name. They are unlikely to be contained anywhere
# within the unauthenticated debug page.
private_bits = [
str(uuid.getnode()),
get_machine_id(),
]

h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, text_type):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
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

return rv, cookie_name

可以看到,计算pin码时,是通过向本机取一下东西,经过md5等操作算出来的。
image.png
看看debug调试结果
image.png

分析出六个值分别位:

1
2
3
4
5
6
wuli丶Decade
flask.cli
DispatchingApp------------------>run函数所属的类
E:\\1待处理\\123\\flask11\\venv\\Lib\\site-packages\\flask\\cli.py------>根据报错信息得出路径
xxxxx5457141305 ------->某网卡的mac地址的十进制
xxxxxx-8fcb-44d5-be62-36049d2db881 分linux、windows、mac,Windows是从注册表中取一个值。

image.png

下面改脚本,在本地计算出pin值
image.png

回到题目本身

由于pycharm版本的原因,构造的六个值稍微有点不同,下面直接给出路径和值。

username ——>/etc/passwd
中间两个一般固定
路径(根据报错)
网卡的mac地址一般存在 /sys/class/net/网卡/address
linux—> /etc/machine-id or /proc/sys/kernel/random/boot_id

1
2
3
4
5
6
'ctf'
'flask.app'
'Flask'
'/usr/local/lib/python2.7/dist-packages/flask/app.pyc'
'2485377892354'
''

但是这里有两个坑
1、路径报错是pyc。
2、/etc/machine-id路径下的值确实是空的。

image.png

image.png
image.png

-------------本文结束感谢您的阅读-------------