安全圈 | 专注于最新网络信息安全讯息新闻

首页

簡單談談java exploit

作者 trentadue 时间 2020-02-27
all

1.  目的

現在安全研究者和各大廠商已經越來越重視Java Exploit,由於一個好的Java漏洞(不是所有的)往往可以不用擔心作業系統平臺以及DEP和ASLR,穩定性遠遠好於一些IE, flash漏洞,囙此也越來越受到一些Exploit Kit(漏洞利用工具箱)的開發者的青睞。下圖是微軟2011上半年安全公告中檢測到的Exploit統計,可以看到Java一直處於領先地位。

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

2.  網頁中的Java

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

在Html裏加入如下程式碼就可以嵌入Applet:

Code:

或者如果打包成jar的話:

Code:

和Applet相對的,Java Web Launch用來從web上啟動Java Application。需要遵循Java Network Launch Protocol (jnlp)。

3.安全性和Sandbox

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

Code:

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:

Code:

接著把網頁掛到某個服務器上,開始守株待兔,泡杯茶,期望所有通過瀏覽器訪問這個頁面的同學都被突然彈出的小算盘嚇了一跳然而人生不如意,十有八九,我們來看看實際效果如何吧:

呃,出錯了,我們得到了一個AccessControlException,提示沒有許可權。其實Java的設計者早就考慮了安全問題,並提出了Sandbox的概念。簡單來講這個sandbox的意思就是:Java虛擬機器在執行所有系統資源相關的操作(讀寫檔案,運行命令,網路通信。。。)時,都會檢查當前程式碼是否有許可權來進行這些操作,如果沒有許可權,就會拋出异常。整個Sandbox機制非常複雜,無法用很短的篇幅講清楚,下麵只介紹一些要點:

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

1.  在Java虛擬機器中運行的程式碼,有受信任(Trusted Code)程式碼和不被信任程式碼(Untrusted Code)之分。默認情况下,Java自帶的庫中的程式碼都是受信任的程式碼,而來自其他地方(比如來自網絡)的程式碼是不受信任的。受信任程式碼默認可以對任何系統資源進行操作而不受限制,而不受信任的程式碼許可權很低。比如我們前面這個例子,由於HelloWorldApplet.class的程式碼來自於網絡上,囙此它是不受信任的程式碼。於是在試圖創建行程(ProcessBuilder.start()函數)時,Java虛擬機器檢查到當前的程式碼不受信任,於是拋出一個許可權异常。我們可以通過定義一些手段來讓自己的程式碼受信任(比如添加Policy,程式碼簽名等等)。2.  在Java虛擬機器進行許可權檢查時,會檢查整個調用棧上的程式碼,而不是只檢查當前函數(這裡會有一些例外,如doPrivilaged和AccessControlContext,暫時可以忽略之)。整個調用棧上只要有任何一個調用來自不受信任的程式碼,就判定為沒有許可權。還是上面這個例子,當最終檢查許可權時,調用棧如下:

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

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

Code:

用於我們自己的HelloWorldApplet程式碼是不受信任的,於是整個檢查失敗,异常被拋出。3.  許可權檢查程式碼是穿插在相關的Java API裡面的,還是我們上面的例子, Runtime.exec調用了ProcessBuilder.start,代碼如下:

3.  許可權檢查程式碼是穿插在相關的Java API裡面的,還是我們上面的例子, Runtime.exec調用了ProcessBuilder.start,代碼如下:

Code:

       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來觸發這個漏洞,請看程式碼:

設想一下我們現在被關在一個封閉的房子裏,想要逃出去,那麼我們可能可以有兩種思路: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來觸發這個漏洞,請看程式碼:

Code:

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

非常典型的棧溢出,大家可以自己調試一下。值得注意的是如果通過IE瀏覽訪問exploit並調試,IE在隔了一段時間得不到響應後會終止Java虛擬機器,可以通過在IE的Terminate Process上下斷來防止Java被關閉。再看CVE-2010-3552,同樣是棧溢出,但是這次的攻擊目標是java的流覽器外掛程式:

再看CVE-2010-3552,同樣是棧溢出,但是這次的攻擊目標是java的流覽器外掛程式:

就是大家喜聞樂見的流覽器外掛程式溢出啦。當docbase這個參數超長時,漏洞被觸發。第二種突破Java Sandbox的方法是“繞”:用Sandbox來保障安全的想法是非常好的,但是人非聖賢,孰能無過,真正到了程式碼實現的時候,開發Java的大牛們還是偶爾會出一些小差錯,導致在某些情况下Sandbox機制可以被繞過。典型的有CVE-2010-0840  和前幾天的CVE-2011-3554。我個人感覺比起第一類“暴力攻擊Java虛擬機器”的漏洞,這類漏洞的危害更大一點。因為攻擊者不需要費勁心思考慮如何讓自己的exploit變得穩定,不需要考慮煩人的DEP和ASLR,只要寫一段做壞事的Java程式碼就好了。下麵看一下CVE-2010-0840:大牛的blog已經講的非常詳細了:

用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。

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

5.總結

終於寫完了,希望本文能幫助大家瞭解一些Java安全的相關知識。