簡單談談Java Exploit

基於Java Exploit在漏洞研究領域越來越重要的地位,在下本着拋磚引玉的態度,寫下了這篇文章,希望對大家瞭解Java Exploit能有一定幫助。


2.  網頁中的Java

目前主要有兩種方式在網頁中啓動Java代碼: Applet和Java Web Launch。其中Java Web Launch是Java 1.5之後新加入的,而Applet則是早就存在。
記得當年學校教Java的時候,最後大作業就是寫一個功能複雜的Applet。不過現在Applet已經不流行了

在Html里加入如下代碼就可以嵌入Applet:



代碼:
<APPLET CODE="HelloWorldApplet.class" WIDTH=200 HEIGHT=100> </APPLET>

或者如果打包成jar的話:



代碼:
<APPLET archive=”HelloWorldApplet.jar”  CODE="HelloWorldApplet.class"   WIDTH=200 HEIGHT=100> </APPLET>
和Applet相對的,Java Web Launch用來從web上啓動Java Application。需要遵循Java Network Launch Protocol (jnlp)。




3.安全性和Sandbox


看到這裏,大家可能會想,既然我們可以隨意地在html中調用Java小程序,而Java語言的功能又非常強大,那直接寫個包含惡意代碼的Java小程序放到網上,不就相當於掛馬了嗎?
比如,你可能會想寫下如下代碼:



代碼:
import java.awt.*;import java.applet.*;import java.io.*;public class HelloWorldApplet extends Applet {    public void init()  {    try {      Runtime.getRuntime().exec("calc.exe");    } catch (IOException e) {      e.printStackTrace();    }  }    public void paint(Graphics g )  {    g.drawString("Hello World!",5,35);  }}
然後在自己的網頁中加入如下代碼調用這個applet:


代碼:
<APPLET CODE="HelloWorldApplet.class" WIDTH=200 HEIGHT=100> </APPLET>

接着把網頁掛到某個服務器上,開始守株待兔,



呃,出錯了,我們得到了一個AccessControlException,提示沒有權限。

其實Java的設計者早就考慮了安全問題,並提出了Sandbox的概念。
簡單來講這個sandbox的意思就是:Java虛擬機在執行所有系統資源相關的操作(讀寫文件,運行命令,網絡通信。。。)時,都會檢查當前代碼是否有權限來進行這些操作,如果沒有權限,就會拋出異常。
整個Sandbox機制非常複雜,無法用很短的篇幅講清楚,下面只介紹一些要點:



1.  在Java虛擬機中運行的代碼,有受信任(Trusted Code)代碼和不被信任代碼(Untrusted Code)之分。
默認情況下,Java自帶的庫中的代碼都是受信任的代碼,而來自其他地方(比如來自網絡)的代碼是不受信任的。受信任代碼默認可以對任何系統資源進行操作而不受限制,而不受信任的代碼權限很低。
比如我們前面這個例子,由於HelloWorldApplet.class的代碼來自於網絡上,因此它是不受信任的代碼。
於是在試圖創建進程(ProcessBuilder.start()函數)時,Java虛擬機檢查到當前的代碼不受信任,於是拋出一個權限異常。
我們可以通過定義一些手段來讓自己的代碼受信任(比如添加Policy,代碼簽名等等)。

2.  在Java虛擬機進行權限檢查時,會檢查整個調用棧上的代碼,而不是隻檢查當前函數(這裏會有一些例外,如doPrivilaged和AccessControlContext,暫時可以忽略之)。
整個調用棧上只要有任何一個調用來自不受信任的代碼,就判定爲沒有權限。

還是上面這個例子,當最終檢查權限時,調用棧如下:



代碼:
Java.AccessControlContext.checkPermission                   (信任代碼)Java.AccessController.checkPermission                               (信任代碼)Java.SecurityManager.checkPermission                                (信任代碼)Java.SecurityManager.checkExec                                                (信任代碼)Java.lang.ProcessBuilder.start                                                    (信任代碼)Java.lang.Runtime.exec                                                               (信任代碼)Java.lang.Runtime.exec                                                              (信任代碼)Java.lang.Runtime.exec                                                              (信任代碼)HelloWorldApplet.init                                         (非信任代碼)sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run   (信任代碼)Java.lang.Thread.Run                                                                  (信任代碼)

用於我們自己的HelloWorldApplet代碼是不受信任的,於是整個檢查失敗,異常被拋出。

3.  權限檢查代碼是穿插在相關的Java API裏面的,還是我們上面的例子, Runtime.exec調用了ProcessBuilder.start,代碼如下:
代碼:
public Process start() throws IOException {        // Must convert to array first -- a malicious user-supplied        // list might try to circumvent the security check.        String[] cmdarray = command.toArray(new String[command.size()]);        cmdarray = cmdarray.clone();        for (String arg : cmdarray)            if (arg == null)                throw new NullPointerException();        // Throws IndexOutOfBoundsException if command is empty        String prog = cmdarray[0];       SecurityManager security = System.getSecurityManager();        if (security != null)            security.checkExec(prog);        String dir = directory == null ? null : directory.toString();        try {            return ProcessImpl.start(cmdarray,                                     environment,                                     dir,                                     redirects,                                     redirectErrorStream);        } catch (IOException e) {            // It's much easier for us to create a high-quality error            // message than the low-level C code which found the problem.            throw new IOException(                "Cannot run program \"" + prog + "\""                + (dir == null ? "" : " (in directory \"" + dir + "\")")                + ": " + e.getMessage(),                e);        }    }}

注意裏面的SecurityManager.checkExec就是權限檢查代碼了。
SecurityManager是Java安全機制的核心,一個運行中的Java Virtual Machine可以有SecurityManager,也可以沒有,但是一旦設置了SecurityManager就不能再更改。
如果沒有SecurityManager,很多權限檢查都不會發生。如果在本地運行一個Java程序,默認是沒有SecurityManager的,
但是如果是通過瀏覽器啓動一個Applet,那相應的瀏覽器是一定會設置一個SecurityManager。



4.幾種類型的Java Exploit

通過前面的介紹,我們知道由於Sandbox機制的保護,正常情況下是不能用Java程序掛馬做壞事的,於是Java Exploit要做的事情就很明顯了:突破Java Sandbox的保護機制。

設想一下我們現在被關在一個封閉的房子裏,想要逃出去,那麼我們可能可以有兩種思路:
1.直接把牆給砸了。 2.找找看房子裏面有沒有沒關嚴實的門窗,或者地道什麼的。

在已有的Java漏洞中,第一種方法對應於那些針對Java虛擬機實現(主要是包括運行庫)和插件進行exploit的漏洞,典型的有CVE-2009-3867,CVE-2009-3869,CVE-2010-3552,CVE-2010-0886等。
這類漏洞的主要思想是:Java程序雖然運行在虛擬機中,但是整個虛擬機(包括運行時庫)的實現需要平臺相關的本地代碼來支撐(在windows上,就有諸如awt.dll,java.dll等本地代碼)。

如果這些代碼中存在漏洞,並且可以通過java代碼來觸發,我們就可以利用這些漏洞來運行shellcode,此時Sandbox機制就無能爲力了(因爲Sandbox針對的是Java代碼)。

我們來看一個例子,CVE-2009-3867。這是一個棧溢出漏洞,存在於Java MidiSystem類的getSoundbank函數中
我們可以通過傳一個超長的URL來觸發這個漏洞,請看代碼:



代碼:
String str1 = repeat(‘/’, 30200);MidiSystem.getSoundbank(new URL(str1));

Java運行庫中的一個strcpy操作引發了這個漏洞:

非常典型的棧溢出,大家可以自己調試一下。
值得注意的是如果通過IE瀏覽訪問exploit並調試,IE在隔了一段時間得不到響應後會終止Java虛擬機,可以通過在IE的Terminate Process上下斷來防止Java被關閉。


第二種突破Java Sandbox的方法是“繞”:

用Sandbox來保障安全的想法是非常好的,但是人非聖賢,孰能無過,真正到了代碼實現的時候,開發Java的大牛們還是偶爾會出一些小差錯,導致在某些情況下Sandbox機制可以被繞過。

典型的有CVE-2010-0840  和前幾天的CVE-2011-3554。

我個人感覺比起第一類“暴力***Java虛擬機”的漏洞,這類漏洞的危害更大一點。
因爲***者不需要費勁心思考慮如何讓自己的exploit變得穩定,不需要考慮煩人的DEP和ASLR,只要寫一段做壞事的Java代碼就好了。

下面看一下CVE-2010-0840:
大牛的blog已經講的非常詳細了:


http://slightlyrandombrokenthoughts....-cve-2010.html


我這邊總結一下,這個漏洞的核心思想如下:

前面提到過,當Java  Sandbox權限檢查時,會檢查整個棧上的代碼,只要有任何非信任的代碼,檢查就失敗。
而CVE-2010-0840通過構造一個表達式(Expression),該表達式將執行setSecurityManager(null)來關閉Sandbox的安全機制。
調用setSecurityManager將觸發權限檢查,因此如果在我們自己的代碼中直接執行這個表達式是沒有權限的。
但是通過將這個表達式加入一個JList容器,讓Applet的UI線程來執行這個表達式,則可以做到權限檢查時,整個調用棧上都是Java自己的受信任庫代碼。於是成功繞過的Java Sandbox。


5.總結

終於寫完了,希望本文能幫助大家瞭解一些Java安全的相關知識
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章