Common Collections 利用链-3(CC3)漏洞分析

slug
series-status
status
summary
date
series
type
password
icon
tags
category

引言

  • CC3和前边介绍的CC1和CC6不同,它使用了不同的命令执行方式,之前的是通过反射的形式调用Runtime类执行命令,而这个利用链则是通过类加载机制去执行任意代码,比之前的命令执行方式具有更高的可扩展性,在漏洞利用工具中也更常使用,可以一定程度上绕过目标系统的限制。
  • 在这里先引用一下由ChatGPT生成的Java类的动态加载流程的描述,便于大家理解这里的类加载流程:
    • Java类的动态加载,也称为运行时类加载,是指在Java应用程序运行时加载Java类的过程。它是通过Java ClassLoader完成的。
      下面是Java类的动态加载流程:
      1. 请求加载类:当Java代码试图使用一个类时,例如通过new关键字创建一个对象,或者使用类的静态成员,就会请求加载该类。
      1. 类加载器查询:Java虚拟机检查该类是否已经被加载。如果已经加载,则跳过后面的步骤,如果没有加载,则继续。
      1. 类加载器加载:Java虚拟机请求相应的类加载器加载该类。类加载器从指定的位置(例如磁盘或网络)加载字节码。
      1. 链接:加载完成后,类加载器将该类的字节码链接到Java虚拟机中。链接的过程包括验证字节码、准备阶段(为静态变量分配内存并设置默认值)和解析阶段(解析符号引用并在运行时解析它们)。
      1. 初始化:最后,Java虚拟机对该类执行初始化。初始化包
      1. 括执行静态初始化块和静态初始化方法,以及调用类的构造方法。
        1. 其中,静态初始化块是在类中使用static{}语句块定义的,它在类被加载时被执行,仅执行一次。静态初始化方法是使用static修饰的方法,它们在类被加载时也会被执行,仅执行一次。
          另外,类的构造方法是在创建该类的实例时调用的,每创建一个实例就会调用一次构造方法,并且执行它的初始化操作。
          总的来说,Java类的动态加载是一个复杂的过程,但它允许Java应用程序在运行时加载和使用新类,这对于某些应用程序是非常有用的。

CC3分析

  • 一般来说,Java中要自行进行类加载一般流程是通过loadClass -> findClass -> defineClass(从字节码加载类)-> instance
  • 而我们如果想要通过类加载去执行任意代码,则可以从defineClass入手去看看有哪些类调用了这个方法,因为这个类是protected的,因此我们不能直接去调用这个类:
notion image
  • 经过逐步排查,会发现这个参数的defineClass可以利用,因为其中有非protected的方法调用了它:
notion image
  • 而调用了它的则是TemplatesImpl的defineClass方法:
notion image
  • 这个方法虽然不是protected的,但是是default的,因此只有同包的方法可以调用它,因此还需要继续找哪个函数调用了TemplatesImpl的这个defineClass方法
  • 最后发现在同类的defineTransletClasses方法中调用了defineClass方法:
notion image
  • 但是这个方法也同样是私有方法,因此还需要继续向前寻找,看哪里调用了这个方法,最好是public的
  • 通过idea的find usage可以看到这里有三个方法调用了这个方法:
notion image
  • 这里进行一个简单的分辨,首先看前两个方法,可以发现一个是直接返回class对象,另一个是返回数组下标:
notion image
notion image
  • 但是显然,如果我们使用这两个类的话我们最多只能加载我们想要加载的恶意类,但是如果我们想要执行我们想要执行的代码还需要实例化它才能执行我们想要执行的静态代码块,因此这两个是不行的。
  • 而当我们看到第三个的时候会发现第三个方法不仅仅通过我们的这个原函数加载了类,还会在接下来实例化这个类:
notion image
  • 这就很完美地契合了我们的目的~
  • 而由于这个函数也是一个私有方法,因此我们还需要继续追溯调用它的地方
  • 我们会发现只有一个方法调用了它,就是TemplatesImpl的newTransformer方法:
notion image
  • 而且他还是一个public的方法~
  • 此时我们就要开始考虑我们的利用代码应该怎么写了,首先我们知道我们只要调用TemplateImpl的newTransformer方法即可完成我们的漏洞利用,此时我们的代码如下:
  • 而此时我们要考虑的则是其中要传那些参数才能保证其中的函数调用链能按照我们想要的流程进行,不会被某个if挡住。
  • 在newTransformer中可以看到我们要调用的getTransletInstance是无论如何都会被调用的,因此无需考虑要传的值的问题:
notion image
  • 而进到getTransletInstance方法后可以看到我们必须要保证_name属性不是null,同时\_class属性是null才能正确加载我们的恶意类:
notion image
  • 这里我们去观察一下这里的构造函数,可以看到这里并没有对name属性进行赋值:
notion image
  • 因此我们需要对name属性进行赋值
  • 继续跟进,可以发现我们还需要保证这里的_bytecodes属性非空,否则就会抛出异常:
notion image
  • 同时后续401行的_tfactory由于需要调用这个对象的方法,因此它也不能是空:
notion image
  • 这里由于这个TemplateImpl类是可以序列化的,所以我们可以直接反射去修改这些属性
  • 对于name属性我们可以任意赋值一个String类型的对象,而bytecodes我们则需要确认一下它的类型以及用处
  • 首先是它的类型,可以在类的定义中看到bytecodes属性是一个二维的byte数组:
notion image
  • 然后在它的使用过程中可以看到其实是对二维数组中的一维的byte数组循环加载对应的类:
notion image
  • 因此这里的bytecodes其实就是由多个类的字节码数组组成的一个二维数组,这里我们也可以只传一个恶意类
  • 这里我们先用一些代码编译出一个恶意类来:
  • 然后我们将其编译后加载它的字节码即可:
  • 接下来还有一个tfactory对象需要关注
  • 可以看到默认的改属性为空,而且他是带有transient属性的属性,也就是说它是不能被序列化的,也就是说即使我们通过反射给它添加了内容在序列化以后它仍是空的
notion image
  • 这里就比较麻烦,因此我们继续看哪里可以将其赋值
  • 我们最终可以发现,就在其readObject方法的最后,就会自动为这个属性赋予一个对象:
notion image
  • 这里说明我们如果进行序列化+反序列化的操作时这里是不需要赋值的
  • 但是这里我们先尝试一下不进行序列化和反序列化的操作去看一下恶意类加载是否有效,我们先通过反射去给它附一个值:
  • 当然这里我们预期是可以直接在httplog收到我们的记录,但是执行过程中会发现这里报了一个空指针错误:
notion image
  • 根据错误信息直接打断点进行调试:
notion image
  • 可以看到其实是_auxClasses对象为空导致了这个问题,因此我们还需要解决一下这个问题
    • 在这里有两种方案去解决,一个是让代码只走上边的if条件,不进入else,另一个是给这个属性赋值
  • 而根据后文配合调试信息可以看到,这里如果走else条件,由于transletIndex属性的值为负,也会导致后续抛出异常,因此只有第一种方法是可以的:
notion image
  • 因此我们要满足这个if条件,也就是说我们要让我们的恶意类的父类的名字是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet:
notion image
  • 因此,修改我们的恶意类代码(由于这个类还是一个接口类,因此我们还需要实现一些它的未实现的方法):
  • 再次编译后运行我们的漏洞利用代码:
notion image
  • 可以看到代码顺利完成执行
  • 至此我们已经完成了序列化之前的部分流程,后续我们只需要用类加载的这个部分结合CC1或者CC3的前半部分即可完成完整的序列化+反序列化漏洞利用
  • 这里我们先把ChainedTransformer的部分加进来,然后利用反射去调用newTransformer方法:
  • 可以发现可以顺利执行:
notion image
  • 接下来直接把CC1的后半段直接复制过来即可:
这里要注意,CC1只有在jdk版本小于8u65的时候可用
  • 效果:
notion image
  • 当然,我们用CC6的后半段也是可以的:
  • 效果:
notion image
  • 可以看到这里代码成功执行~
  • 此时我们的关键节点流程图如下:
notion image

YSO分析

  • 接下来我们再看一下YSO里是怎么用的CC3,可以看到他这里用的是InstantiateTransformer:
notion image
  • 他这里使用这个类的原因则是和我们开发新的利用方法一样,正如有的系统可能过滤了Runtime,那么我们的CC1和CC6就用不了了,那也有可能有的系统过滤了InvokerTransformer,那我们上文的CC3就也用不了了,因此他这里开辟了一条新的道路。
  • 那我们回到之前的newTransformer,看看有没有其他的可以调用它的:
notion image
  • 可以看到这里其实还是有一些调用的,我们逐个观察这些调用的类
  • 可以发现,其中除了我们原本用的第一个,后边两个都是没办法序列化的,那如果是没法序列化的话在其中调用newTransformer方法的时候最好能在构造方法里调用,这样可以保证在序列化和反序列化的过程中还能正常使用
  • 而最后可以看到第二个类的构造方法基本是空的,而第三个类的构造方法中调用了newTransformer:
notion image
notion image
这里看到的代码根据jdk版本不同会有所差异,但是结论是一样的
  • 因此我们选择第三个类
  • 而CC3的作者则也是通过InstantiateTransformer调用了TrAXFilter的构造方法最后完成了整个的利用链
  • 而在这个类的transform方法中恰好调用了可控的对象的构造方法:
notion image
  • 因此,整合所有的方法,最终利用代码为:
  • 效果
notion image

总结

  • 最终的关键节点流程图:
notion image
参考视频:https://www.bilibili.com/video/BV1Zf4y1F74K
Loading...

尚未开始
更新中
近期核心
已完结
已弃更

© River 2021-2025