![](/d/file/p/2023/11-24/851bcd29dda725bcd6b9ca354b197c05.png)
在搜文章的过程中看到先知上的《CVE-2022-22978 Spring Security RegexRequestMatcher 认证绕过及转发流程分析》,刚好自己对spring了解不多。就跟着学习一下文中作者对spring的高低版本对业务转发的分析,记录一下笔记。
环境搭建非常简单,可以直接参考上面提到的文章内容或者白帽汇的《Spring Security RegexRequestMatcher 认证绕过漏洞分析(CVE-2022-22978)》。也可以直接使用零号靶场:http://galaxy.pingan.com.cn/pingan/range/detail/3789正则中,通常用点号.来匹配任意字符,但是默认情况下,它不会匹配换行符的(linux下\n)。而在RegexRequestMatcher中,就采用了默认匹配,导致可以向url中插入%0a(\n)或%0d(\d)就绕过正则匹配,从而绕过Spring Security的认证,成功请求后端资源。![](/d/file/p/2023/11-24/a4b3865a8a44dea65a8f1ab8795153bd.png)
可以看到在有漏洞的5.6.3版本中,DEFAULT = 0,而在5.6.4版本中设置了DEFAULT = Pattern.DOTALL。通过使用Pattern.DOTALL,就改变了正则中.的默认匹配行为,使得.会匹配包括\n、\r在内的任意字符。Spring 2.5.3的测试结果
将依赖改为spring 2.5.3,使用同样的payload进行测试,这时可以绕过Spring Security的认证,但是请求无法找到后端资源进行处理。![](/d/file/p/2023/11-24/316a2fa72878e4d032402c30bc350539.png)
这时候要想搞明白为什么在不同版本下程序会由不同的表现,就需要去看一看spring处理路由的代码了。
在Dispatcher中找到获取Handler的位置下断![](/d/file/p/2023/11-24/83aa1baec7c543103c119931a7a362e9.png)
![](/d/file/p/2023/11-24/321ccbd591c5ec8274355e59722612ed.png)
![](/d/file/p/2023/11-24/64501e10813749f4ea1ccc3051e73476.png)
![](/d/file/p/2023/11-24/47eba1d3c80e93fd3f5a51fb97f50f25.png)
![](/d/file/p/2023/11-24/ed8daf4fd1a8d22bf0a159b5dd46204e.png)
![](/d/file/p/2023/11-24/3ec7aa51402c8128bb5f6d7a3737ff46.png)
![](/d/file/p/2023/11-24/86a56a0d794fafbbb3a88ca0bf127ace.png)
![](/d/file/p/2023/11-24/a4bafca8a725cf7e29516d23807a8e6c.png)
一路跟踪下来,最终会来到pathPatternsCondition分支,而在2.5.3版本中,会进入patternsCondition分支。它们分别调用PathPatternsCondition#getMatchingCondition和PatternsCondition#getMatchingCondition。version2.7.2 – PathPatternsCondition
一路往下跟踪,最终由于路由配置中使用了*,对应WildcardPathElement,在其中不会利用正则匹配,而是匹配到所在位置有内容,则返回true。![](/d/file/p/2023/11-24/62f7a25b5cc246cacf7ec7820e736023.png)
version2.5.3 – PatternsCondition
而在PatternsCondition#getMatchingCondition中,一路跟踪,Spring会取出对应位置,然后进行字符串的正则匹配。![](/d/file/p/2023/11-24/c8120555761ca7f1a4785567d395c06d.png)
![](/d/file/p/2023/11-24/c790ab831b6daace0d295583f4caacd6.png)
而this.pattern为java.util.regex.Pattern对象,在默认情况下它同样只会匹配换行符之外的字符。所以对于此处传入的路径无法正确匹配,返回false,最终导致无法找到handler。
![](/d/file/p/2023/11-24/a3cfe7be59babeeac9f24393d8ab00d5.png)
![](/d/file/p/2023/11-24/4d9a17f2256e155de46c886e00c355d6.png)
>>>> 区别
综上所述,其实2.7.2和2.5.3两个版本的不同在于,在RequestMappingInfo#getMatchingCondition中,由于前期创建的RequestMappingInfo有差异,导致进入了不同的分支。
而RequestMappingInfo取自RequestMappingHandlerMapping.mappingRegistry,关于MappingRegistry的注册过程直接参考:https://blog.csdn.net/weixin_42859458/article/details/118332596
直接在registerHandlerMethod中下断,回溯调用栈可以发现RequestInfo通过getMappingForMethod创建。
![](/d/file/p/2023/11-24/817567611c03ad1388b77a8b5099d759.png)
最终调用到RequestMappingInfo.Builder#build在这里面2.7.2和2.5.3的实现就有所不不同了2.7.2中this.options.patternPatser!=null,进入if块,创建了PathPatternsReuqestCondition,而patternsCondition为null。![](/d/file/p/2023/11-24/1fadcd94a553710769c3f9ba642711c3.png)
在2.5.3则情况相反,this.options.patternParser == null,进入else块,创建PatternsCondition,而pathPatternsCondition为null。
![](/d/file/p/2023/11-24/96053ee05738294fa602f196b7e55d3b.png)
正则表达式单行模式
单行模式(single line mode): 使得 通配符点”.” 匹配所有字符,包括换行符(默认情况下,点是不会匹配换行符的)。不过这个模式不被Javascript和Ruby支持。
所以其实就算Spring Security没有做升级修复,也可以通使用单行模式,增加对换行符的匹配,从而避免被绕过。参考
1. Spring Security RegexRequestMatcher 认证绕过漏洞分析(CVE-2022-22978)https://nosec.org/home/detail/5006.html
2. CVE-2022-22978 Spring Security RegexRequestMatcher 认证绕过及转发流程分析
https://xz.aliyun.com/t/11473
![](/d/file/p/2023/11-24/39ae7072ab87362163f2f80d1ef36562.png)
评论