欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

浅析Java序列化和反序列化

来源:本站整理 作者:佚名 时间:2019-01-15 TAG: 我要投稿
    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(type);
    } catch(IllegalArgumentException e) {
        throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
    }
    MapString, Class> memberTypes = annotationType.memberTypes();
    for (Map.EntryString, Object> memberValue : memberValues.entrySet()) {
        String name = memberValue.getKey();
        Class memberType = memberTypes.get(name);
        if (memberType != null) {
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) ||
                    value instanceof ExceptionProxy)) {
                memberValue.setValue(
                    new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]").setMember(
                            annotationType.members().get(name)));
            }
        }
    }
}
可以看到,在遍历memberValues.entrySet()时,会用键名在memberTypes中尝试获取一个Class,并判断它是否为null,这就是刚才说的需要满足的条件。接下来是网上很少提到过的一个结论:
首先,memberTypes是AnnotationType的一个字段,里面存储着Annotation接口声明的方法信息 (键名为方法名,值为方法返回类型) 。因此,我们在获取AnnotationInvocationHandler实例时,需要传入一个方法个数大于0的Annotation子类 (一般来说,若方法个数大于0,都会包含一个名为value的方法) ,并且原始Map中必须存在任意以这些方法名为键名的元素,才能顺利进入setValue()的流程:
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cls.getDeclaredConstructors()[0];
ctor.setAccessible(true);
Object o = ctor.newInstance(Target.class, m);
以上是TransformedMap的利用构造过程。而ysoserial官方更倾向于使用LazyMap作为装饰器,它在装饰时会传入原始Map和一个Transformer作为工厂,当get()获取值时,若键不存在,就会调用工厂的transform()创建一个新值放入Map中,因此装饰任意一个空Map也可以满足需求:
Map m = LazyMap.decorate(new HashMap(), chain);
但与TransformedMap不同的是,AnnotationInvocationHandler.readObject()中并没有直接的对memberTypes执行get()操作,反而是在它的invoke()中存在get(),但又对方法名有一定的要求:
public Object invoke(Object proxy, Method method, Object[] args) {
    String member = method.getName();
    Class[] paramTypes = method.getParameterTypes();
    if (member.equals("equals") && paramTypes.length == 1 &&
        paramTypes[0] == Object.class)
        return equalsImpl(args[0]);
    assert paramTypes.length == 0;
    if (member.equals("toString"))
        return toStringImpl();
    if (member.equals("hashCode"))
        return hashCodeImpl();
    if (member.equals("annotationType"))
        return type;
    Object result = memberValues.get(member);
    // omit
}
所以,ysoserial使用Java动态代理的方式处理了LazyMap,使readObject()在调用memberValues.entrySet()时代理进入AnnotationInvocationHandler.invoke()阶段,刚好方法名entrySet也可以顺利的跳过前面的几个判断条件,最终达到目的。这也是为什么Payload中会包含两个AnnotationInvocationHandler的原因。
修复方案
Jenkins在1.638版本的Connection.readObject()中,将默认的ObjectInputStream改为了其自定义的子类ObjectInputStreamEx,并传入ClassFilter.DEFAULT校验过滤:
public T> T readObject() throws IOException, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStreamEx(in,
            getClass().getClassLoader(), ClassFilter.DEFAULT);

上一页  [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载