freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Apache Shiro 权限绕过漏洞CVE-2020-11989
2020-09-10 19:51:29

一、漏洞分析

1.1 Apache Shiro组件介绍

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。

1.2 漏洞描述

Apache Shiro 1.5.3之前版本,由于Shiro拦截器与requestURI的匹配流程和Web框架的拦截器的匹配流程有差异,攻击者构造一个特殊的http请求,可以绕过Shiro的认证,未授权访问敏感路径。此漏洞存在两种攻击方式。

1.3 漏洞分析

First Attack

传入的payload首先被服务器接收,并传送给Shiro拦截器处理(org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter方法作为入口)。

调用createSubject方法创建Subject,并调用execute方法进入Shiro FilterChain中。

进入org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain方法中,首先获取请求URI路径。

在Shiro1.5.2版本中,对于requestURI处理的方式存在一些不同,此处也是漏洞触发点所在。Shiro1.5.2使用的是request.getContextPath(),request.getServletPath(),request.getPathInfo()拼接的方式。由于getServletPath()方法会对requestURI进行一次url解码,在之后的decodeAndCleanUriString方法中进行第二次url解码。

回到getChain方法中,迭代获取拦截器的表达式。

这里重点关注/hello/*表达式。代码进入pathMatches方法,最终调用org.apache.shiro.util.AntPathMatcher#doMatch方法进行传入的requestURI与拦截器表达式进行匹配。

匹配过程中,分别将拦截器表达式与requestURI以/作为分隔符进行字符串到数组的转换,通过循环匹配数组中对应的元素,判断requestURI是否符合拦截器表达式匹配形式。

如果表达式中存在通配符*,会将containsStar标志位赋值为true,进入 else if (patIdxEnd == 0)判断条件,返回true。

最终回到doMatch方法中,通过判断表达式数组的元素个数与requestURI的元素个数,以及表达式中是否包含**,完成后续的匹配。

跟进到Spring处理URI的代码,进入org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法,获取requestURI。由于Spring获取requestURI时使用getRequestURI()方法,此方法不会进行URL解码。只会在decodeAndCleanUriString完成一次url解码。

进入lookupHandlerMethod方法,调用addMatchingMappings方法,获取Spring拦截器。

进入org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingCondition方法调用doMatch方法进行requestURI和拦截器表达式的匹配。

Spring拦截器匹配流程和Shiro大致相同,同样是将字符串转换为数组进行匹配。

由于Spring只进行了一次URL解码,所以将未完全解码的部分作为一个整体,从而完成了拦截器表达式与requestURI的匹配。

Second Attack

漏洞触发点同样是Shiro在修复CVE-2020-1957漏洞时,使用request.getContextPath(),request.getServletPath(),request.getPathInfo()拼接的方式,进行requestURI的获取。<br>直接跟踪到uri = valueOrEmpty(request.getContextPath()) + "/" + valueOrEmpty(request.getServletPath()) + valueOrEmpty(request.getPathInfo());

在调用getContextPath()方法获取context-path时,会调用removePathParameter方法清除掉分号以及分号到下一个/中间的数据。

接下来进入for循环中匹配candidate与conotext-path是否相同。

如果不同,则从传入的URL中继续读取下一级目录,直到condidate与context-path相同,返回从URL截取的目录作为contextPath。由于context-path获取方式和removePathparameters方法对URL的处理,攻击者可以请求,让contextPath变量获取到带有分号的非预期值。

在进行requestURI拼接时,构造出根路径带有分号的requestURI。利用CVE-2020-1957漏洞原理,经过decodeAndCleanUriString方法时,截断reqeustURI中分号后的数据,并返回。从而绕过了shiro权限控制。

回顾CVE-2020-1957漏洞

在URI正规化处理时,先调用decodeAndCleanUriString方法进行路径的解码,并清理URI。

进入decodeAndCleanUriString方法,发现此方法会以分号将传入的URI进行截断,并将分号以及分号后面的数据进行清空,返回分号前面的URI数据,从而让/a/b;/c变为/a/b。

继续跟进到Spring拦截器的decodeAndCleanUriString方法中。

从代码中可以发现,Spring对于分号处理的方式与Shiro不同,Spring会先获取分号的位置,并检测分号后是否存在/,如果有,将/的位置记录在slashIndex变量中,并将分号前的数据与/之后的数据进行拼接,从而让/a/b;/c变为/a/b/c。返回处理后的requestURI。

补丁分析

对比Shiro 1.5.2与Shiro 1.5.3版本的改动,在org.apache.shiro.web.util.WebUtils类中添加了删除requestURI结尾的/的代码。

补丁主要优化了getPathWithinApplication方法,并单独定义了getServletPath方法,getPathInfo方法。补丁修复后,调用getPathWithinApplication方法获取requestURI只会在进行getServletPath方法中进行一次url解码,保持与Spring获取requestURI过程中相同的url解码次数。防御了双重url编码绕过。

获取requestURI直接调用getServletPath方法和getPathInfo方法进行拼接,由于不需要与contextpath拼接,从而防御了First Attack攻击。

1.4 漏洞复现

搭建Apache Shiro漏洞环境,使用构造的payload进行攻击,最终绕过授权访问到未授权资源,效果如图:

正常访问:

First Attack

Second Attack

二、影响范围

目前受影响的Apache Shiro版本:

Apache Shiro < 1.5.3

三、修复建议

Apache Shiro最新版本已经修复此漏洞,请受漏洞影响的用户下载最新版本, 下载链接:http://shiro.apache.org/download.html

四、参考链接

1.http://shiro.apache.org/download.html

2.https://github.com/apache/shiro

# web安全 # apache漏洞
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
深信服千里目安全实验室
漏洞复现专栏
  • 0 文章数
  • 0 关注者