Spring Cloud Config,2.2.3之前的2.2.x版本,2.1.9之前的2.1.x版本以及较旧的不受支持的版本允许应用程序通过spring-cloud-config-server模块提供任意配置文件。
修复分析
对比2.2.2和2.2.3版本,发现其中有两个commits似乎和漏洞修复有关。

查看对比发现在spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentController.java中多了一个pathUtils.isInvalidEncodedLocation判断。

为了搞明白为什么要多加一个pathUtils.isInvalidEncodedLocation判断,查看Environment.normalize(),发现是将SLACH_PLACEHOLDER替换成/,SLACH_PLACEHOLDER为(_)。看到这里,是不是就想起了在CVE-2020-5405中利用(_)代替/绕过检测的方法。
1 | public static String normalize(String s) { |
利用分析
调试所使用版本为
spring-cloud-config-2.2.2.RELEASE和CVE-2020-5405一样需要,更将configserver.yml改成本地配置
和前面两个漏洞(CVE-2019-3799和CVE-2020-5405)类似,也是拼接了传入的值,而传入的值能够通过URL二次编码和(_)替换绕过路径检测,实现任意文件读取。但触发任意文件读取的函数和入口不一样。
在EnvironmentController.java中找到getEnvironment方法,其中可控的参数为name、profiles和label,调试步进findOne方法
NativeEnvironmentRepository.java中的getArgs中存在对我们可控参数name、profiles和label的处理,步进该方法进行查看。
在getArgs()方法中看到熟悉的getLocations(),这里可能存在路径拼接
果然,在getLocations()方法中进行了路径的拼接,将location和label参数拼接在一起
光有location的拼接并不能产生文件读取,还得找到读取文件名的控制。
经过一段时间的调试,最终发现在builder.run中发现整个路径的拼接。
整个过程调用栈比较长,调试起来比较麻烦,知道这里将search-locations+label+name拼接起来就行(上面代码中447行中的location是search-locations+label的结果)。
load方法如下
load方法调用loadForFileExtension方法,在其中发现整个文件路径的拼接
注意拼接后还带上了 "-" + profile + fileExtension,虽然profile和fileExtension都是可控的,但是这个-比较难处理。-的存在会导致绝大部分文件都无法被读取。
注意到修复中将#做了限制,根据#在URL中的特性猜测#会使后面的字符变成标识符,不会被识别。
构造PoC进行请求后发现果然能用#“注释”后面的内容。
复现
test.txt文件中的内容为just for test,可以看到其中存在多处文件信息回显。因为file:/tmp//test.txt#-seikei.xml中#后面的字符不会被解析,等于访问的是file:/tmp//test.txt。

由于整个文件路径是由label和name进行的拼接,所以也可以在name处输入路径回溯符。
测试之后发现同样会返回文件内容。
