当使用 RouterFunctions 来处理静态资源且资源处理通过 FileSystemResource 进行配置时,攻击者可以通过构造恶意 HTTP 请求,利用路径遍历漏洞获取相关受影响版本文件系统中的任意文件。
主要影响范围:
Spring Framework
5.3.0 - 5.3.39
6.0.0 - 6.0.23
6.1.0 - 6.1.12
Older, unsupported versions are also affected
当使用 RouterFunctions 来处理静态资源且资源处理通过 FileSystemResource 进行配置时,攻击者可以通过构造恶意 HTTP 请求,利用路径遍历漏洞获取相关受影响版本文件系统中的任意文件。以WebFlux为例,分析具体的成因。
0x01 漏洞分析与复现
1.1 分析过程
查看具体的修复commit:https://github.com/spring-projects/spring-framework/commit/d86bf8b2056429edf5494456cffcb2b243331c49#diff-25869a3e3b3d4960cb59b02235d71d192fdc4e02ef81530dd6a660802d4f8707
可以看到主要是对PathResourceLookupFunction进行了相关的加固处理,调整了 RouterFunctions 资源处理逻辑。
org.springframework.web.reactive.function.server.PathResourceLookupFunction 是 Spring WebFlux 中用于查找资源的函数,它通常与 RouterFunction 一起使用,以便在响应式路由中处理静态资源请求。这个函数的作用是将请求的路径映射到文件系统中的资源,如图片、样式表或 JavaScript 文件等。
主要解析处理是在apply方法进行的,下面看看具体的解析过程:
首先从 ServerRequest 中提取路径容器 PathContainer,它包含了应用程序上下文中的路径。首先检查提取的路径是否与预定义的模式匹配。如果不匹配,返回一个空。
如果路径匹配,使用 this.pattern.extractPathWithinPattern 方法提取模式内的路径部分,并将其传递给 processPath 方法进行进一步处理。
如果处理后的路径包含百分号编码,则使用 StringUtils.uriDecode 方法对其进行解码。检查解码后的路径是否有效,主要是是否非空且不是非法路径(通过isInvalidPath进行判断)
最后如果是有效的路径,尝试创建一个 Resource 对象。并返回对应的内容。
查看下processPath方法的具体实现,其主要用于处理和规范化文件路径字符串。该方法的主要作用是确保路径以正斜杠(/)开始,并且移除路径中不必要的部分:
isInvalidPath方法实现如下,主要是限制了一些路径中常出现的字符字符,防止目录穿越攻击:
以上是PathResourceLookupFunction的大致处理过程。具体的安全措施可以看到主要是对…/这类的穿越符进行了处理。
下面对比下加固后的PathResourceLookupFunction,看看具体的安全措施:
首先processPath进行了相关的改动,首先替换反斜线为斜线:
并调用cleanDuplicateSlashes去掉了多余的斜线:
在调用processPath处理后,还调用了isInvalidEncodedInputPath方法进行安全检查,如果为true则返回空:
isInvalidEncodedInputPath具体实现如下,主要作用是检查给定的路径字符串是否包含不合法的编码输入,可以看到当path路径中存在%号时,首先会进行URL解码,然后通过isInvalidPath进行判断,然后再经过一层processPath转换,这里意图应该是替换反斜线为斜线并去除多余的斜线,然后再进行一次isInvalidPath检查:
最后还修改了isResourceUnderLocation的判断逻辑,把原来的解码后判断是否包含…/修改成了isInvalidEncodedInputPath方法进行检查:
风险版本
修复版本
从修复代码看,猜测相关的利用主要与反斜线以及URL编码有关。尝试进行复现。
1.2 漏洞复现
1.2.1 环境搭建
根据之前的分析,反斜线很自然就联想到在Windows中能通过…\的方式来目录穿越。尝试在Windows环境下进行复现,通过RouterFunction模拟相关的静态资源映射:
通过上述映射,即可以通过/portal路由访问C盘Log目录下的静态资源。
spring相关版本:
1.2.2 复现过程
直接…/…/windows/win.ini方式尝试利用肯定是不行的。因为在isInvalidPath中会调用cleanPath进行处理,然后判断处理后的路径是否包含…/。其中会对反斜线进行相关的转换,最终会认为是非法路径:
结合前面的分析可以考虑URL编码,首先Spring自身会对路径做一层解码。所以得编码两次。例如下面的例子,此时isInvalidPath判断的是…%5c,不会认为是非法字符:
…%255c/…%255c/…%255c/windows/win.ini
然后调用createRelativeURL完成相应的封装:
在对应URL调用时,getInputStream中openConnection会再解码一次。此时即可绕过相关的安全限制。以读取Downloads下的test.txt为例。可以看到最终成功绕过对应的限制,跨目录获取到了相关的文件:
0x02 其他
实际上Spring历史上也出现过类似的风险,ResourceHttpRequestHandler 是 Spring MVC 中用于处理静态资源请求的一个关键组件。它实现了 HttpRequestHandler 接口,专门用于映射和处理静态资源文件的请求。
对比ResourceHttpRequestHandler和PathResourceLookupFunction的代码,其实在processPath的实现上是类似的。
org.springframework.web.reactive.function.server.PathResourceLookupFunction(Spring-WebFlux):
org.springframework.web.servlet.resource.ResourceHttpRequestHandler#getResource:
两者均是对反斜线进行了相关的处理,相对于ResourceHttpRequestHandler而言,主要是为了修复CVE-2018-1271:
除此之外,还可以看到其他漏洞的痕迹,spring-cloud-config历史目录穿越中,对于CVE-2019-3799的利用同样也是基于createRelative的逻辑,才会有类似双重URL编码的绕过利用:
…%252F…%252F…%252F…%252F…%252F…%252F…%252F…%252F…%252F…%252F…%252F…%252Fetc%252Fpasswd
感觉CVE-2024-38816的利用类似CVE-2019-3799和CVE-2018-1271的结合。