该漏洞是CVE-2019-3799的绕过。
存在漏洞的版本:2.2.x系列:< 2.2.2,2.1.x系列:< 2.1.7
修复分析
将2.1.6和2.1.7版本进行对比,其中增加了对所有location是否合法的判断。在上一个漏洞(CVE-2019-3799)中仅对path
进行了判断。2.1.6版本(左侧)代码第69行将path
参数传递给了local
变量,两者的值是一样的,在第70行if判断中!isInvalidPath(local) && !isInvalidEncodedPath(local)
中的参数local
也就是path
。
isInvalidEncodedLocation
和isInvalidEncodedPath
一样,都是用于判断路径中是否存在%
,若存在则再进行一次URL解码再调用isInvalidPath
方法,不同的是,isInvalidEncodedLocation
调用的是isInvalidLocation
方法。
1 | private boolean isInvalidEncodedLocation(String location) { |
isInvalidLocation
方法比isInvalidPath
简单一些,仅对..
进行判断。isInvalidPath
方法则判断WEB-INF
、META-INF
、:/
、../
多个的字符。
1 | private boolean isInvalidLocation(String location) { |
利用分析
调试所使用版本为
spring-cloud-config-2.1.6.RELEASE
漏洞利用需要用到spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/resource/ResourceController.java
中字符替换的操作。
1 | synchronized String retrieve(ServletWebRequest request, String name, String profile, |
关注最开始的两行
1 | label = resolveLabel(label); |
其中resolveLabel
和resolveName
方法如下,将(_)
替换成/
1 | private String resolveName(String name) { |
所以可以利用(_)
来代替/
作为路径分隔符使得@RequestMapping("/{name}/{profile}/{label}/**")
中{name}
和{lable}
就能携带路径分隔符和路径回溯符进行任意文件读取。
如果没有这个替换,在路由分配时就没办法在{name}
和{lable}
中进行回溯,举个例子:
1 | /abc/xyz/../../etc/passwd => |
尝试构造请求http://127.0.0.1:8889/test/test/%2e%2e%28%5f%29%2e%2e%28%5f%29%2e%2e%28%5f%29%2e%2e%28%5f%29%2e%2e%28%5f%29%2e%2e%28%5f%29%2e%2e%28%5f%29tmp/test.txt
,报500错误
查看报错信息,提示Branch name ../../../../../../../tmp is not allowed
在checkout处会进行git请求,造成报错
src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepository.java
因为在默认配置下,spring cloud config使用git远程读取配置,label表示git的分支,在CVE-2019-3799中label的值为master,master分支存在才能进一步读取文件。若修改想要label的值并成功读取任意文件,需要将配置改成本地才不会进行git checkout。
修改配置成以下配置,即改为读取本地配置,从而能读取本地文件。
1 | info: |
再次调试,此时locations[1]的值为file:///Users/seikei/Records/Java/spring-cloud-config-2.1.6.RELEASE/spring-cloud-config-server/src/test/resources/test../../../../../../../../../../../tmp/
,路径回溯之后正好就是file:/tmp/test.txt
⚠️:若search-locations
使用的是相对路径,比如search-locations: file:./src/test/resources/test
,则需要在路径前添加%252f
,因为相对路径拼接label再回溯之后会变成file:tmp/test/txt
导致无法读取文件。(当然绝对路径多添加一个%252f
也能正常访问,在测试时多添加一个%252f
能提升PoC的成功率)
加上%252f
之后就变成了file:%2ftmp/test.txt
,在file协议读取文件时会再次URL解码变成file:/tmp/test.txt
成功读取文件
注:v2.2.0.RELEASE以上无法读取无后缀的文件,在ResourceController中添加了一行获取后缀的代码,若传入无后缀的文件会产生500报错。