前言
在8月25日的安恒杯月賽題出現(xiàn)了一道flask debug配合任意文件讀取的題撵孤,當(dāng)時(shí)沒(méi)有搞出來(lái),作為萌新沒(méi)見(jiàn)過(guò)這種題目袍祖,所以賽后經(jīng)過(guò)講解,自己本地復(fù)現(xiàn)了一波谢揪,收益良多蕉陋。
0x01
此漏洞主要是利用Flask在debug會(huì)生成一個(gè)pin碼捐凭。
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)
問(wèn)題出在這個(gè)pin碼的生成,在同一臺(tái)機(jī)子上多次啟動(dòng)同一個(gè)Flask應(yīng)用時(shí)凳鬓,會(huì)發(fā)現(xiàn)這個(gè)pin碼是固定的茁肠。
0x02
漏洞測(cè)試環(huán)境:
windows
python3.6
Flask 1.0.2
0x03
現(xiàn)在來(lái)分析pin碼是如何生成的,本人是用pycharm單步debug缩举,下面只給出重要代碼段垦梆。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hellso World!'
if __name__ == '__main__':
app.run()
跟進(jìn)app.run函數(shù)。
跟進(jìn)run_simple函數(shù)
跟進(jìn)DebuggedApplication類(lèi)
跟進(jìn)__init__函數(shù)仅孩,可以看到兩個(gè)跟pin相關(guān)的參數(shù)
可以看到_get_pin函數(shù)鐘有對(duì)pin賦值的地方托猩。
跟進(jìn)get_pin_and_cppkie_name函數(shù)
看到注釋?zhuān)梢钥隙ㄟ@個(gè)函數(shù)就是跟pin的生成有關(guān),下面貼出源碼辽慕。
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
可以看到京腥,計(jì)算pin碼時(shí),是通過(guò)向本機(jī)取一下東西鼻百,經(jīng)過(guò)md5等操作算出來(lái)的绞旅。
看看debug調(diào)試結(jié)果
分析出六個(gè)值分別位:
wuli丶Decade
flask.cli
DispatchingApp------------------>run函數(shù)所屬的類(lèi)
E:\\1待處理\\123\\flask11\\venv\\Lib\\site-packages\\flask\\cli.py------>根據(jù)報(bào)錯(cuò)信息得出路徑
xxxxx5457141305 ------->某網(wǎng)卡的mac地址的十進(jìn)制
xxxxxx-8fcb-44d5-be62-36049d2db881 分linux、windows温艇、mac因悲,Windows是從注冊(cè)表中取一個(gè)值。
下面改腳本勺爱,在本地計(jì)算出pin值
回到題目本身
由于pycharm版本的原因晃琳,構(gòu)造的六個(gè)值稍微有點(diǎn)不同,下面直接給出路徑和值琐鲁。
username ------>/etc/passwd
中間兩個(gè)一般固定
路徑(根據(jù)報(bào)錯(cuò))
網(wǎng)卡的mac地址一般存在 /sys/class/net/網(wǎng)卡/address
linux---> /etc/machine-id or /proc/sys/kernel/random/boot_id
'ctf'
'flask.app'
'Flask'
'/usr/local/lib/python2.7/dist-packages/flask/app.pyc'
'2485377892354'
''
但是這里有兩個(gè)坑
1卫旱、路徑報(bào)錯(cuò)是pyc。
2围段、/etc/machine-id路徑下的值確實(shí)是空的顾翼。