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
处输入路径回溯符。
测试之后发现同样会返回文件内容。