首页 > 其他分享 >浅谈Apache与CVE-2023-20860

浅谈Apache与CVE-2023-20860

时间:2023-07-07 15:36:04浏览次数:58  
标签:匹配 浅谈 admin Spring 20860 matches pattern Apache shiro

一、前言

  一般情况下,开发者配置鉴权时,可能都会遵循一个原则,就是"优先使用/**认证兜底,明确哪些接口无需认证,而不是明确哪些接口需要认证。

  在Spring Controller中,以下两个路由访问是等价的(熟悉spring framework源码的话,会知道其实都是在解析pattern前做了补全的处理(不论是AntPathMatcher还是高版本的PathPattern)):

@GetMapping("/admin/*")
@GetMapping("admin/*")

  所以很多时候有的开发者会会直接认为**代表全路径,确实在某些鉴权框架中也生效了。

1.1 关于CVE-2023-20860

  前段时间Spring官方发布了Spring Framework 身份认证绕过漏洞(CVE-2023-20860),当Spring Security使用mvcRequestMatcher配置并将**作为匹配模式时,在Spring Security 和 Spring MVC 之间会发生模式不匹配,最终可能导致身份认证绕过。首先简单回顾下漏洞的原理:

  对比commit可以看到在调用PathPattern的match之前,首先判断pattern是否以/开头,如果不是的话进行补全(以修复版本spring-webmvc-5.3.26为例):

  • PS: 下面代码块中的/admin/**就是pattern:
httpSecurity.authorizeRequests().mvcMatchers("/admin/**").authenticated();

wKg0C2QnsFSAThNqAAERs41afZo945.png

  结合漏洞描述以及对commit的分析,大致绕过的是如下的配置:

httpSecurity.authorizeRequests().mvcMatchers("**").authenticated();

  结合影响的版本可以确定对应的Spring应用使用的是PathPattern进行解析。而MvcRequestMatcher实际上使用Spring MVC的HandlerMappingIntrospector来匹配路径并提取变量,对应的影响判断在匹配pattern跟请求的path时使用的也是PathPattern。根据PathPattern的解析方式,大致可以知道漏洞的成因了。

  首先PathPattern会根据/将URL拆分成多个PathElement对象,然后根据其的链式节点中对应的PathElement的matches方法逐个进行匹配。正常情况下,/**这个Pattern对应的matches是WildcardTheRestPathElement。

  因为PathPattern通配符只能定义在尾部(不能以/结尾),所以当pathElements的元素个数大于PathPattern中的元素个数即可匹配:

wKg0C2QnskSAGxdtAABZf2MEWQI945.png

  所以此时正常情况下访问/admin/page无法绕过。

  但是如果此时使用的是**这个Pattern,因为漏洞版本缺少了补全/的操作(修复版本会判断pattern是否以/开头,如果不是的话进行补全),那么此时对应的matches是RegexPathElement:

  就跟命名里的Regex一样,这个matches主要是通过java.util.regex.compile#matcher处理,看看当前pathElements的内容是否符合正则预期:

wKg0C2QntAWAGe47AACpiTypOJc125.png

  此时获取textToMacth,对于/admin/page这个path第一个元素是/,此时会返回空字符串:

wKg0C2QntG2ANJkVAAA1Win1LIk913.png

  虽然*能匹配到空字符串,但是这里还有一个逻辑,首先pathIndex+1后明显小于matchingContext.pathLength,此时matches为false,再往下的逻辑,pathIndex+1后对应的元素并不是/,而是admin,此时匹配失败,auth Bypass:

matches = pathIndex + 1 >= matchingContext.pathLength && (this.variableNames.isEmpty() || textToMatch.length() > 0);
if (!matches && matchingContext.isMatchOptionalTrailingSeparator()) {
    matches = (this.variableNames.isEmpty() || textToMatch.length() > 0) && pathIndex + 2 >= matchingContext.pathLength && matchingContext.isSeparator(pathIndex + 1);
}

wKg0C2QnthKAQF27AABP1i8TnsI535.png

wKg0C2QntkOAPeMsAAB6y0MrZDI315.png

  在Java生态中,还有一个常用的鉴权组件shiro,那么shiro请求解析的过程又是怎么样处理的呢,是否也会有类似的问题。查看shiro的源码进行简单的分析:

wKg0C2QntreAOQhZAAAk5q5p27s891.png

二、特殊场景下的shiro auth Bypass

  结合上述背景,查看shiro能否按期我们的预期处理**

  假设设置对应的url和过滤器匹配规则如下:

@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean(){
    ShiroFilterConfiguration shiroFilterConfiguration = new ShiroFilterConfiguration();
    shiroFilterConfiguration.setStaticSecurityManagerEnabled(true);
    shiroFilterConfiguration.setFilterOncePerRequest(true);

    ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    bean.setShiroFilterConfiguration(shiroFilterConfiguration);

    bean.setSecurityManager(securityManager());
    bean.setLoginUrl("/login");
    bean.setSuccessUrl("/index");
    bean.setUnauthorizedUrl("/unauthorizedurl");
    Map<String, String> map = new LinkedHashMap<>();
    map.put("/**", "authc");
    map.put("/doLogin", "anon");
    bean.setFilterChainDefinitionMap(map);
    return  bean;
}

  按照预期的设定,按道理是能对以下Controller进行防护的:

@GetMapping("/admin/page")
public String admin() {
    return "admin page";
}

  实际上这个配置并不会生效,还是可以访问到对应的Controller:

wKg0C2QnuMAfArhAABJlnDpfHM504.png

  以1.10.0版本的shiro为例,查看具体的原因:

  当发起HTTP请求时,Shiro 的多个过滤器形成了一条链,所有请求都必须通过这些过滤器后才能成功访问到资源。简单看下Shiro拦截请求处理的过程。

  查阅相关资料,shiro 发挥作用的入口是在org.apache.shiro.spring.web.ShiroFilterFactoryBean.SpringShiroFilter中,其中它继承自 OncePerRequestFilter,字面上看是每个请求执行一次。

  在接收到请求时会先进入 OncePerRequestFilter.doFilter() ,在这里写一个断点:

wKg0C2Qnuo2AG170AAB7mr12Ggc457.png

  这里会做一些简单的判断,然后org.apache.shiro.web.servlet.AbstractShiroFilter#doFilterInternal方法,首先会对request 和 response 对象进行包装,然后调用createSubject方法,这里会处理认证授权信息并进行封装:

wKg0C2QnurCAfXorAAB2XxGTqws584.png

  接着在Callable修改了最近一次的访问时间,然后调用 FilterChain,实际上调用的是org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain来获取(会根据URL路径匹配,解析出ServletRequest请求过程中要执行的过滤器链):

wKg0C2QnutKAYBNPAAA3S40XwbU850.png

  根据前面的分析,在org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain来获取其会根据URL路径匹配,解析出ServletRequest请求过程中要执行的过滤器链:

wKg0C2QnuQiAezRjAAAufuAy2HQ389.png

  首先调用getPathWithinApplication方法获取应用程序内的URI的相对路径,然后往下遍历filterChains,requestURI和pattern匹配的话会代理到 filterChainManager.proxy方法里去,如果不能匹配,会删除最后的"/" 再匹配一次:

wKg0C2QnuSuAV47GAACSYvpaD0407.png

  shiro使用的是AntPathMatcher进行匹配的,如果请求的path和pattern没有以/,就不再进行匹配了,与Spring不同的是,shiro在匹配前并不会对pattern进行检查,补全开头的/:

wKg0C2QnuWCAM7F4AABwG9IAzhY357.png

  根据AntPathMatcher的代码可以知道,如果pattern没有以/开头会直接匹配失败。会导致前面的对于**配置失效,可以看到filterChain仅仅返回了InvalidRequestFilter,并没有返回authc对应的Filter(权限控制失效):

wKg0C2QnuaCABNxOAABcwMkRFyM125.png

PS:实际上只要不以/开头的Pattern,Shiro都会找不到对应的Filter。所以类似admin/*的配置也是会auth Bypass的。

三、其他

  相比CVE-2023-20860,**这个auth Bypass并不是由于shiro与Spring Framework的解析不一致导致的。这个问题也第一时间反馈给了Apache,但是Apache认为这不能算一个漏洞:

wKg0C2Qnu96AELGAAAtrtUDs287.png

  实际上在开发过程中由于各个框架间的差异导致的安全问题是十分常见的,如何正确的使用现有框架,规避不必要的安全风险是一个值得思考的问题。

标签:匹配,浅谈,admin,Spring,20860,matches,pattern,Apache,shiro
From: https://www.cnblogs.com/SecIN/p/17535103.html

相关文章

  • Apache DolphinScheduler 荣获“掘进技术引力榜”「2023 年度 ROBUST 开源项目」奖项!
    经过紧张激烈的投票和严格的专家评审环节,“掘进技术引力榜”活动在上周的稀土掘金开发者大会上公布了「2023年度ROBUST开源项目」奖项的获奖名单,ApacheDolphinScheduler名列其中。ApacheDolphinScheduler代表上台领奖(右三)掘金技术引力榜「2023年度ROBUST开源项目」奖......
  • 盘点2021年Apache年报中出现的国产项目
    盘点2021年Apache年报中出现的国产项目:ShardingSphere,IoTDB,CarbonData,Eagle,Kylin,Apisix,DolphinSchedulerandEcharts1、引言2021年8月31日,Apache软件基金会发布2021财年(2020年5月1日-2021年4月30日)年度报告,报告内容由Apache软件基金会概览、......
  • Failed to instantiate [org.apache.tomcat.jdbc.pool.DataSource]
    问题:项目中没有使用db相关的东西,但是在应用启动时报错:Failedtoinstantiate[org.apache.tomcat.jdbc.pool.DataSource]原因:  pom.xml中配置了和数据库相关的,SpringBoot启动默认会加载org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration类,DataSo......
  • org.apache.poi.openxml4j.exceptions.InvalidOperationException: Can't open the sp
    环境和所需包:      1,JDK1.5      2,poi-3.5-FINAL-20090928.jar,           poi-contrib-3.5-FINAL-20090928.jar,           poi-ooxml-3.5-FINAL-20090928.jar,           poi-scratchpad-3.5-FINAL-20090928.jar,        ......
  • 浅谈 JEP290
    0x01前言属于是拖了很久的文章了,4.18筹划着开始写,6.22左右才真正开始提笔。一开始提到这个概念可能会比较懵逼,其实这就是为什么高版本jdk有部分能打jndi,打不了RMI8u121~8u230打不了RMI0x02关于JEP290JEP290是Java底层为了缓解反序列化攻击提出的一种解决方案......
  • org.apache.log4j.Logger
    Log4j 简介Log4j 是 Apache 的一个开放源代码项目,通过使用 Log4j ,我们可以控制日志信息输送的目的地是控制台、文件、 GUI 组件、甚至是套接口服 务器、 NT 的事件记录器、 UNIXSyslog 守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别......
  • 浅谈斜率优化
    如果一个DP的转移方程可以写成\(f_i=\underset{j<i}{\min\!/\!\max}\>\{f_j+a_i\timesb_j+c_i+d_j\}+C\)的形式,那么可以运用斜率优化。不妨设转移是\(\min\),设\(g_{i,j}=f_j+a_i\timesb_j+c_i+d_j\),即\(f_i=\min\limits_{j<i}g_{i,j}\),式子可以化为\(f_j+d_j=-a_i\time......
  • 浅谈java8中map的新方法
    Map在java8中新增了两个replace的方法1.replace(k,v)在指定的键已经存在并且有与之相关的映射值时才会将指定的键映射到指定的值(新值)在指定的键不存在时,方法会return回来一个nulljavadoc的注释解释了该默认值方法的实现的等价Java代码:if(map.containsKey(key)){returnmap.put(ke......
  • Apache Commons IO
    ​ CommonsIO是ApacheCommons的子项目,提供了对IO操作的封装和扩展,包括文件操作、流操作、文件拷贝等,简化了JavaIO的使用。 CommonsIO是一个用于处理输入输出的常用Java库。它提供了一些常见且有用的实用方法,简化了文件操作、流操作和其它与输入输出相关的任务。要使......
  • Apache Commons IO
    ​ CommonsIO是ApacheCommons的子项目,提供了对IO操作的封装和扩展,包括文件操作、流操作、文件拷贝等,简化了JavaIO的使用。 CommonsIO是一个用于处理输入输出的常用Java库。它提供了一些常见且有用的实用方法,简化了文件操作、流操作和其它与输入输出相关的任务。要使......