Apache Commons Collections(简称“CC”)是 Java 生态中最常用的第三方工具库之一,提供了丰富的集合操作类。然而,其内部部分可序列化类(如 TransformedMapLazyMap)与“函数式接口”(如 Transformer)的组合,存在反序列化代码执行漏洞。从 CC1 到 CC7,攻击者通过不同的类组合与触发逻辑,构建了多条成熟的反序列化攻击链,成为 Java 反序列化漏洞的“经典标杆”。本文将逐一拆解 7 条 CC 攻击链的核心原理、依赖条件与执行流程,并提供体系化的防御方案。

一、CC 系列漏洞基础:核心前提与关键概念

在分析具体攻击链前,需明确 CC 漏洞的两大核心前提与关键类,这是所有 CC 攻击链的共同基础:

1. 核心前提

  • 反序列化入口:目标应用存在“反序列化不可信数据”的场景(如通过 ObjectInputStream.readObject() 读取用户传入的 Serializable 对象);
  • 依赖 CC 库:目标应用引入了存在漏洞的 Apache Commons Collections 版本(不同 CC 链对应不同版本,详见下文)。

2. 关键概念与类

CC 攻击链的核心是“利用可序列化类的 readObject() 方法,触发预设的 Transformer 函数链,最终执行恶意代码”,关键类如下:

类名 作用
Transformer 函数式接口,定义 transform(Object input) 方法,用于对对象进行转换
ChainedTransformer 实现 Transformer,将多个 Transformer 组成“链式调用”,依次执行 transform
InvokerTransformer 实现 Transformer,通过反射调用任意类的任意方法(CC1 核心)
InstantiateTransformer 实现 Transformer,通过反射实例化任意类(CC2 核心)
TransformedMap 实现 Map,对键/值的修改操作触发预设 Transformer(CC1、CC2 触发载体)
LazyMap 实现 Map,对不存在的键调用 get() 时触发预设 Transformer(CC3、CC6、CC7 触发载体)
Proxy JDK 动态代理类,调用代理对象方法时触发 InvocationHandlerinvoke()(CC3、CC7 核心)
BadAttributeValueExpException JDK 内置异常类,readObject() 会调用 toString() 触发恶意逻辑(CC5、CC7 触发载体)

二、CC1 到 CC7 攻击链逐一解析

(一)CC1:最经典的“TransformedMap + InvokerTransformer”链

1. 核心原理

利用 TransformedMap 在反序列化时对“键/值”的转换逻辑,触发 ChainedTransformer 链式调用,最终通过 InvokerTransformer 的反射能力执行系统命令。

2. 依赖版本
  • Apache Commons Collections:3.1 ~ 3.2.1(3.2.2 版本修复了 InvokerTransformer 的危险反射调用);
  • JDK 版本:无特殊限制(JDK 1.7+ 均可触发)。
3. 攻击链流程
  1. 构造 Transformer:创建 ChainedTransformer,包含 3 个关键 Transformer
    • ConstantTransformer:返回目标类(如 Runtime.class);
    • InvokerTransformer:调用 Runtime.getRuntime()(反射获取 getRuntime 方法并执行);
    • InvokerTransformer:调用 Runtime.exec(String cmd)(反射执行系统命令)。
  2. 创建 TransformedMap:通过 TransformedMap.decorate() 为普通 HashMap 绑定上述 Transformer(指定对“值”进行转换);
  3. 触发反序列化:将 TransformedMap 序列化后传入目标应用,目标执行 readObject() 时:
    • TransformedMapreadObject() 会恢复 Map 数据,对“值”触发 Transformer 链;
    • 最终通过 InvokerTransformer 反射执行 Runtime.exec(cmd)
4. 关键代码示例(简化 POC)
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] args) throws Exception {
        // 1. 构造命令执行的 Transformer 链
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class), // 传入 Runtime.class
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, 
                        new Object[]{"getRuntime", new Class[0]}), // 反射 getRuntime() 方法
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, 
                        new Object[]{null, new Object[0]}), // 执行 getRuntime(),返回 Runtime 实例
                new InvokerTransformer("exec", new Class[]{String.class}, 
                        new Object[]{"calc.exe"}) // 执行 exec("calc.exe"),弹出计算器
        };
        Transformer chain = new ChainedTransformer(transformers);

        // 2. 创建 TransformedMap(对值触发 Transformer 链)
        Map map = new HashMap();
        map.put("key", "value");
        Map transformedMap = TransformedMap.decorate(map, null, chain); // 键:null(不转换),值:chain

        // 3. 序列化与反序列化(模拟目标应用操作)
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(transformedMap); // 序列化

        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        ois.readObject(); // 反序列化,触发命令执行
    }
}
5. 特点
  • 最经典、最易理解的 CC 链,无复杂依赖;
  • 3.2.2 版本后因 InvokerTransformer 被限制(禁止调用 execgetRuntime 等危险方法)而失效。

(二)CC2:针对 CC4.x 的“InstantiateTransformer + TemplatesImpl”链

1. 核心原理

CC 4.0 版本修复了 InvokerTransformer,但引入了 InstantiateTransformer(用于实例化类)。攻击者通过 InstantiateTransformer 实例化 JDK 内置的 TemplatesImpl 类,利用 TemplatesImpldefineClass() 方法加载恶意字节码,实现代码执行。

2. 依赖版本
  • Apache Commons Collections:4.0 ~ 4.0.1(4.1 版本修复 InstantiateTransformer);
  • JDK 版本:需支持 TemplatesImpl(JDK 1.6+,默认包含 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl)。
3. 攻击链流程
  1. 构造恶意字节码:编写触发命令执行的 Java 类(如 Evil),编译为字节码;
  2. 构造 TemplatesImpl:设置 TemplatesImpl_bytecodes 为恶意字节码,_name 为非空值(触发 defineClass());
  3. 构造 Transformer:通过 ChainedTransformer 组合 ConstantTransformer(返回 TemplatesImpl)和 InstantiateTransformer(实例化 TemplatesImpl);
  4. 触发反序列化:通过 TransformedMap 作为载体,反序列化时触发 Transformer 链,InstantiateTransformer 实例化 TemplatesImpl 并加载恶意字节码,执行命令。
4. 特点
  • 规避了 InvokerTransformer 的限制,利用 JDK 内置类 TemplatesImpl 加载字节码;
  • 4.1 版本后 InstantiateTransformer 被限制实例化危险类,链失效。

(三)CC3:“LazyMap + Proxy”变种链

1. 核心原理

CC1 的 TransformedMap 需“修改值”触发转换,而 CC3 利用 LazyMapget() 方法(获取不存在的键时触发 Transformer),结合 JDK 动态代理(Proxy),在反序列化时自动调用 get(),无需手动修改值。

2. 依赖版本
  • Apache Commons Collections:3.1 ~ 3.2.1(同 CC1,依赖 InvokerTransformer);
  • JDK 版本:支持动态代理(JDK 1.3+)。
3. 攻击链流程
  1. 构造 Transformer:同 CC1(ChainedTransformer + InvokerTransformer),实现命令执行;
  2. 创建 LazyMap:通过 LazyMap.decorate()HashMap 绑定 Transformer 链;
  3. 创建动态代理:以 LazyMap 为目标,创建 Map 接口的代理对象,InvocationHandler 中重写 invoke(),调用 LazyMap.get()
  4. 触发反序列化:反序列化代理对象时,readObject() 会调用代理的 entrySet() 方法(Map 反序列化默认操作),进而触发 InvocationHandler.invoke()LazyMap.get()Transformer 链 → 命令执行。
4. 特点
  • 无需“修改值”即可触发,适用场景更广(如目标反序列化时不修改 Map 值,但会调用 entrySet() 等方法);
  • 依赖 Proxy,需目标反序列化时调用代理对象的方法。

(四)CC4:“PriorityQueue + TransformedMap”的 CC4.x 适配链

1. 核心原理

CC4 针对 CC 4.x 版本,利用 PriorityQueue(优先级队列)的 readObject() 会调用 comparator.compare() 的特性,结合 TransformedMapInstantiateTransformer,触发 TemplatesImpl 加载恶意字节码。

2. 依赖版本
  • Apache Commons Collections:4.0 ~ 4.0.1(同 CC2,依赖 InstantiateTransformer);
  • JDK 版本:支持 PriorityQueue(JDK 1.5+)。
3. 攻击链流程
  1. 构造 TemplatesImplTransformer:同 CC2,通过 InstantiateTransformer 实例化 TemplatesImpl
  2. 创建 TransformedMap:绑定 Transformer 链;
  3. 创建 PriorityQueue:将 TransformedMapentrySet() 作为元素传入,设置 comparator 为触发 Map 操作的逻辑;
  4. 反序列化触发PriorityQueuereadObject() 会调用 comparator.compare(),间接触发 TransformedMapTransformer 链 → InstantiateTransformerTemplatesImpl 加载字节码 → 命令执行。
4. 特点
  • 利用 PriorityQueue 的反序列化逻辑,无需依赖 Proxy
  • 仅适用于 CC 4.x 版本,4.1 后失效。

(五)CC5:“BadAttributeValueExpException + LazyMap”链

1. 核心原理

利用 JDK 内置异常类 BadAttributeValueExpExceptionreadObject() 方法:该方法会调用 getValue().toString(),若 getValue() 返回 LazyMap 的代理对象,会触发 invoke()LazyMap.get()Transformer 链。

2. 依赖版本
  • Apache Commons Collections:3.1 ~ 3.2.1(依赖 LazyMapInvokerTransformer);
  • JDK 版本:支持 BadAttributeValueExpException(JDK 1.2+,默认包含)。
3. 攻击链流程
  1. 构造 LazyMap + Proxy:同 CC3,创建绑定 Transformer 链的 LazyMap,并生成代理对象;
  2. 创建 BadAttributeValueExpException:将其 val 字段设为上述代理对象;
  3. 反序列化触发BadAttributeValueExpExceptionreadObject() 调用 val.toString() → 代理对象的 invoke()LazyMap.get() → 命令执行。
4. 特点
  • 利用 JDK 内置类(无第三方依赖除 CC 外),隐蔽性更强;
  • 无需目标调用 MapentrySet(),仅需反序列化 BadAttributeValueExpException

(六)CC6:“HashMap + TiedMapEntry + LazyMap”链

1. 核心原理

CC6 针对 CC 3.2.1 版本(InvokerTransformer 未完全修复),利用 HashMapreadObject() 会处理 TiedMapEntry 的特性:TiedMapEntry.getValue() 会调用 LazyMap.get(),进而触发 Transformer 链。

2. 依赖版本
  • Apache Commons Collections:3.2.1(特定版本,3.2.2 后失效);
  • JDK 版本:支持 HashMapTiedMapEntry(JDK 1.2+,CC 库包含 TiedMapEntry)。
3. 攻击链流程
  1. 构造 LazyMap:绑定 Transformer 链(同 CC1);
  2. 创建 TiedMapEntry:将 LazyMap 作为 map 参数传入 TiedMapEntry
  3. 创建 HashMap:将 TiedMapEntry 作为键存入 HashMap
  4. 反序列化触发HashMapreadObject() 会调用 TiedMapEntry.hashCode()TiedMapEntry.getValue()LazyMap.get() → 命令执行。
4. 特点
  • 利用 HashMap 反序列化的默认逻辑,无需额外方法调用;
  • 仅适用于 CC 3.2.1 版本,兼容性较窄。

(七)CC7:“BadAttributeValueExpException + LazyMap + Proxy”终极链

1. 核心原理

CC7 是 CC5 的优化版,通过调整 BadAttributeValueExpExceptionval 字段类型(直接设为 LazyMap 而非代理对象),并利用 Proxyinvoke() 触发 LazyMap.get(),进一步降低触发条件。

2. 依赖版本
  • Apache Commons Collections:3.1 ~ 3.2.1(同 CC1、CC3);
  • JDK 版本:支持 BadAttributeValueExpExceptionProxy
3. 攻击链流程
  1. 构造 LazyMap + Proxy:同 CC3,生成 LazyMap 的代理对象;
  2. 修改 BadAttributeValueExpException:通过反射将 val 字段设为代理对象(绕过 val 的类型检查);
  3. 反序列化触发readObject() 调用 val.toString() → 代理 invoke()LazyMap.get() → 命令执行。
4. 特点
  • 结合 BadAttributeValueExpException 的隐蔽性与 Proxy 的灵活性,适用场景最广;
  • 需通过反射修改 BadAttributeValueExpExceptionval 字段(默认 valObject 类型,可绕过检查)。

三、CC 系列攻击链核心差异对比

攻击链 核心类组合 依赖 CC 版本 触发关键操作 优势 劣势
CC1 TransformedMap + InvokerTransformer 3.1~3.2.1 反序列化时修改 Map 值 简单易懂,兼容性强 需手动修改值,场景受限
CC2 InstantiateTransformer + TemplatesImpl 4.0~4.0.1 TransformedMap 转换值 规避 InvokerTransformer 限制 依赖 CC4.x,字节码构造复杂
CC3 LazyMap + Proxy 3.1~3.2.1 代理对象调用 entrySet() 无需修改值,场景更广 依赖 Proxy 方法调用
CC4 PriorityQueue + TransformedMap 4.0~4.0.1 PriorityQueue 比较元素 适配 CC4.x,无 Proxy 依赖 仅支持 CC4.x,兼容性窄
CC5 BadAttributeValueExpException + LazyMap 3.1~3.2.1 反序列化异常类 用 JDK 类,隐蔽性强 依赖 LazyMap 代理
CC6 HashMap + TiedMapEntry + LazyMap 3.2.1 HashMap 计算 hashCode 无需额外方法调用 仅支持 3.2.1,版本限制严
CC7 BadAttributeValueExpException + Proxy 3.1~3.2.1 异常类 val.toString() 场景最广,隐蔽性最强 需反射修改异常类字段

四、体系化防御方案

CC 系列漏洞的本质是“不可信数据反序列化 + 危险类的链式调用”,防御需从“阻断反序列化入口”“限制危险类”“升级依赖”三个维度落地:

1. 根本防御:禁止反序列化不可信数据

  • 核心原则:仅反序列化“可信来源”的数据(如内部系统生成的序列化对象),拒绝用户传入的 Serializable 对象;
  • 替代方案:使用安全的序列化协议(如 JSON、Protobuf)替代 Java 原生序列化,避免 ObjectInputStream.readObject() 的直接调用。

2. JDK 层面:配置序列化过滤器(Serial Filter)

JDK 9+ 引入 serialFilter,可通过黑名单/白名单限制反序列化的类,阻断 CC 链的关键类:

  • JVM 参数配置(全局生效):
    java -Djdk.serialFilter=!org.apache.commons.collections.functors.*,!org.apache.commons.collections.map.TransformedMap,!org.apache.commons.collections.map.LazyMap,!com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl -jar app.jar
    
  • 代码层面配置(局部生效):
    ObjectInputStream ois = new ObjectInputStream(inputStream);
    // 设置过滤器,禁止 CC 关键类
    ois.setObjectInputFilter((filterInfo) -> {
        String className = filterInfo.serialClass().getName();
        if (className.startsWith("org.apache.commons.collections.functors.") 
            || className.equals("org.apache.commons.collections.map.TransformedMap")) {
            return ObjectInputFilter.Status.REJECTED; // 拒绝反序列化
        }
        return ObjectInputFilter.Status.ALLOWED;
    });
    ois.readObject();
    

3. 依赖层面:升级 Apache Commons Collections 到安全版本

  • CC 3.x 系列:升级至 3.2.2 及以上(修复 InvokerTransformer 危险反射);
  • CC 4.x 系列:升级至 4.1 及以上(修复 InstantiateTransformer 危险实例化);
  • Maven 配置示例
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.2.2</version> <!-- 安全版本 -->
    </dependency>
    

4. 工具层面:检测与监控

  • 静态检测:使用代码扫描工具(如 FindSecBugs、SonarQube)检测代码中是否存在 ObjectInputStream.readObject() 处理不可信数据的场景;
  • 动态监控:部署应用防火墙(WAF)或运行时监控工具(如 AppSensor),监控异常的序列化流量(如包含 CC 关键类的 payload);
  • POC 验证:使用 ysoserial(Java 反序列化漏洞测试工具)生成 CC 链 POC,验证应用是否存在漏洞(需授权测试)。

五、总结与启示

CC 系列漏洞从 1 到 7 的演进,体现了攻击者对 Java 反序列化机制与第三方库的深度挖掘——从依赖单一类到组合 JDK 内置类,从明确触发条件到隐蔽触发逻辑,攻击链的兼容性与隐蔽性持续增强。

对企业而言,防御的核心并非“针对某一条 CC 链”,而是建立“反序列化安全治理体系”:

  1. 优先避免使用 Java 原生序列化,采用安全协议;
  2. 若必须使用,通过 serialFilter 严格限制反序列化类;
  3. 定期升级第三方依赖,修复已知漏洞;
  4. 结合静态检测与动态监控,形成全生命周期防护。

只有从“源头阻断风险”而非“被动防御攻击链”,才能真正抵御 Java 反序列化这类“基础性漏洞”的威胁。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐