问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
浅谈Spring Framework身份验证绕过漏洞(CVE-2023-20860)
渗透测试
Spring官方发布了Spring Framework 身份认证绕过漏洞(CVE-2023-20860),当Spring Security使用mvcRequestMatcher配置并将**作为匹配模式时,在Spring Security 和 Spring MVC 之间会发生模式不匹配,最终可能导致身份认证绕过。
0x01 漏洞描述 ========= Spring官方发布了**Spring Framework 身份认证绕过漏洞(CVE-2023-20860)**,当Spring Security使用mvcRequestMatcher配置并将`**`作为匹配模式时,在Spring Security 和 Spring MVC 之间会发生模式不匹配,最终可能导致身份认证绕过。 1.1 影响版本 -------- - Spring Framework 6.0.x <= 6.0.6 - Spring Framework 5.3.x <= 5.3.25 **(其他低于5.3.x的系列版本不受此漏洞影响)** 0x02 原理分析 ========= 2.1 MvcRequestMatcher --------------------- 根据漏洞描述,主要是mvcRequestMatcher的问题。参考https://docs.spring.io/spring-security/reference/servlet/integrations/mvc.html#mvc-requestmatcher 发现mvcRequestMatcher主要使用Spring MVC的HandlerMappingIntrospector来匹配路径并提取变量。相比AntPathRequestMatcher会更严谨。例如mvcMatchers("/index") ,除了匹配/index,对于/index/, /index.html, /index.do也会匹配。避免了AntPathRequestMatcher的绕过一些问题。 在查看mvcRequestMatcher首先简单描述下两个概念: - Pattern: ```java httpSecurity.authorizeRequests().mvcMatchers("admin/**").authenticated(); ``` 这里的`/admin/**`就是Pattern。 - Path:实际请求的路径 以spring-webmvc-5.3.20版本为例,查看具体实现,主要是在org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher#matches方法进行匹配: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-b22881ed7d058c04c907cc8125dd6d478c3febf5.png) 首先会在notMatchMethodOrServletPath方法对请求方法以及servletPath进行简单的比对: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-79319885440ee9c247fdfdf1cf1d90aec9ceec9a.png) 然后调用getMapping方法对当前请求进行处理(实际调用的是HandlerMappingIntrospector的getMatchableHandlerMapping方法): ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-d2616aed38474fa79fc559be9760ccce0d26de0b.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-315d672bd45aa09cb7b2bf5b723245dde30e6f17.png) 主要是寻找能处理指定请求的HandlerMapping,继续往下跟进会发现实际调用的是org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法,然后调用lookupHandlerMethod方法,首先直接根据路径获取对应的Mapping,获取不到的话调用addMatchingMappings遍历所有的ReuqestMappingInfo对象并进行匹配,实际上就是spring web解析的逻辑: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-a92986fa138cbc6c20b421535d68f2c00ed02b5c.png) 获取到mapping后调用对应的match方法,跟pattern进行匹配,然后将匹配的结果封装在RequestMatchResult中返回: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-0a80107d9fbf0f7eb7d8e9f44bc590a4f88cdb65.png) 继续跟进具体的match方法: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-67ddc7aed7b3edd5decb72059e4b2158bb03e9c0.png) 实际上调用的是org.springframework.web.servlet.handler.PathPatternMatchableHandlerMapping#match方法: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-1216d92cad7b98e455ea19487c6c45c515a361e0.png) 继续调用的PathPattern(根据影响的版本可以确定对应的Spring使用的是PathPattern进行解析)的matches方法将pattern跟path进行匹配: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-6a281f4fc12479f4684f26ad204f538e6b1e5e9f.png) 而PathPattern首先会根据/将URL拆分成多个**PathElement**对象,以/admin/index/为例,这里会分割成多个对象,然后根据PathPattern的链式节点中对应的PathElement的matches方法逐个进行匹配。 简单地分析了mvcRequestMatcher以后,看看PathPattern的工作原理。 2.2 PathPattern --------------- 2.6以及之后的Spring会使用PathPatternsRequestCondition通过PathPattern来进行URL匹配。 主要在org.springframework.web.util.pattern.PathPattern#matches方法: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-b5eb5831fe6c8b81af9ec220f5fec61bba741589.png) 首先会根据/将URL拆分成多个**PathElement**对象,以/admin/index/为例,这里会分割成多个对象,然后根据PathPattern的链式节点中对应的PathElement的matches方法逐个进行匹配: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-ee138fc27d43fae3a1107283853f36d89d6e56a9.png) 例如Pattern为/admin/\*的话,首先第一个元素是分隔符`/`,会调用SeparatorPathElement的matches方法进行处理: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-ea070694325c12181cd7af4fb584f1301817d90b.png) 处理完后pathIndex++,继续遍历下一个元素进行处理,下一个是admin,会通过LiteralPathElement#matches进行处理,同样的最后会对pathindex进行+1,然后继续遍历PathElement元素直到遍历结束为止: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-92fdc1d29344ed5788b503ea11e2578060cfcb61.png) 在最后会根据matchOptionalTrailingSeparator(此参数为true时,默认为true)进行一定的处理,如果Pattern尾部没有斜杠,请求路径有尾部斜杠也能成功匹配(类似TrailingSlashMatch的作用): ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-426fe615e29798929c1032e6bdc16b34970daa51.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-ace95217ebb5963634c1d86b64fa07edfa90cf9a.png) 所以这里/admin/index和/admin/index/都是可以访问到对应的路由的。 除此之外,根据不同Pattern的写法,还有很多PathElement: - WildcardPathElement(/api/\*) - SingleCharWildcardedPathElement(/api/?) - WildcardTheRestPathElement(/api/\*\*) - CaptureVariablePathElement(/api/{param}) - CaptureTheRestPathElement(/api/{\*param}) - LiteralPathElement(/api/index) - RegexPathElement(/api/.\*) 2.3 绕过分析 -------- 根据前面的分析,因为mvcRequestMatcher主要使用Spring MVC的HandlerMappingIntrospector来匹配路径并提取变量。猜测大致的问题也应该出现在这里。对比修复前后版本的HandlerMappingIntrospector代码: 这里的返回值从PathSettingHandlerMapping变成了LookupPathMatchableHandlerMapping: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-c6abb955e34a2f59b4b164dd2a7616431402bb1d.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-ed47b8ee9865bc993f884787ca249b97ac3ad662.png) 结合前面的分析可知,修改的其实是在调用PathPattern的match方法前的处理。对比下代码可以发现,在调用PathPattern的match方法之前多了一个步骤,首先**判断pattern是否以`/`开头,如果不是的话进行补全**: - spring-webmvc-5.3.26 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-ce289d7f29c54b017833d8c78187da15cb94430c.png) - spring-webmvc-6.0.7 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-c6806e0bcf704ddb46d7a50dbdc7ed23278b081d.png) 根据前面的分析,做以下猜想,在Spring Controller中,以下两个路由访问是等价的: ```Java @GetMapping("/admin/*") @GetMapping("admin/*") ``` 当Spring Security使用mvcRequestMatcher模式进行权限控制时,如果对应的配置没有以`/`开头,根据前面的分析,猜测会因为解析差异导致绕过的问题。 例如如下配置,admin目录下的路由会经过认证处理,非认证通过的会返回403: ```Java @Override protected void configure(HttpSecurity httpSecurity) throws Exception{ httpSecurity.authorizeRequests().mvcMatchers("admin/**").authenticated(); } ``` Controller: ```Java @GetMapping("/admin/index") public String Manage(){ return "manage"; } ``` 根据前面对PathPattern的matches的分析,这里会根据PathPattern的链式节点中对应的PathElement的matches方法逐个进行匹配,当pattern为`admin/**`时,此时第一个Element是admin,对应LiteralPathElement#matches解析: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-64ed6f923b99503d6ca0b8dddd7b3ef879fb1f9d.png) 此时会获取path的第一个Element(如果访问的path是/admin/index,那么第一个Element是`/`): ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-8d63417535783f45c9b0419eee98185e16f74563.png) 而`/`明显不是PathSegment的实例,此时匹配失败会返回false,但是Spring Controller却能正常解析,那么便导致了绕过问题。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-533b69e1b4ab4c180e0704fed99b3554732d0746.png) 0x03 漏洞复现 ========= 根据前面的猜想,同样的是前面的case,当对应的配置没有以`/`开头,会因为解析差异导致绕过的问题,可以看到成功绕过了设置的Spring security规则,访问了/admin/index: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-c563e685d24f0ace780b17d7c7aa22ff86a47bac.png) 这里再做一个对比,将spring-webmvc切换到5.3.9版本,此时上述的case是无法绕过的: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-d3de2d361a504bd0648f03c9c0587ed706553341.png) 0x04 其他 ======= 在Spring生态中,除了PathPattern以外,还有一种解析模式是AntPathMatcher。 同样的根据之前的分析,其实主要是PathPattern的解析方式问题,这里再做一个假设,同样是存在缺陷的版本spring-webmvc-5.3.20版本,此时通过properties配置切换匹配模式为AntPathMatcher: ```php spring.mvc.pathmatch.matching-strategy = ant_path_matcher ``` 此时发现没办法绕过了: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-576b5055d83f7221140f6002aca6ad4575c3e3e6.png) 简单说下原因,主要是AntPathMatcher的实现,其大概方式是将需要匹配的path和Pattern分割成string数组,分别是pathDirs和pattDirs两个数组,然后从左到右开始匹配,主要是一些正则的转换还有通配符的匹配。例如/admin/\*的`*`实际上是正则表达式`.*`,然后通过java.util.regex.compile#matcher进行匹配,这里进行分割时不会将`/`作为内容引入分析,从调试信息也可以看到,在此之前也将`/`进行了补全: ![图片.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-6ef52db757a2c3eabebe6293574cd2384f27d52a.png) 将`/`进行了补全的操作主要是在RequestMappingHandlerMapping#match方法中进行的: ![图片.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-997d3c24b87ab24283e9e9afdba361579d29870d.png) 这里实际上调用的是RequestMappingInfo#build方法: ![图片.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-bbcd5f2763f9f08f2b1c7b4a898148179506785a.png) 这里对Pattern进行了重新处理,在调用PatternsRequestCondition的构造方法的时候,调用了initPatterns方法: ![图片.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-526f1b7d1397e56bd6858e02f9cdd1b078e83624.png) 如果Pattern不是以`/`开头会进行补全: ![图片.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-ad1558f15b33e39692fd8571e767db245ff1d547.png) 0x05 修复方式 ========= 目前官方已有可更新版本,建议升级至: - Spring Framework 6.0.x >= 6.0.7 - Spring Framework 5.3.x >= 5.3.26 同样是上面的case,将spring-webmvc版本升级到5.3.26后,上述case已无法绕过: ```XML <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.26</version> </dependency> ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-4b12bfd8f7558e7477cf219edfd26f7e7e6fe34a.png)
发表于 2023-03-30 09:00:02
阅读 ( 15810 )
分类:
漏洞分析
1 推荐
收藏
2 条评论
肾虚小王子
2023-08-10 18:30
请问这个pom文件能发一下吗,我用springboot搭建起来,没有复现成功
tkswifty
回复
肾虚小王子
Spring Framework 6.0.x <= 6.0.6 Spring Framework 5.3.x <= 5.3.25 (其他低于5.3.x的系列版本不受此漏洞影响)
请先
登录
后评论
请先
登录
后评论
tkswifty
64 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!