Spring Cloud Gateway CVE-2022-22947 漏洞分析

匿名者  920天前

作者:cdhe@白帽汇安全研究院

1、漏洞信息

当Gateway Actuator端点是启用状态并且是允许访问的以及相关配置不安全时,使用Spring Cloud Gateway的应用程序容易受到代码注入攻击。远程攻击者可以发出恶意制作的请求,允许在远程主机上进行任意远程执行。

https://tanzu.vmware.com/security/cve-2022-22947

微信截图_20220528195358.png

1.1 、影响版本

Spring Cloud Gateway 3.1.0

Spring Cloud Gateway 3.0.0 - 3.0.6

不受支持的版本也会受到影响

1.2、漏洞补丁

https://github.com/spring-cloud/spring-cloud-gateway/commit/337cef276bfd8c59fb421bfe7377a9e19c68fe1e

微信截图_20220528203646.png

通过查看修复补丁以及对比原文件,此修复的地方是ShortcutConfigurable接口的getValue方法中的StandardEvaluationContext类,将此类换成了GatewayEvaluationContext类,而GatewayEvaluationContext类是自定义的且继承EvaluationContext接口GatewayEvaluationContext
类内部是对SimpleEvaluationContext类的一些调用封装等。

StandardEvaluationContext类:支持更全面的SpEL功能

SimpleEvaluationContext类:基本的SpEL功能,仅支持 SpEL 语言语法的一个子集,例如排除对 Java 类型、构造函数和 bean 引用。

2、漏洞分析

2.1、调用链

环境可以直接下载对应版本然后载入ide运行即可

https://github.com/spring-cloud/spring-cloud-gateway/tags

直接开始从漏洞修复的地方看,也就是ShortcutConfigurable接口的getValue方法

微信截图_20220528202728.png

既然此处会被执行SpEL表达式,那就可以查看都有哪些地方对此进行调用,可以选中getValue
方法并且ctrl+alt+h即可查看被调用情况,可以看到有三个枚举常数对getValue方法进行调用并且都在ShortcutConfigurable接口中,而且三处都对normalize方法进行重写

微信截图_20220529014007.png

继续向上追踪,查看哪些地方对normalize方法进行了调用,这次进入到了ConfigurationService类的内部类ConfigurableBuilder中的normalizeProperties方法,而normalizeProperties方法调用了normalize方法

微信截图_20220529023625.png

ConfigurationService类的内部类AbstractBuilder中的bind方法调用了normalizeProperties方法

微信截图_20220529024354.png

bind方法被五处地方所引用:

AbstractRateLimiter类中的onApplicationEvent方法

RouteDefinitionRouteLocator类中的loadGatewayFilters方法与lookup方法

WeightCalculatorWebFilter类中的handle方法

BetweenRoutePredicateFactoryTests类中的bindConfig方法

本次主要关注loadGatewayFilters方法,loadGatewayFilters方法继续向前路径为:loadGatewayFilters() -> getFilters() -> convertToRoute() -> getRoutes()

image.png

整个的调用链为

getValue:273, Spelexpression (org.springframework.expression.spel.standard)

getValue:60, ShortcutConfigurable (org.springframework.cloud.gateway.support)

normalize:94, ShortcutConfigurable$ShortcutType$1 (org.springframework.cloud.gateway.support)

normalizeProperties:140, ConfigurationService$ConfigurableBuilder (org.springframework.cloud.gateway.support)

bind:241, ConfigurationService$AbstractBuilder (org.springframework.cloud.gateway.support)

loadGatewayFilters:144, RouteDefinitionRouteLocator (org.springframework.cloud.gateway.route)

getFilters:176, RouteDefinitionRouteLocator (org.springframework.cloud.gateway.route)

convertToRoute:117, RouteDefinitionRouteLocator (org.springframework.cloud.gateway.route)

...

2.2、分析

首先getRoutes()调用convertToRoute方法并把参数routeDefinition传入,routeDefinition参数就是自定义的路由,而RouteDefinition的内容就是路由的语法格式

image.png

可以从toString方法看到数据字段的情况,而且filters字段是要求是ArrayList类型

image.png

image.png

之后定义了一个List<GatewayFilter>类型的gatewayFilters变量,其中GatewayFilter继承ShortcutConfigurable接口

image.png

之后调用getFilters方法并且传入自定义路由

image.png

首先判断是否有默认的filters,系统默认会有,所以(!this.gatewayProperties.getDefaultFilters().isEmpty())返回true,调用loadGatewayFilters设置默认的filters(!routeDefinition.getFilters().isEmpty())结果也是true,调用loadGatewayFilters方法传入id以及filters数组

image.png

传入到loadGatewayFilters方法后filters就成为了filterDefinitions,首先获取filterDefinitions的大小然后开启循环,将filterDefinitions中的数据放入definition,获取definition.getName放入factory,然后判断factory是否是null,如果是就抛出异常,提"Unable to find GatewayFilterFactory with name {filter}",不是的话判断logger是否已开始debug模式,然后打印日志"RouteDefinition {id} applying filter {args} to {filter}"

image.png

定义变量configuration,通过加载definition参数,也就是传入的自定义路由,创建配置服务然后调用bind方法

image.png

bind方法处主要是一些判断以及提示,判断configurationService的一些内容字段,比如configurable

是否为nullname等是否为null以及是否是空白等,(this.normalizedProperties == null) true,调用normalizeProperties方法

image.png(this.service.beanFactory != null) true,调用normalize方法传入若干参数

image.png

args作为参数开启循环,argsMap<String, String>类型,然后取出keyvalue,其中key要求不能是"_genkey_"开头,否则就报错"Must not instantiate utility class."以及其他几个并列要求,然后调用getValue方法

image.png

image.png

entryValue赋值给rawValueentryValue中的数据就是要执行的恶意代码,调用trim去除前后空格,判断rawValue是否为null以及是否"#{"开头和"}"结尾,设置context变量,将beanFactory
放入context.BeanResolver

image.png

image.png

调用parseexpression方法,对字符串做处理,最终把恶意字符串开头的"#{"和结尾的"}"去掉

image.pngimage.png

expression调用getValue方法,进入Spelexpression.class,判断context是否为空,获取compiledAst赋值给compiledAst其值为nullif语句为假没进入逻辑,调用expressionState方法,设置rootobject

image.pngimage.png

再次调用getValue方法,得到想要的结果

image.png

互联网上的利用代码大致相同,主要区别在于filtersname字段,根据上面的分析可以看出是对参数有各种判定以及要求的,那么在创建路由时也可能会进行判断或者说在那里能看到对name字段的对比判断

image.png

image.png

这里可以利用ide下方的Actoator中的映射功能查看创建路由对应的方法,这里就是AbstractGatewayControllerEndpoint.java中的save方法,可以直接在这里下断点

image.png

首先对传入的路由做格式化检查,然后调用validateRouteDefinition方法做内容检查

image.pngimage.png

validateRouteDefinition方法又调用isAvailable方法,其中对比恶意路由中自定义的filters.name是否在GatewayFilters.name

image.png

GatewayFilters.name一共29个

AddRequestHeader

MapRequestHeader

AddRequestParameter

AddResponseHeader

ModifyRequestBody

DedupeResponseHeader

ModifyResponseBody

CacheRequestBody

PrefixPath

PreserveHostHeader

RedirectTo

RemoveRequestHeader

RemoveRequestParameter

RemoveResponseHeader

RewritePath

Retry

SetPath

SecureHeaders

SetRequestHeader

SetRequestHostHeader

SetResponseHeader

RewriteResponseHeader

RewriteLocationResponseHeader

SetStatus

SaveSession

StripPrefix

RequestHeaderToRequestUri

RequestSize

RequestHeaderSize

之后两行语句还是进行对比,对比Predicates.name,漏洞原作者的自定义路由里对其进行了定义,没有这个也可以利用,调用isAvailable方法,对比Predicates.name是否在routePredicates.name

image.png

routePredicates.name一共13个

After

Before

Between

Cookie

Header

Host

Method

Path

Query

ReadBody

RemoteAddr

Weight

CloudFoundryRouteService

之后的if语句将判断返回值,如果自定义的name与系统要求的name不一致那么就返回报错信息

image.png

3、修复

3.1.x升级到3.1.1+

3.0.x升级到3.0.7+

不需要Gateway actuator endpoint的话可以management.endpoint.gateway.enabled: false 禁用它

或者在https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.security设置保护措施


参考:

https://wya.pl/2022/02/26/cve-2022-22947-spel-casting-and-evil-beans/

https://tanzu.vmware.com/security/cve-2022-22947

本文为白帽汇原创文章,如需转载请注明来源:https://nosec.org/home/detail/5008.html

最新评论

昵称
邮箱
提交评论