日前,Struts2再次爆出安全漏洞,主要影響國內電商、銀行、運營商等諸多大型網站和為數眾多的政府網站。國外安全研究人員日前發現,Apache Struts2在處理CVE-2014-0094的漏洞補丁中存在缺陷,會被輕易繞過,可導致任意命令執行。黑客進而能夠竊取到網站數據,或者對網站進行DDoS攻擊。
4月24日,360網站衛士第一時間添加防御規則,并率先發布臨時解決方案;4月25日下午,Apache官方才發布Struts2漏洞的臨時修復方案;4月25日晚上,360網站安全檢測率先發布“Struts2漏洞檢測”工具。
360網站衛士盤點了近年來曝出的高危Struts2漏洞,并分析了Struts2為什么屢屢出現重大安全漏洞。
4年前就存在Struts2代碼執行問題
Struts2漏洞,這里主要指的是J2EE開源框架struts2出現的命令執行漏洞,危害巨大,可導致遠程執行任意系統命令,進而獲取系統控制權,數據庫控制權,導致信息泄露。所有使用struts2框架開發的系統都受影響。
Struts2的代碼執行問題最早要追溯到2010年,當時來自Google安全Team的Meder Kydyraliev發現可以通過用unicde編碼的形式繞過參數攔截器對特殊字符“#”的過濾,造成代碼執行問題,官方漏洞編號S2-003,我們可以在struts2官方的漏洞公告中看到如下文字,如下圖:
雖然Apache官方給出了利用代碼,但是對卻忽視了這個漏洞的威力,因為官方看到Meder Kydyraliev給出的代碼以為就是一個簡單的bypass,沒有意識到利用此漏洞可以遠程執行任意命令,于是隨意修改了一下過濾規則便草草了之了。
當時Apache官方是這樣修補的,他們用正則將含有“u0023”的請求全部過濾掉。這樣的修復根本沒有起到作用,因為“u0023”在傳遞過程中被轉義為“\u0023”所以正則根本沒匹配上,悲劇的是他們沒有意識到這個問題。
好在官方終于發現了ognl表達式的威力,ognl可以調用java靜態方法,struts2本身就是一個命令執行漏洞,于是他們修改了一些參數進而限制執行java靜態方法。經過研究發現,他們修改了OGNL上下文中一些命名空間中的屬性,比如將#_memberAccess.allowStaticMethodAccess設置為true,#context["xwork.MethodAccessor.denyMethodExecution"]設置為false。但是通過unicde編碼繞過過濾規則的問題依然存在。他們以為這樣便萬事大吉了。
可能是因為Apache冷落了Google安全Team,沒過多久,Meder Kydyraliev大神便發飆了,這次他構造了一個可以遠程執行任意命令的利用代碼提交給Apache官方,因為之前unicode編碼繞過的問題一直存在,所以還是S2-003的bypass方式,只不過這次他給出了利用ONGL調用java靜態函數執行系統命令的方法,并且在他的blog(http://blog.o0o.nu/2010/07/cve-2010-1870-struts2xwork-remote.html)當中給出了詳細的分析,如圖所示:
所以S2-003的修復宣告失敗,此漏洞編號S2-005,CVE-2010-1870。但是官方的修復卻很簡陋,他們重新修改了正則,封堵了Meder Kydyraliev的利用,但是始終沒有從根本上解決問題,他們沒有意識OGNL表達式用八進制編碼依然是可以執行的,比如”u0023”換成八進制的“43”,再次繞過。
這次,Apache官方終于意識到問題的嚴重性,更加嚴謹的改寫了正則,過濾掉了出現, @等字符的請求內容,官方修改的正則表達式,如圖所示:
傷痕累累的struts2
但是struts2的問題依然存在。
大概是2011年,Meder Kydyraliev再次發飆(CVE-2011-3923),他提出新的利用思路,借助action實例中的私有變量的set方法執行OGNL調用java靜態方法執行任意命令。關于這個問題,其實還牽扯出好多web容器的特性,但是危害依然巨大,此漏洞編號S2-009,CVE-2011-3923,而官方依然是通過正則過濾的方式來修復此問題,官方修復如圖所示
此后還出現過S2-013利用struts2標簽執行ognl的方式,但是這些漏洞都相對雞肋,影響范圍也就大打折扣了。
事實上struts2框架底層是利用OGNL表達式來實現的,然而OGNL表達式的功能過于強大,導致可以直接調用java靜態方法。但是官方為了防止在OGNL表達式中直接調用java靜態方法,它在OGNL上下文中內置了幾個命名對象。例如,#_memberAccess["allowStaticMethodAccess"]默認被設置為false,#context["xwork.MethodAccessor.denyMethodExecution"]默認被設置為true。
之前的幾個漏洞使官方意識到他們做的很多限制都白費,比如上面提到的這幾個屬性的值可以利用執行OGNL進行修改,我們不難看出上面提到的這兩個漏洞都會先設置這兩個屬性,然后調用java靜態方法。
一直到2013年,在S2-013被爆出之后,#_memberAccess["allowStaticMethodAccess"]的屬性,使它沒有權限被修改。這樣看似從根本上解決了問題。但是他們忘記了利用java反射類來訪問私有成員變量這種猥瑣的方法。關于反射類這里順帶說一下,在這之前還出現過例如S2-008命令執行,不過這其實是Struts2開發模式的一個特性,嚴格來講不能算是漏洞,只不過在他的處理邏輯當中用戶可以控制執行OGNL,但是默認struts2的開發模式是關閉的,所以此漏洞很雞肋。但值得一提的是一直到2014年pwntesting的一篇分析文章中給出S2-008的利用方式,真正利用java反射類修改前面那兩個屬性的奇技淫巧。
另外,還有另一種方法更加干脆,就是java.lang.ProcessBuilder這個類,隨便new一個實例然后調用start()方法,便達到命令執行的目的。
還有,去年7月份爆出的S2-016命令執行漏洞。關于這個漏洞,其實是DefaultActionMapper類支持以"action:"、"redirect:"、"redirectAction:"作為導航或是重定向前綴,但是這些前綴后面同時可以跟OGNL表達式,由于struts2沒有對這些前綴做過濾,導致命令執行。
一直到最近的S2-020,以及后續的補丁被繞過,又讓傷痕累累的struts2火了一把,但是這次問題不是出在ognl執行上,而是換成了操控容器的classLoader屬性,這個其跟前面提到的S2-009有點類似,因為struts2框架有這樣一個特性,只要接受到用戶提交aa=bb這樣的請求時,就會通過OGNL去執行對應的setaa方法,所以當用戶去提交class.classLoader….這樣的參數時,當然可以操控對應classLoader的屬性,至于classLoader,不同的容器有不同的屬性,能夠控制這些屬性是很危險的,比如tomcat的docBase可以控制根目錄的位置等等。
關于S2-020的修復,官方發揚了他們一貫的作風,繼續使用正則表達式這種“高端“技術過濾用戶請求,可惜還是被攻擊者繞過。
就這個問題來言,想對struts2的開發人員說:還是應該關注一下底層代碼。至于在S2-020補丁被繞過之后的修復,官方還是用正則,但這次他們要從根本上修復框架問題,所以坐等Apache出最終補丁吧。
Apache官方難辭其咎
回顧struts2的漏洞歷史,我們發現官方難辭其咎。
首先,開發人員安全意識不強,雖然采取了基本的安全措施,但是形同虛設;其次,官方修復力度不夠,給人的感覺總像是在敷衍了事,未能從根本上解決問題;再就是,官方的開放精神確實很震撼,竟然直接將漏洞的PoC掛在官網上,這樣給了很多人進一步研究漏洞利用的機會,這個也是導致問題更加嚴重的一個原因。
近兩年關于struts2的攻擊事件頻發,影響包括向移動、電信、聯通、各大網銀、證券等等各大門戶網站,。因為struts2是一款功能非常強大的j2ee框架,特別是對于廣大開發人員,應用非常廣泛。所以一旦struts2出現0day,導致互聯網上出現大面積被入侵、被拖庫,信息泄露等事件。對于此問題,建議相關技術人員應及時更新struts2框架版本。有實力的公司最好自己開發應用框架,避免使用開源框架。
struts2漏洞盤點
影響比較大,利用比較廣泛的struts2漏洞:
2010年S2-005
CVE-2010-1870 XWork ParameterInterceptors bypass allows OGNL statement execution
2012年1月S2-008
CVE-2012-0392 struts2 DevMod Remote Command Execution Vulnerability
2012年1月S2-009
CVE-2011-3923 Struts<=2.3.1參數攔截器代碼執行
2013年5月S2-013
CVE-2013-1966 Struts2 <= 2.3.14 includeParams屬性遠程命令執行漏洞
2013年7月S2-016
CVE-2013-2251 Struts2 <= 2.3.15.1 action、redirect、redirectAction前綴遠程命令執行漏洞
2014年3月S2-020
Struts2 <= 2.3.16 DoS attacks and ClassLoader manipulation
2014年4月S2-021
Struts2 <= 2.3.16.1 bypass patch(ClassLoader manipulation)