官方描述:https://spring.io/blog/2019/04/17/cve-2019-3799-spring-cloud-config-2-1-2-2-0-4-1-4-6-released
We have released Spring Cloud Config 2.1.2, 2.0.4, and 1.4.6 to address CVE-2019-3799: Directory Traversal with spring-cloud-config-server. Please review the information in the CVE report and upgrade immediately.
在release中找到修复前的版本2.1.1代码 https://github.com/spring-cloud/spring-cloud-config/releases/tag/v2.1.1.RELEASE 用于复现
本次更新的代码路径为spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/resource/GenericResourceRepository.java
,commit地址为 https://github.com/spring-cloud/spring-cloud-config/commit/3632fc6f64e567286c42c5a2f1b8142bfde505c2
修复分析
补丁主要是在findOne()方法中增加了传入参数path的判断
isInvalidPath
方法用于判断路径中是否存在WEB-INF
、META-INF
、:/
、../
,如果存在则返回true,导致if (!isInvalidPath(local))
为假。
1 | protected boolean isInvalidPath(String path) { |
isInvalidEncodedPath
方法用于判断路径中是否存在%
,若存在则再进行一次URL解码再调用isInvalidPath
方法。
第二个和第三个if中的processPath
方法是将\
替换成/
,再清除连续重复的/
。
1 | private boolean isInvalidEncodedPath(String path) { |
利用分析
调试所使用版本为
spring-cloud-config-2.1.1.RELEASE
既然是findOne()方法中增加了路径分析,表明之前未做路径防护可能导致任意文件读取漏洞产生。回溯findOne()方法就能找到漏洞的利用点。查看findOne()方法的调用信息(在方法处右键点击Find Usages),可以看到有12处相关引用,由于限制在path参数,于是主要寻找path参数可控的点。
找到src/main/java/org/springframework/cloud/config/server/resource/ResourceController.java
中调用了findOne()
方法,且path可控。
1 | "/{name}/{profile}/{label}/**") ( |
path参数为@RequestMapping("/{name}/{profile}/{label}/**")
中**
的部分,也就是uri第三个标签后的全部内容。注意此处传入的label必须为master,具体原因在CVE-2020-5405中会讲到。
传入GET /name/profile/master/..%252F..%252F..%252F..%252F..%252F..%252Ftmp%252Ftest.txt
时,path参数为..%252F..%252F..%252F..%252F..%252F..%252Ftmp%252Ftest.txt
FileUrlResource
对象中的relativePath
为..%2F..%2F..%2F..%2F..%2F..%2Ftmp%2Ftest.txt
path解码后的路径,路径回溯正好变成/tmp/test.txt
findOne()方法执行完成之后,返回Resource对象。Resource是Spring框架中用于访问低级资源的一个抽象接口,常用来读取配置文件。
根据findOne()返回的Resource对象,读取到的文件内容。
复现
IDEA导入程序后,先等待maven自动安装依赖。依赖安装完成后,找到spring-cloud-config-server中src/main/java/org/springframework/cloud/config/server/ConfigServerApplication.java
,点击ConfigServerApplication
类右边的运行,启动程序即可,端口默认为8888,想要修改端口的话可在src/main/resources/configserver.yml
中修改。
程序运行起来之后访问http(s)://ip:port/foo/label/master/..%252f..%252f..%252f..%252fetc%252fpasswd
得到以下结果
不二次编码的效果如下,因为不二次编码传到代码前会自动URL解码一次,变成/foo/label/master/../../../../etc/passwd
,等效于直接访问/../etc/passwd
,tomcat会把它识别成一个错误的URI,请求不会传到ResourceController中,也就不会进行后续findOne()
方法的调用。