ysoserial中给出了调用链

LinkedHashSet.readObject()
  LinkedHashSet.add()
    ...
      TemplatesImpl.hashCode() (X)
  LinkedHashSet.add()
    ...
      Proxy(Templates).hashCode() (X)
        AnnotationInvocationHandler.invoke() (X)
          AnnotationInvocationHandler.hashCodeImpl() (X)
            String.hashCode() (0)
            AnnotationInvocationHandler.memberValueHashCode() (X)
              TemplatesImpl.hashCode() (X)
      Proxy(Templates).equals()
        AnnotationInvocationHandler.invoke()
          AnnotationInvocationHandler.equalsImpl()
            Method.invoke()
              ...
                TemplatesImpl.getOutputProperties()
                  TemplatesImpl.newTransformer()
                    TemplatesImpl.getTransletInstance()
                      TemplatesImpl.defineTransletClasses()
                        ClassLoader.defineClass()
                        Class.newInstance()
                          ...
                            MaliciousClass.<clinit>()
                              ...
                                Runtime.exec()

值的是在JDK版本7u21中的原生反序列化调用链

7u21的核心点在于sun.reflect.annotation.AnnotationInvocationHandler#equalsImpl

Untitled

方法的else语句存在一处Method.invoke方法可以用于动态执行方法,这里会调用getMemberMethods方法获取Method数组

Untitled

Method数组来源于this.type.getDeclaredMethods()

而在sun.reflect.annotation.AnnotationInvocationHandler#invoke方法会调用equalsImpl方法

Untitled

这自然会让我想到动态代理调用equals方法

看看ysoserial的调用链是如何触发equals方法,用到HashSet#readObject

Untitled

这里可以用到HashMap,在CC7中,HashMap#put方法会触发equals方法

Untitled

只需要满足HashMap前后的对象HashCode相同

由于我们需要触发调用链,则需要保证这里key为Proxy代码对象,k为TemplatesImpl对象,那么需要Proxy.hashCode等于TemplatesImpl.hashCode

TemplatesImpl.hashCode会调用native方法,这里看Proxy.hashCode,由于动态代理的关系这里会触发AnnotationInvocationHandler#invoke方法,进而触发hashCodeImpl方法

Untitled

遍历memberValues,对键值对进行处理计算,计算每个127 * ((String)var3.getKey()).hashCode() ^ *memberValueHashCode*(var3.getValue())并求和。