Spring Cloud Function 是来自 Pivotal 的 Spring 团队的新项目,它致力于促进函数作为主要的开发单元。该项目提供了一个通用的模型,用于在各种平台上部署基于函数的软件,包括像 Amazon AWS Lambda 这样的 FaaS(函数即服务,function as a service)平台。
研究发现,Spring Cloud Function 的 RoutingFunction 类中 apply() 方法将请求头中 spring.cloud.function.routing-expression 字段定义的 SpEL 表达式使用 StandardEvaluationContext 进行解析 ,从而造成远程代码执行漏洞。
项目地址:https://github.com/spring-cloud/spring-cloud-function
影响版本
3.0.0.M3 <= Spring Cloud Function <= 3.2.2
漏洞简析
前几天 Spring4Shell 爆发时这个漏洞还作为一个 0day 没有分配 CVE,看到 tw 上有些人说 Spring Core RCE 就是这个洞,然而并不是:)。
闲话少说跟着过一遍流程吧。
以 spring-cloud-function-samples/function-sample-pojo 为例分析
git clone --branch v3.2.0 https://github.com/spring-cloud/spring-cloud-function
配置spring-cloud-function-samples/function-sample-pojo/src/main/resources/application.properties(非必选项,未配置的情况下借助 /functionRouter 接口一样可以触发)
spring.cloud.function.definition:functionRouter
Run
至此环境就位。
老规矩先看补丁,定位到 commit: dc5128b80c6c04232a081458f637c81a64fa9b52,重点关注如下修改
看到从请求头中获取的 spring.cloud.function.routing-expression 原本由 StandardEvaluationContext 解析,commit 更新后新增了 isViaHeader 变量做了一个判断,如果是从请求头中获取的 spring.cloud.function.routing-expression 值,使用 SimpleEvaluationContext 解析,以达到防御 SpEL 表达式注入的目的。
依据 RoutingFunctionTests
构造一个无害 payload
POST /functionRouter/rand HTTP/1.1
Host: 192.168.110.1:8080
spring.cloud.function.routing-expression: SpEL-Expression
Content-Type: application/x-www-form-urlencoded
定位到org.springframework.cloud.function.context.config.RoutingFunction#functionFromExpression,看到 SpEL 表达式解析的位置,使用 StandardEvaluationContext 对象解析 SpEL 表达式的值,造成 SpEL 表达式注入问题,sink 点打断点,开启 debug 并发送我们上面构造的 payload
向上追溯看看其中几个关键点
定位到org.springframework.cloud.function.context.config.RoutingFunction#route,从请求头的 spring.cloud.function.routing-expression 字段取值
继续向上,省略参数传递过程
-> org.springframework.cloud.function.context.config.RoutingFunction#apply
-> org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper#doApply
-> org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper#apply
-> org.springframework.cloud.function.web.util.FunctionWebRequestProcessingHelper#processRequest
直到 source:org.springframework.cloud.function.web.mvc.FunctionController#form,可以看到 FunctionController#form 规定了触发的请求头为 POST 方法,MIME 类型为 "application/x-www-form-urlencoded" 或 "multipart/form-data"。(这里针对本例漏洞环境 spring-cloud-function-samples/function-sample-pojo,顺便去看了一下 spring-cloud-function Release 版本中 source 不唯一)。
最后放一下调用栈
getValue:349, SpelExpression (org.springframework.expression.spel.standard)
functionFromExpression:201, RoutingFunction (org.springframework.cloud.function.context.config)
route:127, RoutingFunction (org.springframework.cloud.function.context.config)
apply:86, RoutingFunction (org.springframework.cloud.function.context.config)
doApply:699, SimpleFunctionRegistry$FunctionInvocationWrapper (org.springframework.cloud.function.context.catalog)
apply:551, SimpleFunctionRegistry$FunctionInvocationWrapper (org.springframework.cloud.function.context.catalog)
processRequest:100, FunctionWebRequestProcessingHelper (org.springframework.cloud.function.web.util)
form:84, FunctionController (org.springframework.cloud.function.web.mvc)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:-1, DelegatingMethodAccessorImpl (sun.reflect)
invoke:-1, Method (java.lang.reflect)
doInvoke:205, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:150, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:117, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:895, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:808, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:87, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1067, DispatcherServlet (org.springframework.web.servlet)
doService:963, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doPost:909, FrameworkServlet (org.springframework.web.servlet)
service:681, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:100, RequestContextFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:93, FormContentFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:889, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1743, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:-1, Thread (java.lang)
漏洞修复
自查 Spring Cloud Function 使用版本,升级到安全版本即可。
全局搜索 jar 文件版本,若是 Maven 引入则查 pom,很简单不说了。
当然官方使用 SimpleEvaluationContext 解析 SpEL 可以修复 SpEL 表达式注入引起的代码执行系统接管问题,但是仍存在 SpEL 表达式中初始化超大数组导致的内存耗尽 DOS 问题,有兴趣的小伙伴可以研究一下文末许少的文章,后续官方应该还会发布相应的安全修补措施。
Referer
https://www.cyberkendra.com/2022/03/rce-0-day-exploit-found-in-spring-cloud.html
https://mp.weixin.qq.com/s/U7YJ3FttuWSOgCodVSqemg
https://github.com/spring-cloud/spring-cloud-function/commit/dc5128b80c6c04232a081458f637c81a64fa9b52
https://xz.aliyun.com/t/11114
声明:本文档仅用于技术研究,由于传播、使用此文档提供的信息而造成任何后果,均由使用者本人负责,Craft Security Group 团队及文章作者不为此承担任何法律责任。
原文始发于微信公众号(Craft Security Group):Spring Cloud Function SpEL 表达式注入漏洞(CVE-2022-22963)分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论