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