S2-052 遠端程式碼執行漏洞檢測與利用
除錯環境搭建
使用官方的rest-sample即可,下載2.5.12版本的原始碼https://github.com/apache/struts/archive/STRUTS_2_5_12.zip,然後將apps下面的rest-showcase原始碼脫下來。
Eclipse中新建一個maven工程,web.xml,pom.xml和struts.xml如下:
pom.xml
<!-- struts2依賴包 --> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.5.12</version> </dependency> <!-- struts restful 依賴包 --> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-convention-plugin</artifactId> <version>2.5.12</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-rest-plugin</artifactId> <version>2.5.12</version> </dependency>
struts.xml(src/main/resources/下)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <!-- Overwrite Convention --> <constant name="struts.convention.action.suffix" value="Controller"/> <constant name="struts.convention.action.mapAllMatches" value="true"/> <constant name="struts.convention.default.parent.package" value="rest-default"/> <constant name="struts.convention.package.locators" value="action"/> <constant name="struts.convention.result.path" value="/WEB-INF/"/> </struts>
其他的action檔案、jsp檔案複製過來到maven工程的對應目錄即可,右鍵啟動專案,然後瀏覽器可以訪問到:http://127.0.0.1:8080/struts2-052/orders,說明除錯環境搭建成功。
漏洞分析
根據該漏洞發現者文章https://lgtm.com/blog/apache_struts_CVE-2017-9805所述,是一個叫ContentHandler的東西有問題。
在2.5.12原始碼中搜索這個字串:
在struts-plugin.xml配置了很多的bean,這些bean按照content-type進行分類,並唯一指定一個具體的Handler。這些Handler都實現了ContentTypeHandler介面。
從API DOC上描述
Handles transferring content to and from objects for a specific content type
來看,這個ContentTypeHandler實際上是按照Content-type的不同,將請求的資料丟給指定的子類進行處理,具體是怎麼處理的呢,以XStreamHandler為例:
這裡實際上就是把XML和java物件之間進行轉化,比較專業的詞彙叫“marshal“和”“unmarshal”。從以往的例子看,這種情況導致的命令執行也不是一次兩次了,json轉換庫如fastjson,jackson都有過漏洞,
這次換成了Struts2裡的XML的物件轉換。其實就是XStreamHandler的toObject方法中觸發了漏洞,我們就先在這行程式碼下斷點,執行poc之後,會發現斷點生效了。
我們來看看呼叫函式流程資訊:
在Restful模式下,對Action的路由處理是使用Rest系列的程式碼,這裡是ContentTypeInterceptor類呼叫的XStreamHandler方法。我們來看看上層程式碼中的intercept方法:
首先是從HttpServletRequest裡判斷ContentType,可以很清晰的看到,通過ContentType將request的位元組流分發給對應的Handler進行處理。當ContentType為application/xml的時候,
很自然的就分發給了XStreamHandler這個類來處理,這個類沒有進行任何校驗,直接進行了轉換。我們可以用marsshalsec工具來生成payload。
(1)下載原始碼https://github.com/mbechler/marshalsec
(2)maven編譯 mvn clean package -DskipTests
(3)去target目錄下找到jar檔案,執行:
java -cp marshalsec-0.0.1-SNAPSHOT-all.jar marshalsec.XStream ImageIO "calc" > 1.txt
然後將這段XML用POST發給struts2-rest,當然,ContentType要設定為xml的,然後就可以觸發了。當命令中有空格時,提交多個<string>節點即可。
後話
關於如何從XML到命令執行的過程,實際上是Moritz Bechler大神的一個paper,https://github.com/mbechler/marshalsec/blob/master/marshalsec.pdf,這個paper隨著marshalsec工具釋出。
這裡只分析Struts2的漏洞原因,關於XML->RCE過程,大家可以仔細閱讀這個paper進行深入瞭解。
參考
https://lgtm.com/blog/apache_struts_CVE-2017-9805