之前就有师傅在先知发表过,这里跟进了一下。
漏洞信息
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) |