Flask SSTI利用方式的探索

Kiritan發表於2021-09-29

Flask SSTI利用方式的探索

一、SSTI簡介&環境搭建

​ 一個統一風格的站點,其大多數頁面樣式都是一致的,只是每個頁面顯示的內容各不相同。要是所有的邏輯都放在前端進行,無疑會影響響應效果和效率,很不現實。把所有的邏輯放在後端,又會導致太過複雜,前輕後重。

​ 模板的誕生是為了將顯示與資料分離,讓前端工作人員專注表現設計,後臺人員注重業務邏輯,同時簡化程式碼的複雜程度。模板技術多種多樣,但其本質是將模板檔案和資料通過模板引擎生成最終的HTML程式碼。

​ Flask使用Jinja 2作為模板引擎。Jinja的語法很簡單,大致有這麼幾種:

​ {%....%}語句(Statements)

​ {f .…H}列印模板輸出的表示式(Expressions)

​ {#....#}註釋

​ #...##行語句(Line Statements)


什麼是SSTI,SSTI會導致什麼問題?

​ SSTI,又稱服務端模板注入攻擊。jinja2模板中使用{{ }}語法表示一個變數,它是一種特殊的佔位符。當利用jinja2進行渲染的時候,它會把這些特殊的佔位符進行填充/替換。但是在進行目標編譯渲染的過程中,執行了使用者插入的惡意內容,因而可能導致了敏感資訊洩露、程式碼執行、GetShell 等問題


​ 測試環境搭建:Ubuntu + Docker

​ 題目:https://github.com/Tiaonmmn/pasecactf_2019_web_honey_shop

​ 微調,增加了SSTI~



二、敏感資訊洩露導致身份偽造

image-20210929193413466

Flask session機制:

image-20210929193650684

​ 通過.隔開的3段內容,第一段其實就是base64 encode後的內容,但去掉了填充用的等號,若decode失敗,自己需要補上1-3個等號補全。中間內容為時間戳,在flask中時間戳若超過31天則視為無效。最後一段則是安全簽名,將session data,時間戳,和flask的secret key通過sha1運算的結果。


SSTI方式

​ f12 → Application → Cookies

image-20210929194411414

​ 這裡我們只需要找到secret key就可以對其簽名,得到一個有效的valid Signature,對其中內容進行替換

​ 檢視robots.txt,看看能不能得到有效資訊

image-20210929195040530

​ 我們再去訪問一下hello,得到Hello guest

image-20210929195116235

​ 這裡我們嘗試用name對欄位進行替換,說明使用者可以通過name欄位傳資訊到後臺,後臺再將資訊渲染到頁面來

image-20210929195206936

​ 我們再進行測試,發現這裡沒有對使用者輸入進行過濾,而是直接執行

image-20210929195738406

​ 我們通過config可以檢視到一些重要的資訊,這裡我們就拿到了secret key

image-20210929200106479

​ 我們在Linux的終端中利用flask-unsign工具,可以進行session的偽造(以修改金錢為例)

flask-unsign --sign --cookie "{'balance':666666}" --secret "獲取的secret key"

​ 回到我們獲取session的位置,將執行結果替換原來的值,再重新整理一下,發現我們的金額成功被修改為我們設定的值

image-20210929201148129


原題解題方式:

​ 原題中圖籤可以下載,我們獲取URL,發現URL通過download?image指明一個引數進行下載,這裡我們可以審查一下有沒有目錄穿越問題

image-20210929201455165

​ 發現passwd可以被下載,說明網站確實存在目錄穿越問題

image-20210929201836508

​ 我們再去下載環境變數,獲取到檔案裡的secret key

image-20210929202706589



三、Flask PIN碼利用

Flask PIN碼機制:

image-20210929224707255

​ Flask應用在Debug模式下提供的一種頁面端的互動除錯工具,和我們平時使用的Python命令列是一樣的,也就是給我們提供了一個互動式的web端shell。但是PIN碼的生成規則是有規律可循的,使得獲取PIN碼成為可能,之後能夠利用的方式有很多。(只有在Debug開啟的情況下)

​ 這裡我們在原來輸入config的位置亂寫,然後頁面報錯

image-20210929225041150


​ 我們如何獲得Flask PIN呢?

import hashlib
from itertools import chain
probably_public_bits = [
    'flaskweb'# username
    'flask.app',# modname
    'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
    '/usr/local/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]

private_bits = [
    '2485377957894',# str(uuid.getnode()),  /sys/class/net/ens33/address
    '3c7c60af8484830ab0b1e9615fada4e74d93a8a111baa4afcd949feeab56c320'# get_machine_id(), /etc/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)
————————————————
版權宣告:本文為CSDN博主「火 柴 人」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/weixin_43536759/article/details/105194333

​ 我們可以通過獲取Flask上的六個值來計算出Flask的PIN

​ 1.username:執行當前Flask的使用者名稱(可通過之前下載的passwd檔案獲取)

​ 2.modename:一般預設值為flask.app

​ 3.getatrr函式的值:一般預設值為Flask

​ 4.Flask的路徑(報錯資訊可以直接檢視)

​ 5.網路地址十進位制

image-20210929231201457

​ 檔案裡為十六進位制值,可通過命令轉換為十進位制

int ("十六進位制值去掉冒號",16)

​ 6.Flask執行機器的機器碼

​ 一般環境在etc/machine-id下,如果報錯,可以去proc/self/cgroup嘗試,這裡docker裡面就是我們要找的值

image-20210929231820532


​ 計算出PIN碼後我們可以回到報錯頁面,點選報錯資訊右邊的小視窗,輸入PIN碼後即可得到一個互動視窗,但在這裡只能得到互動命令的返回值,看不到具體輸出

image-20210929232432125

​ 但我們可以通過popen+read將返回的值顯示出來

image-20210929232625356

image-20210929232813230



四、SSTI導致RCE

​ Python魔法函式+內建函式

​ 所謂魔法函式(Magic Methods),是Python的一種高階語法,允許你在類中自定義函式(函式名格式一般為__xx__),並繫結到類的特殊方法中。比如在類A中自定義__str__()函式,則在呼叫str(A())時,會自動呼叫__str__()(函式,並返回相應的結果。在我們平時的使用中,可能經常使用__init__函式和__del__函式,其實這也是魔法函式的一種。

​ 在python中,輸入可以檢視python內建函式。help(dir(._builtins ))。簡單理解就是Python中自帶的函式,直接拿來用就好了。

image-20210929233848328

​ 這裡我們需要定位到popen的位置,這裡為302,然後就可以直接利用它,再去找到一些可以利用的子類,比如popen和os

image-20210929234156765

​ 發現這裡可以達到直接執行系統命令的操作

image-20210929234413988

image-20210929234542738

image-20210929234607391

相關文章