1. 程式人生 > >S2-029 遠端程式碼執行漏洞檢測與利用

S2-029 遠端程式碼執行漏洞檢測與利用

概述

struts2 029漏洞已經爆出一段時間,網上有一些相關分析,首先,漏洞確定是出現在OGNL解釋執行的過程,具體漏洞測試poc網上已經有很多,我在2.2.1、2.3.24.1、2.3.25、2.5beta3、2.3.26上面測試都可以執行,不過修改的安全引數稍微不同。

2.2.1只需要下面幾個:

#_memberAccess['allowPrivateAccess']=true

#_memberAccess['allowProtectedAccess']=true

#_memberAccess['allowPackageProtectedAccess']=true

#_memberAccess['allowStaticMethodAccess']=true

2.3.24.1和2.5beta3則需要:

#_memberAccess['allowPrivateAccess']=true

#_memberAccess['allowProtectedAccess']=true

#_memberAccess['excludedPackageNamePatterns']=#_memberAccess['acceptProperties']

#_memberAccess['excludedClasses']=#_memberAccess['acceptProperties']

#_memberAccess['allowPackageProtectedAccess']=true

#_memberAccess['allowStaticMethodAccess']=true

2.3.25和2.3.26 則需要

#_memberAccess['excludedPackageNames']=#_memberAccess['acceptProperties']

#_memberAccess['allowPrivateAccess']=true

#_memberAccess['allowProtectedAccess']=true

#_memberAccess['excludedPackageNamePatterns']=#_memberAccess['acceptProperties']

#_memberAccess['excludedClasses']=#_memberAccess['acceptProperties']

#_memberAccess['allowPackageProtectedAccess']=true

#_memberAccess['allowStaticMethodAccess']=true

話說官方說的升級到2.3.25就沒事了是咋回事?

分析

下面就對2.3.25進行跟蹤分析一下。

首先官網還沒有2.3.25的編譯版本下載,有的只是beta版。而beta版的程式碼經過對比,發現還是有很多與2.3.25不同的,故github 2.3.25原始碼,然後mvn install -Dmaven.test.skip=true編譯。

編譯好之後,部署測試程式。Poc測試程式碼如下:

這裡先說明一下,漏洞的根源是呼叫findString、findValue等函式。所以切換到
/struts-STRUTS_2_3_25/core/src/main/java/org/apache/struts2/components下面,對這裡的原始碼進行查詢findValue引數,可以發現很多類都有呼叫

下面我們對textfield這個標籤舉例進行跟蹤分析。Textfield有很多屬性,

我們先看下size屬性,通過findString查詢size,findString的定義如下:

可以看到它實際呼叫findValue(expr,String.class),定義如下:

而這個函式判斷傳入的如果是String類的話,會呼叫TextParseUtil.translateVariables,先解析字串後在去執行expr,否則直接執行expr。getStack().findValue()就是程式碼被解釋執行的位置,這時候的expr的%{}會被去掉。

這裡注意到一個問題,就是相對2.3.25之前的版本,此處多了一個ComponentUtils.containsExpression(expr)判斷,該函式是檢測expr表示式中是否同時包含”%{“和”}”,如果是,返回真。也就是檢測傳入的字串是不是標準的OGNL表示式。這個判斷可能會有點用。比如,如果我們想通過size引數帶入執行程式碼,如果我們直接寫size=”expr”,那麼expr進入findValue(expr,String.class)後過不了ComponentUtils.containsExpression(expr)的判斷,什麼都不執行直接返回。想要執行,必須寫成%{expr}。其他用途還沒看出來。

下面還要說下readonly屬性,這個屬性是布林型,而findValue(expr,String.class)中如果不是String型別的話,不需要%{}就可以直接執行,也就是說readonly=”expr”就會被執行。

上面說的是OGNL程式碼的正常執行過程,程式碼只被執行一次。而這次漏洞報的是雙重執行,也就是expr在一次請求中存在被解釋執行兩次的情況。這個情況在i18n原始碼中存在,具體細節請參考http://www.freebuf.com/vuls/99432.html。通過圖2,可以發現在UIBean中也存在這樣的情況。進一步查詢,可以發現可疑情況:

其中UIBean中有三處出現對name屬性進行執行的操作,檢視程式碼邏輯發現果然有問題:

其中就有TextField。而UIBean的屬性也不少。其中的name和value值得關注。

先是

得到name的string,之後到這裡

會先嚐試獲取value的值,如果value為空,那麼就二次解釋執行了name。並且在執行前給name加上了”%{}”。最終造成二次執行。

而UIBean被很多類繼承:

類似的textarea、label、hidden等都存在二次執行問題。

另外在2.3.25中多了#_memberAccess['excludedPackageNames']

所以必須將其清空,我們的payload才能執行。

結論

該漏洞可利用性比較困難,能夠雙重執行的屬性位於name中,而我們通常可以傳參的地方是value(不排除哪個程式設計師可以讓你操控name屬性值,同時你的value值還為空的情況),查詢value的執行情況:

出現在Param.java和UIBean.java中,而閱讀程式碼後,發現這兩次執行都是位於不同分支中,不能造成二次執行的情況。

所以此次漏洞危害不算大,不過就是官方說的修復有點不理解。最後給出我測試的2.3.25和2.3.26的完整war包,測試uri:http://localhost:8080/2.3.25/http://localhost:8080/2.3.26/

執行的命令是touch /tmp/fuckp4e。部署並訪問uri後,如果出現此檔案,說明存在漏洞。