Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2020-2551 PoC — Oracle Fusion Middleware WebLogic Server 安全漏洞

Source
Associated Vulnerability
Title:Oracle Fusion Middleware WebLogic Server 安全漏洞 (CVE-2020-2551)
Description:Vulnerability in the Oracle WebLogic Server product of Oracle Fusion Middleware (component: WLS Core Components). Supported versions that are affected are 10.3.6.0.0, 12.1.3.0.0, 12.2.1.3.0 and 12.2.1.4.0. Easily exploitable vulnerability allows unauthenticated attacker with network access via IIOP to compromise Oracle WebLogic Server. Successful attacks of this vulnerability can result in takeover of Oracle WebLogic Server. CVSS 3.0 Base Score 9.8 (Confidentiality, Integrity and Availability impacts). CVSS Vector: (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).
Description
CVE-2020-2551 POC to use in Internet
Readme
# Weblogic-CVE-2020-2551-To-Internet
### CVE-2020-2551 POC to use on the Internet

* 测试POC(可用于外网测试)

  python CVE-2020-2551.py [HOST] [IP] 

* 尝试修改codebase进行远程类加载(失败)

  python CVE-2020-TEST.py [HOST] [IP]


# 浅谈 weblogic CVE-2020-2551 漏洞 & 外网POC构造

(首发于安全客,[原文链接](https://www.anquanke.com/post/id/206494))


### 0x00 基础概念

学习这个漏洞需要一些前置知识,比如CORBA与RMI

简单的概述一下:

CORBA是OMG制定的一套技术标准,用于分布式应用,其中用到了IDL进行跨语言支持,客户端与服务端之间用IIOP协议进行通信

RMI是另一种分布式应用技术,在JAVA中可以用JNDI进行简化应用,客户端与服务端使用JRMP协议进行通信,不过在weblogic中RMI使用的是T3协议,关于这个之前也爆出过不少[漏洞](https://blog.knownsec.com/2018/04/weblogic-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9Ecve-2018-2628%E6%BC%AB%E8%B0%88/)

[RMI-IIOP](https://docs.oracle.com/javase/8/docs/technotes/guides/rmi-iiop/tutorial.html#7738)结合了RMI与CORBA各自的优点,通过IIOP协议部署RMI应用

[官方文档](https://docs.oracle.com/javase/8/docs/technotes/guides/rmi-iiop/rmi_iiop_pg.html)也提到:

RMI server objects can use the IIOP protocol and communicate with CORBA client objects written in any language



### 0x01 RMI-IIOP

暂时不提weblogic,先关注一下如何编写一个RMI-IIOP实例:

客户端代码可以参考[Java 中 RMI、JNDI、LDAP、JRMP、JMX、JMS那些事儿(上)](https://paper.seebug.org/1091/#weblogic-rmi)中的[测试项目](https://github.com/longofo/rmi-jndi-ldap-jrmp-jmx-jms/tree/master/rmi-iiop),可以自己编译HelloClient和HelloServer,也可以用测试项目中编译好的

在命令行启动名称服务器(java自带):

```
start orbd -ORBInitialPort 1050
```

命令行开启服务端HelloServer并配置远程调试,关于如何用IDEA进行远程调试,可以参考[这里](https://www.anquanke.com/post/id/201762)开头提到的方法

```
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 HelloServer
```

当然不远程调试直接看结果也行,直接启动

```
java HelloServer
```

命令行开启客户端

```
Java HelloClient 
```

此时会弹出计算器,成功远程调试的话,可以看到如下调用栈

![](https://p1.ssl.qhimg.com/t01229669c9075dbb0a.png)

EvilMessage.readObejct()中执行命令

![](https://p4.ssl.qhimg.com/t01bf4871fd59f1812f.png)

题外话:weblogic的[安装](https://badcode.cc/2018/05/20/WebLogic-%E5%8A%A8%E6%80%81%E8%B0%83%E8%AF%95%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/)与[调试](https://badcode.cc/2018/05/20/WebLogic-%E5%8A%A8%E6%80%81%E8%B0%83%E8%AF%95%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/)文章



那么weblogic中的RMI-IIOP呢?[关于 Java 中的 RMI-IIOP](https://paper.seebug.org/1105/)这篇文章中有提到关于weblogic RMI-IIOP的利用,在它的基础上进行了一些研究,[Using WebLogic’s RMI over IIOP](https://www.oreilly.com/library/view/weblogic-the-definitive/059600432X/ch04s04.html)讲到几种weblogic使用RMI-IIOP客户端的几种方式,包括:

1.独立RMI客户端(配合jndi,不使用weblogic的任何东西)
2.WebLogic客户端
3.J2EE clients
4.CORBA/IDL clients

前两种方式的区别,看来只是JNDI\_FACTORY设置上的区别

![](https://p4.ssl.qhimg.com/t01bbb532965ba915b4.png)

之前研究weblogic T3反序列化的时候,在weblogic上部署过Helloserver应用,有sayhello()方法可以利用,尝试设置两种JNDI_FACTORY调用一下,使用第二种JNDI\_FACTORY成功调用sayHello()方法

![](https://p5.ssl.qhimg.com/t01d32c56e876358e09.png)
那么修改一下weblogic T3协议的[POC](https://github.com/longofo/rmi-jndi-ldap-jrmp-jmx-jms/blob/master/weblogic-rmi-client/src/main/java/com/longofo/weblogicrmi/Payload1.java),其实只是把RMI修改为了IIOP,发现jtaTransactionManager利用链执行成功,向本地jrmplisten发出了jrmp请求

![](https://p3.ssl.qhimg.com/t018f007a1ba77e682f.png)

看一下流量,在remove()方法调用时,发送remove\__java\_lang\_Object请求,流量中有恶意数据,但没有找到aced魔术头

![](https://p5.ssl.qhimg.com/t01b67a89d59b6b6637.png)

猜测在服务端进行了特殊的解析后,再反序列化数据,看一下调用栈,可以看到后半部分的执行链跟前面原生RMI-IIOP的执行链很像,前面是从CDRInputStream.read_value()触发,这里是从weblogic中IIOPInputStream.read_value()触发,(read_value这个点,在[19年的议题](https://i.blackhat.com/eu-19/Wednesday/eu-19-An-Far-Sides-Of-Java-Remote-Protocols.pdf)上也提到了)

![](https://p5.ssl.qhimg.com/t0112ed8892670dee64.png)

这里请求会首先给clusterableServerRef.invoke()处理,根据不同的invoker调用this.invoker.invoke(),然后这里调用了Mejb_dj5nps_HomeImpl_WLSkel.invoke(),因为是“remove”,所以进入case 6分支调用IIOPInputStream.readObject(),在read_value()方法中解析IIOPInputStream数据并触发反序列化,这个就是利用remove()方法的POC

### 0x02 CVE-2020-2551

Lucifaer师傅的分析[文章](https://lucifaer.com/2020/02/25/WebLogic WLS核心组件RCE分析(CVE-2020-2551)/?from=timeline&isappinstalled=0#2-2-Weblogic解析流程)提到了用bind()方法进行利用,这个也是互联网上主流的利用方式,跟一下调用栈

![](https://p4.ssl.qhimg.com/t010c0953eedd2ce880.png)

跟前面一样,这里请求会首先给clusterableServerRef.invoke()处理,根据不同的invoker调用this.invoker.invoke(),这里调用了CobraServerRef.invoke(),然后在 \_NamingContextAnyImplBase.\_invoke()中,因为va1是“bind_any”,所以进入case 0分支,调用IIOPInputStream.read_any()方法,后面还是会调用IIOPInputStream.read_value()触发反序列,之前说在流量中没有看到aced魔术头,是因为在IIOPInputStream中有一套解析方式,IIOPInputStream的hex-value形式如下,其中包含类名和字段信息:

![](https://p3.ssl.qhimg.com/t01aff53927b8153309.png)

最终会调用恶意类的readObejct()方法

![](https://p3.ssl.qhimg.com/t014ca19d4d751f0f0b.png)


看了一下补丁,发现跟2015年T3反序列化利用的[补丁](https://www.tenable.com/security/research/tra-2016-09)的是同个位置

![](https://p3.ssl.qhimg.com/t0181a34517cc5af375.png)

[WebLogic CVE-2020-2551漏洞分析](https://paper.seebug.org/1138/)的测试中,看到CVE-2020-2551过滤的类位置也是在weblogic.iiop.Utils类中

![](https://p5.ssl.qhimg.com/t013774e3aeaf2d19a0.png)

但是在本地测试的时候,weblogic10.3.6打上2015年的补丁,并没有触发isBlacklisted()函数(但是在MsgAbbrevInputStream与InboundMsgAbbrev中都有调用isBlacklisted()进行黑名单验证,奇奇怪怪。。。)

![](https://p1.ssl.qhimg.com/t0159484bec95bae799.png)

这次CVE-2020-2551的补丁,在weblogic.iiop.Utils.LoadClass()中添加了过滤的verifyclassermitted()方法

![](https://p5.ssl.qhimg.com/t014b82504356994d71.png)

黑名单过滤了恶意类,其中包括JtaTransactionManager的父类com.bea.core.repackaged.springframework.transaction.support.AbstractPlatformTransactionManager,这个类是weblogic自带的,十分危险,在看补丁的时候就有一个想法,因为606行的验证是在LoadClass()之后,那如果在加载className的时候进行了类的加载执行恶意静态代码块不就绕过防御了吗?这个后面再说

### 0x03 模拟IIOP协议构造POC

用JAVA程序写的POC有网络问题,就直接打本地weblogic服务可以,打docker容器或者外网机器不行,提到这个问题的分析文章:

[手把手教你解决Weblogic CVE-2020-2551 POC网络问题](https://xz.aliyun.com/t/7498)

[漫谈 WebLogic-CVE-2020-2551](https://paper.seebug.org/1149/)

下面对POC进行调试,可以参考前面remove那个,也可以参考[Y4er](https://github.com/Y4er/CVE-2020-2551)的,前面两篇文章提到两种解决方法:

* 修改weblogic.jar包并重新打包
* 模拟IIOP协议

我都进行了尝试,重新打包weblogic后,会报java.lang.NoSuchMethodError:weblogic.security.subject.SubjectManager.installCESubjectManager错误,但是没有找到解决方案

![](https://p0.ssl.qhimg.com/t01842f4e0d86f44b55.png)

所以就尝试模拟IIOP协议,先在POC下断点调试

![](https://p5.ssl.qhimg.com/t01d9503f5d84a55e54.png)

发现在new InitialContext(env)时,调用EndPointImpl.sendReceive()中发送和接收了两个包

![](https://p4.ssl.qhimg.com/t018879ca6b321f39fb.png)

LocateReply中包含IOR信息,这里要理解[什么是IOR](http://www.pvv.ntnu.no/~ljosa/doc/encycmuclopedia/devenv/corba-index.html),它的作用是,在RMI-IIOP客户端利用IIOP协议与服务端对象进行交互时,用来提供IIOP通信需要的host和port,还有红框部分Object_key用来区分服务端不同的对象

![](https://p3.ssl.qhimg.com/t01fa94e9770504478f.png)

在模拟IIOP协议时,需要重点关注的是Object_key,host跟ip其实并不影响,之前开始测试的时候,是直接把所有的包重新重放一遍,发送resolve_any的时候返回location forward

![](https://p5.ssl.qhimg.com/t01cf7a0af53d9d4116.png)

GIOP的[官方文档](https://docs.oracle.com/cd/E13211_01/wle/wle42/corba/giop.pdf)中讲到location forward时,表示Object_key是会变化的,不同次请求返回的Object_key可能不同(这里说的Object_key就是数据包中的key address),这个Object_key,前面也有说到,是在用IIOP协议时,用来区别跟哪个对象进行通信,这个值需要在LocateReply中动态获取

![](https://p4.ssl.qhimg.com/t01f4568e843c72df80.png)

最后这里没有选择模拟remove(),而是模拟bind()方法发出的IIOP请求,因为请求比较少,看下本地正常利用的数据包

![](https://p2.ssl.qhimg.com/t01b26261d0b5e8a492.png)

发送LocateRequest,接收data,通过正则匹配获取LocateReply中的key address

![](https://p2.ssl.qhimg.com/t01058ed9f3c252983f.png)

手动设置恶意jrmp服务器(rmi://...)地址,间隔1秒发送bind_any包,因为这里的利用链发出的jrmp请求不是用DGCClient,所以不受[JEP290](https://mogwailabs.de/blog/2019/03/attacking-java-rmi-services-after-jep-290/)的影响,可以通过jrmplisten进行利用

![](https://p5.ssl.qhimg.com/t014fef0f53246e55cf.png)

POC在docker环境中测试成功,可以用[vulhub](https://github.com/vulhub/vulhub/tree/master/weblogic/ssrf)这个SSRF环境,设置IP为宿主机,docker成功获取宿主机jrmp请求,具体代码放在[Github](https://github.com/Dido1960/Weblogic-CVE-2020-2551-To-Internet/blob/master/CVE-2020-2551.py)

![](https://p4.ssl.qhimg.com/t012799157906eb1652.png)

### 0x04 验证猜想

前面有提到利用codebase加载远程代码绕过检测的想法,研究过[JNDI攻击](https://www.veracode.com/blog/research/exploiting-jndi-injections-java)的同学应该清楚,codebase可以用来指定远程类的位置,如果codebase可控并且程序允许远程加载类,那么就可以加载远程恶意类执行静态代码块中的恶意代码。

阅读代码可知,weblogic.iiop.Utils.lodaClass()第二个参数表示codebase,这个参数在IIOPInputStream.read_value()中读取,即为var8参数,1659行调用readIndirectingRepositoryId(var8),最终会调用weblogic.iiop.Utils.lodaClass(),要执行1644行和1659行代码,需要(va4r & 1)=1,(va4 & 6)=2,所以var4的值为3

![](https://p5.ssl.qhimg.com/t01160d6c37735efe9f.png)

readIndirectingRepositoryId到getClassFromId的调用栈,最后会执行304行loadclass()


![](https://p5.ssl.qhimg.com/t019cd344213124a196.png)

看一下bind_any数据包,其实就是通过GIOP Header与GIOP Request组合而成的,GIOP Request中又包括key address(与LocateteReply中一致)、ServiceContextList与stub_data,var4的值就是stub_data中的\x7f\xff\xff\x02,所以(va4r & 1)=0,(va4 & 6)=2,不会执行1644行代码设置codebase

![](https://p5.ssl.qhimg.com/t014ab4c0dba18c4fe0.png)

我们通过修改下面第一个框\x7f\xff\xff\x02为\x00\x00\x00\x03,第二个框添加codebase长度与值信息,具体的代码放在[Github](https://github.com/Dido1960/Weblogic-CVE-2020-2551-To-Internet/blob/master/CVE-2020-TEST.py),可以看到还进行了一个对齐操作,这是一个坑点,因为在读取后面类信息前,会判断下一个字节的位置是不是4的倍数,如果不是会忽略掉一些位数,比如下一个字节的位置是1,则会忽略掉3个字节,直接从位置为4的这个字节读起,这个位置是相对整个bind_any包来说的,这里如果字节不是4的倍数,进行补0操作

![](https://p2.ssl.qhimg.com/t017effa14933cb15f1.png)

还有一个问题,看一下之前readIndirectingRepositoryId到getClassFromId的调用栈,中间会经过findClassInfo()函数,在这里,如果已经加载过某个类,类ID信息会保存起来,findClassInfo()时直接返回类信息,不会进入weblogic.iiop.Utils.getClassFromID()函数

![](https://p2.ssl.qhimg.com/t011d7d0b9164fd3d9d.png)

所以在进行测试时,每次都要改一下类名

![](https://p4.ssl.qhimg.com/t0163c76b9f9d201aeb.png)

不管怎样,最后总算是成功模拟IIOP协议修改了codebase值,并执行weblogic.iiop.Utils.getClassFromId()函数。

不幸的是,在获取RMIURLClassFinder的时候返回NULL,RMIEnvironment.getEnvironment().isNetworkClassLoadingEnabled()函数返回false

![](https://p5.ssl.qhimg.com/t0164e10b369c405a5b.png)

原因是ServerMBeanImpl中\_NetworkClassLoadingEnable参数的值为False

![](https://p0.ssl.qhimg.com/t016ddf4a9c673e8717.png)

想要看看在哪个weblogic配置文件中设置了这个参数,但是没有找到。。。

### 总结

其实在学习这个漏洞的过程中,发现需要有很多的前置知识,比如java反序列化,RMI,JNDI那些东西,相关的学习可以参考这个[文章专栏](https://www.anquanke.com/subject/id/206001),虽然最后修改codebase进行利用失败,但还是收获不少。
File Snapshot

[4.0K] /data/pocs/077c582ae355b45c0c709f73d21512e0bc1ea985 ├── [4.0K] CVE-2020-2551.py ├── [5.3K] CVE-2020-TEST.py └── [ 14K] README.md 0 directories, 3 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →