产品介绍
Spring WebFlux 是 Spring Framework 5.0 中引入的新的响应式Web框架。与Spring MVC不同,它不需要Servlet API,是完全异步且非阻塞的,并且通过Reactor项目实现了Reactive Streams规范。Spring WebFlux 可用于创建基于事件循环执行模型的完全异步且非阻塞的应用程序。
漏洞概述
当WebFlux使用了Spring Security进行用户认证与鉴权时,若错误地使用了无前导”/“的路径通配符”**“,程序将因为WebFlux与Spring Security对其不同的解析模式差异导致可能的认证绕过。
受影响版本
-
6.1.0 to 6.1.1
-
6.0.0 to 6.0.4
-
5.8.0 to 5.8.4
-
5.7.0 to 5.7.9
-
5.6.0 to 5.6.11
漏洞分析
调试环境
使用idea新建基于maven的java项目,在pom.xml中引入相关依赖包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.14</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>ssawt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringSecurityAndWebfluxTest</name>
<description>SpringSecurityAntWebfluxTest</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
</project>
这里使用spring-boot-starter-parent版本为2.6.14,其依赖的Spring Security版本为5.6.9,为存在漏洞版本;
为了对比,使用spring-boot-starter-parent版本2.7.14作为漏洞修复版本,其依赖的Spring Security 版本为5.7.10,为修复版本。
为了能够进行漏洞复现,我们需要使用WebFlux编写一部分代码,并使用Spring Security作为认证框架。
上图是调试代码的结构,我们使用Spring Boot来搭建项目,并编写了一个控制器HelloController
,该控制器中定义了一个路由处理方法,即所有访问http://127.0.0.1:8080/hello-dev/xxxx
的请求都由该方法进行处理。
另外设置了5个响应处理器,分别对无访问权限、未认证、认证失败、认证成功、登出成功的情况进行处理。在AuthorizationConfig
中定义了基于内存的用户名密码以及路由规则
关于如何在WebFlux中使用Spring Security可以参考该文章:https://juejin.cn/post/7220662065294770233
漏洞原理
一般来说,对于只有漏洞编号的漏洞,我们若要进行复现,第一步需要做的就是进行信息收集,信息收集是为了达成以下几个目的:
-
获取官方或者第三方漏洞通告中关于漏洞描述的蛛丝马迹,这些通告中的漏洞描述往往泄露大量的信息;
-
是否已有安全研究员披露漏洞详情;
-
漏洞是否修复,修复的Commit ID是多少。
对于该漏洞来说,Spring 官方发布了一则通告:
https://spring.io/security/cve-2023-34034
在该漏洞描述中提到,在WebFlux中为Spring Security配置使用"**"这种模式的正则将导致Spring Security 与 Spring WebFlux的行为不一致从而导致认证绕过。这里可以提取到一个关键点就是需要在Spring Security中使用"**"进行模式匹配才会导致漏洞。当然这点信息可能不太够,所以我们继续搜索,于是发现了下面这篇帖子:
https://www.oschina.net/news/249933
这篇帖子里已经由关于该漏洞较多的信息泄露了。原来在Spring Security中”**“不能匹配路径分隔符"/",而在Spring WebFlux中却可以匹配路径分隔符。
可能看到这里,我们对具体的绕过姿势还不是特别清楚。在该通告的最后提供了该漏洞的修复记录:
https://github.com/spring-projects/spring-security/commit/7813a9ba26e53fe54e4d2ec6eb076126e8550780
在该修复记录中,增加了一处测试代码:
该测试模拟了一个HTTP请求”/user/test“,然后将其与路径模式”user/**“进行比较,如果比较结果为True
则测试通过。那么我们想想,这是不是说漏洞没有修复前这个比较结果为False,也就是说对于没有前导"/"并使用了"**"的路径模式是不能匹配带前导"/"的路径的?怀着这样的想法,我们修改我们的调试环境代码进行测试。
在控制器中设置一个不带前导"/"的路径模式
同样在Spring Security中也设置一个同样不带前导”/“的路径模式,两者保持一致,且访问该路径需要进行权限校验,并且其余所有的请求都不需要认证
此时将程序运行起来,然后访问http://127.0.0.1:8080/hello-dev/test
发现WebFlux正确地匹配到了该路径,但Spring Security却并没有匹配到(若匹配到应提示进行未登录),这正好印证了漏洞描述中说的Spring Security与Spring WebFlux匹配规则不一致的问题。为了进一步确认,我们在Spring Security认证代码中增加前导"/"
再次访问http://127.0.0.1:8080/hello-dev/test
响应结果提示未登录了。至此,我们已经了解了该漏洞的表现形式以及利用方法。要完成该漏洞的利用需要程序员错误的Spring Security 路径模式设置,即未给路径添加前导”/“,想来这种情况是比较少见的,所以该漏洞的影响其实是有限的。
知道了漏洞的表征,我们仍需要查看一下补丁以确认该漏洞是否真如我们猜想的这样,在config/src/main/java/org/springframework/security/config/web/server/AbstractServerWebExchangeMatcherRegistry.java
中新增了以下代码。
该修改主要修改了pathMatchers(java.lang.String...)
方法的实现逻辑,在进行路径匹配前首先调用了parsePatterns
方法对路径模式进行解析。parsePatterns
方法先获取到一个默认的路径模式解析器parser
,然后调用其initFullPathPattern
方法:
public String initFullPathPattern(String pattern) {
return StringUtils.hasLength(pattern) && !pattern.startsWith("/") ? "/" + pattern : pattern;
}
该方法判断传入的路径模式的长度若不为0,则判断其是否以"/"开头,若不是则添加前导"/",即强制路径模式以”/“开头,从而避免了该漏洞。
另一方面既然存在漏洞版本的Spring WebFlux能够正确解析无前导”/“的情况,我们仍需看看其解析逻辑。Spring WebFlux在扫描Controller时会依次扫描各方法的注解,而这些注解的处理类在org.springframework.web.reactive.result.method.annotation
包下面,而处理RequestMapping
的处理器为RequestMappingHandlerMapping
,在Bean装载时最终会调用到其createRequestMappingInfo
方法。
该方法首先会解析element是否含有RequestMapping这个注解,element是一个Method对象。如果有再调用createRequestMappingInfo
的重载方法。
在该方法中首先会创建一个builder,其封装了RequestMapping设置的请求方法、路径、参数、请求头等条件,然后调用build方法准备构建一个RequestMappingInfo
对象。
注意到此时设置的路径匹配模式仍然没有发生变化,在调用parse方法后会发生什么?
parse方法会判断路径匹配模式是否一”/“开头,若不是则补充。如此便能正常地进行路径匹配,这与存在漏洞的Spring Security版本处理逻辑不同,故两者的差异导致了漏洞产生。
修复措施
-
升级软件到不受影响的版本或最新版,官方仓库地址:https://github.com/spring-projects/spring-security/;
-
规范编码,避免不规范的路径匹配模式。
参考链接
-
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-34034
-
https://www.cvedetails.com/cve-details.php?t=1&cve_id=CVE-2023-34034
-
https://www.cnnvd.org.cn/home/globalSearch?keyword=CVE-2023-34034
-
https://spring.io/security/cve-2023-34034
-
https://www.oschina.net/news/249933
-
https://github.com/spring-projects/spring-security/commit/7813a9ba26e53fe54e4d2ec6eb076126e8550780
原文始发于微信公众号(一个安全研究员):漏洞复现 | WebFlux Security 认证绕过(CVE-2023-34034)
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论