根据我了解到的信息,这是一个与 Spring Security 框架中的 RegexRequestMatcher 类相关的漏洞。具体来说,那些使用 RegexRequestMatcher 且在正则表达式中包含点号 (.) 的应用程序,可以被 \r(%0a)、\n(%0d) 这些字符绕过;从而攻击者无需身份验证即可访问不允许访问的路径。
Spring Security 框架中存在漏洞的版本:
5.5.x 早于 5.5.75.6.x 早于 5.6.4我们需要访问 Spring Security 的源代码来静态分析这个漏洞。具体来说,我使用了 Github 的 commits 比较功能,对比 5.6.3(存在漏洞的版本)和 5.6.4(已修复漏洞的版本)两个版本。参见以下链接:Comparing 5.6.3...5.6.4 · spring-projects/spring-security (github.com)

我检查了 RegexRequestMatcher 类中的改动。可以看到,在 5.6.4 版本中,该类使用了 Pattern.DOTALL,而不是像 5.6.3 版本那样使用默认的 .。
其中:
Pattern:是 java.util.regex 包中的 3 个类之一,用于处理正则表达式。Pattern.DOTALL:使用此标志时,正则表达式中的“.”将匹配所有字符,包括 \n , \r 等换行字符。Pattern.CASE_INSENSITIVE:不区分大写和小写字符。
默认情况下,正则表达式中的 . 会匹配除 \n, \r 等换行字符之外的所有字符。此时,如果某个正则函数用于校验某个字符串的模式,那么当字符串中包含换行字符时,该正则是无法匹配的。为了避免这种情况,可以使用 Pattern.DOTALL 标志。
然而,如果有人故意使用 %0d 代替 \n,或使用 %0a 代替 \r,上述正则仍然无法匹配。因此,在 5.6.4 版本中,RegexRequestMatcherTests.java 中额外增加了对这种情况的检查。具体来说,它会先将 %0d 和 %0a 分别转换为 \n 和 \r,然后再用正则进行检查。

步骤 1: 使用 Spring Initializr 创建一个 Spring Boot Web 应用程序,并附带 Spring Security 和 Spring Web 两个依赖。

步骤 2: 创建一个 Controller,当有请求到达 /admin/* 路径时,输出 This is a CVE-2022-22978 demo 这行文字。

步骤 3: 设置身份验证机制,每当用户访问 /admin/<bất kì> 路径时,使用 regexMatchers("/admin/.*").authenticated() 进行验证。这正是攻击者所利用的漏洞,可以在无需身份验证的情况下查看 /admin/<bất kì> 页面的内容。

步骤 4: 在配置文件中,声明包含漏洞的 Spring Security 版本。这里我选择版本 5.6.3。

步骤 5: 使用命令 gradlew bootRun 运行应用程序,程序默认使用 Apache Tomcat 监听 8080 端口。我们访问 /admin/xyz 路径(只要是以 /admin/ 开头的路径都可以)。

返回 403 Forbidden 状态码,意味着由于未进行身份验证,我无法访问。
此时,利用 Spring Security(版本 5.6.3)中 regexMatchers 函数的漏洞——它无法匹配 \r(%0d) 和 \n(%0a) 这类换行字符——我们便可通过 payload /admin/%0dxyz 在无需身份验证的情况下访问上述路径。

同样,使用 payload /admin/%0axyz

由此可见,我们仅用一个非常简单的 payload 就成功利用了 CVE-2022-22978 漏洞。
5.7.1
使用上述类似的 payload 对 Web 进行攻击尝试:/admin/%0dxyz

此时,应用程序不再返回攻击者所期望的响应。
git clone https://github.com/ducluongtran9121/CVE-2022-22978-PoC.git
cd CVE-2022-22978-PoC
gradlew bootRun
Java 18
Gradle 7.4.1