之前就有师傅在先知发表过,这里跟进了一下。
漏洞信息
Spring Cloud Config 程序内部在处理客户端传入的资源时存在自动将 (_) 转换为 / 的隐藏转换,当 profile
设置为 native
时,则会导致服务端路径穿越,因此攻击者可以利用此机制来穿越到其它路径,读取任意文件。
影响范围
1 | Spring Cloud Config 2.2.0 - 2.2.1 |
环境搭建
以 2.1.5 版本为例,从官方 Git 仓库下载 v2.1.5.RELEASE
版本:
https://github.com/spring-cloud/spring-cloud-config/archive/v2.1.5.RELEASE.zip
解压缩后,进入解压缩之后的 spring-cloud-config-2.1.5.RELEASE
代码目录,找到下列文件,修改其中的内容:
1 | info: |
设置profiles-active
为native
1 | profiles: |
设置search-locations
为任意文件夹。
1 | cloud: |
主文件入口位置为org.springframework.cloud.config.server.ConfigServerApplication
,运行spring-cloud-config-server
模块,环境开启成功运行在127.0.0.1:9999
。
maven依赖安装
我是在idea中直接打开的,maven依赖会自动安装,不是使用ide的话参考下面的方法
执行下面的命令启动 Spring Cloud Config 服务端(注:必须要安装 maven,并且加入 PATH 环境变量):
1 | cd spring-cloud-config-server |
项目启动成功后,通过浏览器即可访问。
1 | http://127.0.0.1:9999/ |
漏洞利用
POC
1 | # Linux |
漏洞分析
Config-Client
可以从Config-Server
提供的HTTP接口获取配置文件使用,Config Server
通过路径/{name}/{profile}/{label}/{path}
对外提供配置文件,POC就会通过路由到这个接口
根据spring官方文档可知,解析下路由的结构:
name,应仓库名称。
profile,应配置文件环境。
label,git分支名。
**,通配子目录。
所以我们从HTTP入口org/springframework/cloud/config/server/resource/ResourceController.java
第71行这里开始调试,请求url为http://127.0.0.1:9999/b/a/..(_)..(_)..(_)..(_)..(_)..(_)..(_)..(_)..(_)etc/resolv.conf
。76行,我们的path
被解析为resolv.conf
。然后跟进第77行retrieve
方法
先跟进105行解析name的resolveName
方法
替换name
中存在的(_)
,这里name没有变化,然后我们继续跟进上述代码的resolveLabel
方法
这里label中所有的(_)
被替换成了/
,所以最终返回../../../../../../../../../etc
,然后我们接着跟进107行的findOne
方法
到达org/springframework/cloud/config/server/resource/GenericResourceRepository.java
,跟进64行getLocations
方法
到达org/springframework/cloud/config/server/environment/SearchPathCompositeEnvironmentRepository.java
,这里跟进47行getLocations
方法
到达org/springframework/cloud/config/server/environment/NativeEnvironmentRepository.java
文件的,this.searchLocations;
是生成file://
开头的代码
最终上述org/springframework/cloud/config/server/resource/GenericResourceRepository.java
文件的64行代码会返回对应的file
协议的绝对路径地址且为有两个元素的数组,然后接着去跟进69行getProfilePaths
方法
这里返回是个Set集合,返回结果为resolv-a.conf
和resolv.conf
,这段代码不难理解
然后上面的返回的值resolv-a.conf
和resolv.conf
分别迭代。然后跟进org/springframework/cloud/config/server/resource/GenericResourceRepository.java
的第70行isInvalidPath
方法,这里主要是检测path
中是否包含一些字符的,比如限制了WEB-INF
、META-INF
等
然后接着跟进org/springframework/cloud/config/server/resource/GenericResourceRepository.java
的第70行isInvalidEncodedPath
方法,这里主要是检测是否为有效url编码的,影响也不是很大
然后就根据反射读取文件了
1 | Resource file = this.resourceLoader.getResource(location) |