struts2最近量產漏洞分析
可能是由于溝通問題,導致struts2官方對我提交的S2-012漏洞名稱理解錯誤,漏洞描述為struts 2的某個示例應用出現(xiàn)漏洞,但是struts2是按照框架出現(xiàn)漏洞修補的。而這個s2-012竟然引發(fā)了一連串血案。
其實發(fā)這篇文章,我非常惱火,任誰手里有一個0day,捂了半天,結果又被別人公開,都會非常惱火。去年我在XCON發(fā)布的S2-012漏洞,其實struts 2還存在相似的漏洞。在struts中,框架接收到的用戶輸入,除了參數(shù)、值以外,還有其他地方,比如文件名。這個漏洞,是struts2對url中的文件名做了解析,導致的ognl代碼執(zhí)行。
這中間存在一些技術細節(jié),下面展開分析。
enableOGNLEvalExpression的騙局
從漏洞公告上看到這個詞,很容易認為是struts 2把ognl表達式干掉了,可以選擇終結一切。事實上禁止的是OGNL的其中一種調用方式,而這種調用方式,也只是在S2-013這里調用。
struts2有另外一段威武的代碼,真正在防守這個漏洞。
org.apache.struts2.views.util.DefaultUrlHelper這個類:
原本是這樣寫的,代碼走到translateAndEncode就會調用ognl執(zhí)行,它的邏輯一共包括ognl的translate,以及urlencode這兩個功能。
補丁之后,這里被改為僅僅urlencode,不再做ognl執(zhí)行。這個和enableOGNLEvalExpression沒有任何關系。
我沒有細看內容,只看方法名的變動,就感覺可以洗洗睡了,不必往下跟進了。
allowStaticMethodAccess騙局
一直以來allowStaticMethodAccess是struts2的poc標配,從第一個poc出現(xiàn)開始,就一直存在。
在2013年5月27日,也就是前幾天,大家可以自行查看SVNlog,struts2做了一件很猥瑣的事情,把以下代碼刪除了:
這個動作直接導致一個結果,以后在OGNL的POC中執(zhí)行
#_memberAccess["allowStaticMethodAccess"]=true
一定會報錯的,因為沒有set方法了。
很有終結一切的意思,就像以后有新的OGNL漏洞,就不能寫這一句了。但是我可以繞過這個東西,下面結合s2-015漏洞做個示例。
struts2框架s2-015吐槽
這個漏洞,被人公布出來,實際上,發(fā)布者一共發(fā)布了幾個漏洞,包含S2-015、以及S2-012。具體地址在
https://communities.coverity.com/blogs/security/2013/05/29/struts2-remote-code-execution-via-ognl-injection
非常詳細,某同事認為他比我分析的好,所以我就不寫翻譯了,大家自己看。
后來仔細想了想,猜測老外可能遇到s2-012,導致了該文章的發(fā)布,當然,這只是我的個人YY。
發(fā)布者手握2個0day,很不幸,我也有這兩個0day,去年xcon發(fā)布了一個,之后提交了官方,但是他不知道,因為官方到今年才公開修補。
前幾天官方突然公開修補了我發(fā)布的一個0day,這個老外看到s2-012后,可能也非常惱火,因為這個漏洞和他手頭分析的0day剛好相同,所以一怒之下和其他0day一起發(fā)出來了,共同組成一篇文章。可以看到,發(fā)布者直接從blog發(fā)布,之后官方才收到消息開始修補。
這個漏洞的觸發(fā)代碼展現(xiàn)形式和s2-012非常像,所以理解了s2-012后,可以聯(lián)想到這個0day,很容易通過測試出來,我當時也是看到類似的使用,隨手測試就發(fā)現(xiàn)了。相信有很多漏洞,都是類似的情況下發(fā)現(xiàn)的。甚至可能不止我們手里有,其實你也非常惱火。
S2-015的poc在老外的文章中如下:
http://127.0.0.1:8080/struts2-blank/example/$%7B%23context
['xwork.MethodAccessor.denyMethodExecution']
=%21%28%23_memberAccess['allowStaticMethodAccess']=t
rue%29,%28@java.lang.Runtime@getRuntime%28%29%29.exec
%28'touch%20aaa'%29.waitFor%28%29%7D.action/
由于POC中存在#_memberAccess["allowStaticMethodAccess"]=true,所以發(fā)布者提到升級到s2-014可以緩解。
其實發(fā)布者誤解了,但是struts2開發(fā)者沒有誤解,所以趕緊推出了S2-015。
但是如果不講出來,你還是會發(fā)現(xiàn)那個POC在S2-014之后其實不能打,就如老外文章中所說,被緩解了。那要怎么打呢?
OGNL的POC有個小技巧
這個東西的含義,是允許靜態(tài)方法執(zhí)行,那么官方禁止修改這個設置,意思就是永遠禁止靜態(tài)方法執(zhí)行。
因為POC中的“@java.lang.Runtime@getRuntime”其實就是在執(zhí)行靜態(tài)方法,所以才一定要開啟靜態(tài),但是這只是java代碼的一種寫法罷了。
我們可以用另一個寫法,繞過這個限制。
(new java.lang.ProcessBuilder('calc')).start()
這段代碼中,沒有調用任何靜態(tài)方法,僅僅是new一個對象,之后執(zhí)行其中一個動態(tài)方法,所以不必allowStaticMethodAccess一樣能達到執(zhí)行系統(tǒng)命令的效果。
這個小技巧,可以干很多事情。
1、可以繞過某些WAF,我不告訴你是哪些,免得你拿去騙獎品。
2、可以為以后新的OGNL代碼執(zhí)行鋪路,避免0day來了,我們居然因為這個不會寫POC。
S2-015的修補
簡單說一句,這里沒有什么研究價值,這次修補,官方采用了限制action的名稱,只能
[a-z]*[A-Z]*[0-9]*[.\-_!/]*
總結struts2出現(xiàn)過的ognl表達式輸入點
1、request參數(shù)名
2、request參數(shù)值
3、request文件名
4、request的cookie名
5、respose的body
慘不忍睹,好像HTTP頭基本都出了問題,也沒剩下多少了。一個流行框架,能夠在這么多地方出現(xiàn)遠程代碼執(zhí)行,真是難為struts開發(fā)者了。
同時也問一下使用struts的同學們,你們這些年,是怎么過來的?
在阿里巴巴,我時常分析struts2漏洞然后發(fā)報告,有時候會是0day,那就要出個補丁給各個項目用,最后等到官方發(fā)布補丁時,我們再評估是否需要重新更新回去。導致我們時常勸說開發(fā)人員盡量不要使用這個框架,尤其是項目初期評審時,發(fā)現(xiàn)struts2,深惡痛絕,說很多很多話用于嚇唬開發(fā)人員。
在這種趨勢下,我對這個東西已經再無任何僥幸心理,決定推出一個虛擬補丁。至于阿里的真實方案,我肯定不能告訴大家,但是可以講講思路。
統(tǒng)一防御方案
首先升級到最新版本。
在ognl這個語言的入口,加入攔截代碼,一旦發(fā)現(xiàn)危險調用,直接干掉。
代碼原理是,在OGNL執(zhí)行之前,對語句做判斷,看到有黑名單的代碼,就干掉。理論上,開發(fā)人員理論上不會自己寫OGNL用于操作文件,執(zhí)行命令等,他們最多從session中取一個值,或者在頁面上取一個值。
覆蓋掉Ognl.Ognl類,添加如下代碼:
加入QQ郵箱呢?具體原因不說,只說結果,結果是,我的郵箱可以收到0DAY,你如果真的看懂了,自己猜猜原因?