漏洞分析 | U8 Cloud ServiceDispatcher反序列化漏洞及补丁分析

news2024/11/27 16:27:29

0x01 概述

近期,爆出了 U8cloud ServiceDispatcherServlet 接口的反序列化漏洞。在对该漏洞进行分析时,我们发现 NC 也曾出现过 ServiceDispatcherServlet 接口的反序列化漏洞。经过分析后发现,这两个漏洞的功能代码实现方式并不相同。但二者都实现了自定义的序列化过程(本文简称 NetObjectStream),并且对传输的序列化流进行了合法性检测。因此,该漏洞的利用条件需要的自定义序列化类生成序列化数据进行攻击。
在漏洞分析过程中,我们发现并验证了其他接口也使用了自定义的序列化类(NetObjectStream)进行反序列化解析数据,存在反序列化漏洞(已报送给国家漏洞监管单位和厂商进行修复)。
在这里插入图片描述

0x02 补丁分析

官方的修复方式是通过增加序列化对象的限定类来防止反序列化漏洞。具体是在 NetObjectInputStreamNCObjectInputStream 类中增加了新的构造方法,在构造方法中增加了 InvocationInfo.classESBContextForNC.class 两个白名单限制类。

未修复
NetObjectInputStream objIn = new NetObjectInputStream(new ByteArrayInputStream(bytes));

修复后
NetObjectInputStream objIn = new NetObjectInputStream(new ByteArrayInputStream(bytes), new Class[]{InvocationInfo.class, ESBContextForNC.class});

NetObjectInputStreamNCObjectInputStream 类中增加了新的构造方法 [1],将上述的两个限定类传入到私有变量 classes 中用于检测处理。

public NetObjectInputStream(InputStream in, Class[] classes) throws IOException {
    this.bufferSize = 1024;
    ObjectResolver resolver = new NCObjectResolver();
    this.objIn = new NCObjectInputStream(new NCInputStream(in), resolver, classes); // [1]
}

NCObjectInputStream 方法中将 classes 传入了 this.classes 变量中,即将 InvocationInfo.class, ESBContextForNC.class 两个限定类传入到 this.classes [2] 中。

由于传入的 resolver 是个对象,所以会调用 this.enableResolveObject(true); 该方法的作用是当调用 enableResolveObject(true) [3] 时,在反序列化过程中通过调用 resolveObject() 方法来解析特定类型的对象。

public NCObjectInputStream(InputStream in, ObjectResolver resolver, Class[] classes) throws IOException {
    super(in);
    this.check = false;
    this.resolver = resolver;
    this.classes = classes; // [2]
    if (this.resolver != null) {
        this.enableResolveObject(true);  // [3]
    }
}

由于开启了 enableResolveObject(true) ,当序列化对象在调用 readObject() 方法中,默认调用其 resolveObject() 方法。

方法内部通过 isAssignableFrom() 方法 [3] 判断传入的字节流是否为 InvocationInfo.class, ESBContextForNC.class 的实现类或子类,如果不是,则会抛出异常,导致字节流不能够被反序列化,从而避免了反序列化漏洞的产生。

protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
    try {
        Class superImpl = super.resolveClass(desc);
        if(classes!=null&&classes.length>0){
            if(this.check) return superImpl;
            boolean legal = false;
            for(Class c:classes){
                if(c.isAssignableFrom(superImpl)){  // [3]
                    legal = true;  // [4]
                    break;
                }
            }
            if(legal){
                this.check = true;
                return superImpl;
            }else{
                throw new IllegalArgumentException("####### class::" + superImpl);
            }
        }
        return superImpl;
    } catch (ClassNotFoundException nfe) {
      ...
    }
}

0x03 路由分析

U8cloud 在 web.xml 文件中配置 ServiceDispatcher 的 servlet 映射,URL路由为 /ServiceDispatcherServlet,servlet 命名为 CommonServletDispatcher

<servlet-mapping>
  <servlet-name>CommonServletDispatcher</servlet-name>
  <url-pattern>/ServiceDispatcherServlet</url-pattern>
</servlet-mapping>

跟进名为 CommonServletDispatcher 的 servlet ,发现该 servlet 映射到了 nc.bs.framework.comn.serv.CommonServletDispatcher 类中,并在初始化中定义了一个 service 参数 [5] ,该参数映射到了 nc.bs.framework.comn.serv.ServiceDispatcher 类[6]中。

<servlet>
  <servlet-name>CommonServletDispatcher</servlet-name>		
  <servlet-class>nc.bs.framework.comn.serv.CommonServletDispatcher</servlet-class>
  <init-param>
    <param-name>service</param-name>  // [5]
    <param-value>nc.bs.framework.comn.serv.ServiceDispatcher</param-value>  // [6]
  </init-param>
  <load-on-startup>10</load-on-startup>
</servlet>

0x04 漏洞分析

CommonServletDispatcher 类继承了 HttpServlet 接口并实现了 init()destroy()doGet()doPost() 方法。在 init() 方法中通过 web.xml文件定义的 service 参数获取对应的 nc.bs.framework.comn.serv.ServiceDispatcher 类 [7] 并进行实例化 [8]。

public class CommonServletDispatcher extends HttpServlet {
  private ServiceHandler serviceHandler = null;
     public void init() throws ServletException {
        String targetname = null;
        Throwable cause = null;
        this.log.debug("ServletDispatcher.initing......");
        if ((targetname = this.getInitParameter("service")) != null) {  // [7]
            try {
                Class handlerClass = Class.forName(targetname);
                this.serviceHandler = (ServiceHandler)handlerClass.newInstance();// [8]
            } 
          ...
        }
        ...
    }
}

doGet 方法中定义了 label118 的循环体,循环中通过 this.serviceHandler.execCall(request, response); 调用了上文中已经实例化后的 nc.bs.framework.comn.serv.ServiceDispatcher 类的 execCall 方法并传入 requestresponse

this.serviceHandler.execCall(request, response);

跟进 ServiceDispatcher 类的 execCall() 方法,通过调用本类中的静态方法 readObject() [9] 对传入的 request.getInputStream() 序列化流数据进行反序列化操作。

int[] lsizes = new int[1];
long wBeginTime;
try {
    ThreadTracer.getInstance().beginReadFromClient();
    wBeginTime = System.currentTimeMillis();
    invInfo = (InvocationInfo)readObject(request.getInputStream(), streamRet, lsizes);  // [9]
}

跟进 ServiceDispatcher 类的 readObject() 方法,该方法首先使用 BufferedInputStream 读取流数据,通过 NetObjectInputStream 类的 readInt 方法对流数据进行移位运算解析得到流的字节长度,对比 BufferedInputStream 类中读取的长度来判断是否为一个正常的可以被 NetObjectInputStream 解析的反序列化流数据。

如果判断是一个可以被 NetObjectInputStream [10] 读取的流,则读取该流并进行 readObject() [11] 反序列化操作,这时就可以通过构造利用链进行反序列化攻击。

public static Object readObject(InputStream in, boolean[] retValue, int[] lsizes) throws IOException, ClassNotFoundException {
  BufferedInputStream bin = new BufferedInputStream(in);
  int len = NetObjectInputStream.readInt(bin);
  byte[] bytes = new byte[len];
  int readLen = bin.read(bytes);
  int tmpLen;
  for(lsizes[0] = readLen; readLen < len; readLen += tmpLen) {
    tmpLen = bin.read(bytes, readLen, len - readLen);
    if (tmpLen < 0) {
      break;
    }
  }
  if (readLen < len) { // [10]
    throw new EOFException("ReadObject EOF error readLen: " + readLen + " expected: " + len);
  } else {
    NetObjectInputStream objIn = new NetObjectInputStream(new ByteArrayInputStream(bytes));  
    if (retValue != null) {
      retValue[0] = objIn.isCompressed();
      retValue[1] = objIn.isEncrypted();
    }
    return objIn.readObject();   // [11]
  }
}

0x05 漏洞利用

U8 Cloud 中依赖了 commons-collections-3.2.1 ,则可以通过cc链达到反序列化利用的目的。

在利用过 CC 链利用过程中,我们尝试直接通过 CC 链攻击时发现并不能有效地成功利用。经过调试后发现,原因出在该接口的序列化数据中,正如上文提到需要经过 NetObjectInputStream 进行序列化数据检测,而我们则使用的是 ObjectOutputStream 进行生成的序列化数据,导致了序列化数据不合法。

于是我们尝试通过 NetObjectOutputStream 生成序列化序列化数据,这样就通过了 NetObjectInputStream 对序列化流的合法性检测,成功达到了反序列化攻击的目的。

// 序列化对象
public static void serialize(Object obj) throws IOException {
  ByteArrayOutputStream bao = new ByteArrayOutputStream();
  NetObjectOutputStream noo = new NetObjectOutputStream(bao);
  noo.writeObject(obj);
  noo.flush();
  noo.close();
  FileOutputStream fos = new FileOutputStream("./ServiceDispatcherServlet.ser");
  NetObjectOutputStream.writeObject(fos, obj);
}

0x06 总结

本文对 U8Cloud ServiceDispatcher 接口反序列化漏洞原理进行跟踪分析,讲述了该漏洞是如何被利用以及官方的修复方式。同时,在漏洞分析过程中,我们发现并验证了其他接口也使用了自定义的序列化类(NetObjectStream)进行反序列化解析数据,存在反序列化漏洞(已报送给国家漏洞监管单位和厂商进行修复)。

Goby 欢迎表哥/表姐们加入我们的社区大家庭,一起交流技术、生活趣事、奇闻八卦,结交无数白帽好友。

也欢迎投稿到 Goby(Goby 介绍/扫描/口令爆破/漏洞利用/插件开发/ PoC 编写/ IP 库使用场景/ Webshell /漏洞分析 等文章均可),审核通过后可奖励 Goby 红队版,快来加入微信群体验吧~~~

文章来自Goby社区成员:Gryffinbit@白帽汇安全研究院,转载请注明出处。
微信群:公众号发暗号“加群”,参与积分商城、抽奖等众多有趣的活动
获取版本:https://gobysec.net/sale

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1170236.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5.1用栈实现队列(LC232-E)

算法&#xff1a; 这道题就是用栈模拟队列。 举个例子理解&#xff1a; 输入&#xff1a; ["MyQueue", "push", "push", "peek", "pop", "empty"] [[], [1], [2], [], [], []] 输出&#xff1a; [null, null, …

二叉树第i层结点个数

//二叉树第i层结点个数 int LevelNodeCount(BiTree T, int i) {if (T NULL || i < 1)return 0;if (i 1) return 1;return LevelNodeCount(T->lchild, i - 1) LevelNodeCount(T->rchild, i - 1); } int GetDepthOfBiTree(BiTree T) {if (T NULL)return 0;return Ge…

Linux - 进程控制(下篇)- 进程等待

进程等待 为什么进程需要等待&#xff1f; 我们知道&#xff0c;在Linux 当中&#xff0c;父子进程之间一些结构就是一些多叉树的结构&#xff0c;一个父进程可能管理或者创建了很多个字进程。 而其实我们在代码当中使用fork&#xff08;&#xff09;函数创建的子进程的父进程…

Paddle炼丹炉炸了Unexpected BUS error encountered in DataLoader worker

Paddle训练报错&#xff0c;内存不足 python train.py -c config/ResNet_W18.yaml修改配置文件config/ResNet_W18.yaml # 原配置 loader:num_workers: 4use_shared_memory: True# 修改后 loader:num_workers: 2use_shared_memory: False

《申论技巧》

一、做题过程 做题过程&#xff1a; 四个要素分析题干 一对多&#xff1a;考虑材料之间的灵活运用&#xff1b;问题对策&#xff1b;并列&#xff1b;主材料与辅材料 多个题目对应一个一篇材料&#xff1b;答案各有侧重&#xff0c;不重合 主体内容 二、读材料 2.1 粗读…

Makefile实例

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;…

C#中LINQtoObjects、LINQtoDataSet和LINQtoXML

目录 一、使用LINQ操作数组和集合 二、使用LINQ操作DataSet数据集 1.AsEnumerable()方法 2.CopyToDataTable()方法 3.AsDataView()方法 4.Take()方法 5.Sum()方法 6.示例 &#xff08;1&#xff09;源码 &#xff08;2&#xff09;生成效果 三、使用LINQ操作XML 1.…

立创eda专业版学习笔记(8)(运行模式)

以前没注意过这个问题&#xff0c;我有2台电脑&#xff0c;都能登录eda专业版&#xff0c;但是一台是全在线模式&#xff0c;另外一台是半离线模式&#xff0c;虽然是同一个账号&#xff0c;但是打开里面的工程会发现&#xff0c;两边的工程完全不同&#xff0c;因为一台的工程…

建模与决策

&#xff08;1&#xff09; 人们老说&#xff1a;算法、建模。但其实人们口中老说的算法、建模&#xff0c;不是真正的算法、建模&#xff0c;而是逻辑。 所以我在这里&#xff0c;对建模、模型做个定义&#xff0c;那就是&#xff1a; 一定能用数学公式进行表达、推导 而且这个…

佩戴舒适好的蓝牙耳机有哪些?五款佩戴最舒适的蓝牙耳机分享

随着骨传导耳机的普及&#xff0c;越来越多的人开始了解这种新型的耳机。相较于传统耳机&#xff0c;骨传导耳机长时间佩戴不会出现耳朵疼痛等问题&#xff0c;因此成为了很多人的首选。它不需要塞入耳道&#xff0c;避免了耳朵的闷热感&#xff0c;更能有效预防耳道感染的发生…

友提:2023年小学生古诗文大会今天(11月4日)截止,请及时参赛

根据小学生古诗文大会方案和通知&#xff0c;2023第八届上海小学生古诗文大会暨古诗文“桂冠少年”选拔活动初选答题时间为10月21日开始到11月4日结束。 即到今天晚上23点59分就结束初选答题&#xff0c;请各位家长和孩子们准时参赛&#xff0c;必要的时候设置闹钟。 一、如何…

基于Python+Networkx的最短路径

networkx是一个用Python语言开发的图论与复杂网络建模工具&#xff0c;内置了常用的图与复杂网络分析算法&#xff0c;可以方便的进行复杂网络数据分析、仿真建模等工作。利用networkx可以以标准化和非标准化的数据格式存储网络、生成多种随机网络和经典网络、分析网络结构、建…

3+单细胞+代谢+WGCNA+机器学习

今天给同学们分享一篇生信文章“Identification of new co-diagnostic genes for sepsis and metabolic syndrome using single-cell data analysis and machine learning algorithms”&#xff0c;这篇文章发表Front Genet.期刊上&#xff0c;影响因子为3.7。 结果解读&#x…

xilinx fpga ddr mig axi

硬件 参考&#xff1a; https://zhuanlan.zhihu.com/p/97491454 https://blog.csdn.net/qq_22222449/article/details/106492469 https://zhuanlan.zhihu.com/p/26327347 https://zhuanlan.zhihu.com/p/582524766 包括野火、正点原子的资料 一片内存是 1Gbit 128MByte 16bit …

【教3妹学编程-算法题】数组中两个数的最大异或值

3妹&#xff1a;“太阳当空照&#xff0c;花儿对我笑&#xff0c;小鸟说早早早&#xff0c;你为什么背上炸药包” 2哥 :3妹&#xff0c;什么事呀这么开心呀。 3妹&#xff1a;2哥你看今天的天气多好啊&#xff0c;阳光明媚、万里无云、秋高气爽&#xff0c;适合秋游。 2哥&…

成人编程先学什么?成人编程一般要学几年

成人编程先学什么&#xff1f;成人编程一般要学几年 给大家分享一款中文编程工具&#xff0c;零基础轻松学编程&#xff0c;不需英语基础&#xff0c;编程工具可下载。 这款工具不但可以连接部分硬件&#xff0c;而且可以开发大型的软件&#xff0c;向如图这个实例就是用这个…

【操作系统】多线程同步与互斥

文章目录 一. 实验目的二. 实验内容三. 实验步骤四. 实验结果五. 实验总结 一. 实验目的 &#xff08;1&#xff09;加强对进程同步和互斥的理解&#xff0c;学会使用信号量解决资源共享问题。 &#xff08;2&#xff09;熟悉Linux 进程同步原语。 &#xff08;3&#xff09;掌…

第8章_聚合函数

文章目录 1 聚合函数介绍1.1 AVG和SUM函数1.2 MIN和Max函数1.3 COUNT函数演示代码 2 GROUP BY2.1 基本使用2.2 使用多个列分组2.3 演示代码 3 HAVING3.1 基本使用3.2 WHERE和HAVING的对比3.3 演示代码 4 SELECT的执行过程4.1 查询的结构4.2 SELECT执行顺序4.3 SQL的执行原理演示…

前K个高频单词(Java详解)

一、题目描述 给定一个单词列表 words 和一个整数 k &#xff0c;返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率&#xff0c; 按字典顺序 排序。 示例1&#xff1a; 输入: words ["i", "love&…

curl(四)证书相关

一 证书相关 ① -k 1、客户端忽略服务端证书校验 -k | --insecure --> 单向[1]、这个选项显式地允许curl 执行不安全 的SSL连接和传输[2]、所有SSL连接都试图通过使用默认安装的CA证书捆绑包来确保安全[3]、这使得所有被认为是不安全的连接失败,除非使用-k --> 自签…