Skip to content

Latest commit

 

History

History
76 lines (63 loc) · 5.32 KB

File metadata and controls

76 lines (63 loc) · 5.32 KB

One For All Echo 回显(OneForAllEcho)

  • 类别:bytecode 别名:— 优先级:10
  • 作者:4ra1n
  • 依赖:无(生成的字节码在目标端按需反射,不引入攻击方依赖)

作用

生成一段"万能"命令回显字节码:一个 Payload 同时兼容 WebLogic、Jetty、Tomcat、Spring 四类中间件,运行时自动探测当前容器类型并从指定 HTTP 请求头取命令、执行、把结果写回响应体,实现无文件的命令执行回显。属于链尾字节码 sink(END)。

链接标签

  • 入口 tagsBytecodeEchoEND
    • Bytecode:本 gadget 产出的是原始类字节码(byte[]),需由上游的 BytecodeConvert 之类装载器把它包进 TemplatesImpl 并触发 defineClass
    • Echo:标识"命令回显"型 sink。
    • END:链的最内环、终点,其后不再衔接其它 gadget。
  • 衔接 nextTags:空 —— 作为终点不产出可继续包装的对象。
  • excludes:空。

源码剖析

gadget 本体极薄,真正的攻击逻辑在被 Javassist 改写的模板类 OneForAllEchoBytecode 中:

public byte[] getObject() throws Exception {
    JavassistHelper javassistHelper = new JavassistHelper(OneForAllEchoBytecode.class);
    javassistHelper.modifyStringField("header", this.header);   // 把回显请求头名写死进模板
    javassistHelper.modifyBooleanField("debug", this.debug);
    javassistHelper.modifyBooleanField("once", this.once);
    return javassistHelper.getBytecode();                        // 返回改写后的类字节码
}

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    chain.doCreate(context);   // END 环,其后无 gadget,仅走一遍链上下文
    return this.getObject();
}

JavassistHelper 以模板类 OneForAllEchoBytecode 的已编译字节码为蓝本,用 modifyStringField / modifyBooleanField 把静态字段 header/debug/once 的初值替换为用户参数,再 getBytecode() 导出为 byte[]。这段 byte[] 会被上游 BytecodeConvert 塞进 TemplatesImpl._bytecodes,反序列化触发 TemplatesImpl.newTransformer()defineClass → 静态块执行。

模板类通过静态块入口执行 run(),逐一探测四类容器:

static { OneForAllEchoBytecode.run(); }

public static void run() {
    if (isJetty())    { doJetty();    if (once) return; }
    if (isWeblogic()) { doWeblogic(); if (once) return; }
    if (isTomcat())   { doTomcat();   if (once) return; }
    if (isSpring())   { doSpring(); }
}

isXxx() 均以 Class.forName 探测各容器特征类(Jetty ServletContextHandler、WebLogic ServletRequestImpl、Tomcat Bootstrap、Spring RequestAttributes)判定环境。命中后各 doXxx() 用反射从当前线程/线程组里翻出 Request/Response 对象:

  • doTomcat():遍历 ThreadGroup.threads,找到 http 处理线程,沿 target → this$0 → handler → global → processors → req 反射链取到请求,读 header 头执行命令,再经 ByteChunk.doWrite 写回。
  • doWeblogic()Thread.getCurrentWork()ServletRequestImplgetHeaderwriteStream 回显。
  • doJetty():翻 Thread.threadLocals.table,找 AsyncHttpConnection/HttpConnection,取 getRequest/getWriter 回显。
  • doSpring()RequestContextHolder.getRequestAttributes() 取 request/response 回显。

命令执行统一在 exec(),按 os.namesh -ccmd.exe /c,读取标准输出(空则读错误流)并以 GBK 解码返回。debug=true 时会向目标 stdout 打印探测日志;once=true 表示某类容器回显成功即返回、不再尝试后续。

参数(@Param)

字段 名称 说明 默认 可选值
header 请求头 key 携带待执行命令的 HTTP Header 名 X-Authorization 任意
debug 打印调试信息 是否在目标进程 stdout 打印探测/回显调试日志 false true/false
once 成功后返回 成功回显一次后是否停止尝试后续容器 false true/false

适用版本与原理要点

  • 与具体依赖版本无关:模板类在目标端全程反射调用容器内部字段/方法,不硬编译任何容器 API,因此对 Jetty/WebLogic/Tomcat/Spring 的多版本有较强兼容性。
  • 需要一个能加载并触发字节码的上游(TemplatesImpl 系),因此常见落地形态是 [BytecodeConvert, OneForAllEcho] 或更长链的最内环,且要求目标 JDK 允许 TemplatesImpl 定义类(JDK < 17,或配合高版本绕过 gadget)。
  • 回显走的是明文 HTTP,命令在请求头、结果在响应体,流量层可检测。
  • 回显结果按 GBK 解码,非中文/UTF-8 环境可能乱码。

所属预设链

自由构件,未直接出现在预设链(usedInChains 为空)。可替换 命令执行/23-命令执行 一类链中的 Exec,与 BytecodeConvert 组合使用。

关联

  • 上游(消费本 Bytecode 产物):BytecodeConvertTemplatesImpl
  • 同族回显 sink:ResinEcho(Resin 专用回显)、以及 TomcatEcho/JettyEcho/WebLogicEcho 等单容器回显。
  • 同族非回显 sink:Exec(直接命令执行)、ReverseShellOsSleep