Apache Shiro 权限绕过漏洞(CVE-2020-13933)
前言
之前分析了 Apache Shiro 权限绕过漏洞(CVE-2020-11989),过了一段时间又出现了新的权限绕过漏洞 (CVE-2020-13933)。应该是在修复的基础上进行了绕过。 CVE-2020-11989 的影响版本为 Apache Shiro < 1.5.3 , CVE-2020-13933 的影响版本为 Apache Shiro < 1.6.0 。
在这个地方 Shiro 对 url 的处理是造成 CVE-2020-11989 的原因之一,Apache Shiro 1.5.3 对此进行的修复。
然后我们查看最新版的 Apache Shiro 1.6.0 发现在 web/src/main/java/org/apache/shiro/web/filter/InvalidRequestFilter.java
增加了
从全局上对分号,反斜杠和非ASCII字符进行了过滤
环境搭建
还是选择 https://github.com/l3yx/springboot-shiro
项目进行测试
下载完成后修改一下 pom.xml
中 org.apache.shiro
所对应的版本号 为 1.5.3 ,同时将 LoginController
中修改为
1 |
|
原因之后描述,同时为了方便调试,同时在 pom.xml 文件中加入
1 | <dependency> |
生成 war 包,部署于Tomcat
修改 \apache-tomcat-8.0.52\bin\catalina.bat
文件
1 | if not "%JPDA_ADDRESS%" == "" goto gotJpdaAddress |
catalina.bat jpda start
启动 ,配置 idea 中的远程调试
漏洞分析
Shiro 层
Tomcat 类中的 org.apache.catalina.core.ApplicationFilterChain
是用于管理针对请求 request 的过滤器。
Tomcat 的类 ApplicationFilterChain 是一个 Java Servlet API规范 javax.servlet.FilterChain 的实现,用于管理某个请求 request 的一组过滤器 Filter 的执行。当针对一个 reques 所定义的一组过滤器 Filter 处理完该请求后,组后一个 doFilter() 调用才会执行目标 Servlet 的方法 service(),然后响应对象 response 会按照相反的顺序依次被这些Filter处理,最终到达客户端。
根据调试时显示出的调用链,可以看到先执行到了 shiro 中的 OncePerRequestFilter
这个类
在 shiro 中 org.springframework.web.filter.OncePerRequestFilter
这个类是其他的所有 filter 的父类,所有的 filter 的 doFilter 方法都是调用的这个类中的 doFIlter 方法。
首先调用 getAlreadyFilteredAttributeName()
为过滤器标记,然后判断过滤器是否已经调用过,是否未为当前请求启用。
org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter
然后调用 doFilterInternal
方法,跟进后可以看到执行的是 public abstract class AbstractShiroFilter extends OncePerRequestFilter
中的 doFilterInternal
方法
org.apache.shiro.web.servlet.AbstractShiroFilter#doFilterInternal
跟进函数 executeChain
org.apache.shiro.web.servlet.AbstractShiroFilter#executeChain
跟进函数 getExecutionChain
org.apache.shiro.web.servlet.AbstractShiroFilter#getExecutionChain
跟进其中的 getChain
org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain
跟进方法 getPathWithinApplication
org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getPathWithinApplication
WebUtils#getPathWithinApplication
修复了之前 shiro 1.5.2 所存在的 url 双编码绕过问题。但是我们可以注意到最后的返回值是 /admin/
org.apache.shiro.web.util.WebUtils#getPathWithinApplication
org.apache.shiro.web.util.WebUtils#getServletPath
返回值为 /admin/;whippet
org.apache.shiro.web.util.WebUtils#getPathInfo
返回 ""
org.apache.shiro.web.util.WebUtils#removeSemicolon
将 ;
及其之后的全部删除
回到函数 org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain
继续向下执行
如果请求的不是 /
,就去除末尾的 /
,返回值就是 /admin
private static final String DEFAULT_PATH_SEPARATOR = "/";
接着根据 filterChainManager.getChainNames()
获取的拦截器进行匹配
org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#pathMatches
org.apache.shiro.util.AntPathMatcher#matches
org.apache.shiro.util.AntPathMatcher#match
org.apache.shiro.util.AntPathMatcher#doMatch
pattDirs
的最后一位是 *
所以会返回 false
没有匹配到会返回 null
匹配到的话会指向 ProxiedFilterChain
1.路径匹配:pathMatches(pathPattern, requestURI),默认的Fliter逐个与请求URI进行匹配;2、代理FilterChain:ProxiedFilterChain。如果匹配不上,那么直接走servlet的FilterChain,否则先走shiro的代理FilterChain(ProxiedFilterChain),之后再走servlet的FilterChain
继续单步执行
最后返回 ApplicationFilterChain
相当于并没有执行 Filter
此时就相当于已经绕过了 shiro 的权限验证,可以直接访问到需要权限目录下的文件,但是有时会返回这样的界面,是因为 Spring 并没有匹配到相对应的页面。
Spring层
chain.doFilter(request, response);
接下来的调用栈如图
Spring 在 Tomcat 中运行时需要提供对 Servlet 规范的支持,因为 Tomcat 时基于 Servlert 规范的 web 容器。 DispatcherServlet 是 Servlet 规范的具体实现。在 web 开发过程中,启动 Tomcat 容器时会根据其 Servlet 规范启动 Spring 实现的 DispatcherServlet ,这样就驱动了 Spring 的运行。
DispatcherServlet 在将请求映射到处理器时,调用了 getHandler
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
跟进 getHandlerInternal
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
通过 getLookupPathForRequest
获取请求的绝对路径
org.springframework.web.util.UrlPathHelper#getLookupPathForRequest(javax.servlet.http.HttpServletRequest)
org.springframework.web.util.UrlPathHelper#getPathWithinServletMapping
跟进函数 getPathWithinApplication
org.springframework.web.util.UrlPathHelper#getPathWithinApplication
跟进函数 getRequestUri
org.springframework.web.util.UrlPathHelper#getRequestUri
decodeAndCleanUriString
对 url 进行了解码处理
org.springframework.web.util.UrlPathHelper#decodeAndCleanUriString
此处存在一个问题,因为利用 ;
就可以直接绕过 shiro 的权限验证,但是为什么在直接使用 ;
时会返回 404 错误,在 spring 中不能找到该页面
org.springframework.web.util.UrlPathHelper#decodeAndCleanUriString
decodeAndCleanUriString
会先将 url
中 ;
后面的数据进行分割然后再进行 url 解码
解决了这个小小的问题,又产生了一个大大的疑问,在 Apache Shiro权限绕过漏洞分析(CVE-2020-11989) 一文中师傅所利用的 POC 为 /;/test/admin/page
如果是这样的话,从 ;
进行分割,最后得出来的应该是一直去请求 /
页面,不应返回权限下的页面,这个问题暂且放下,继续向下分析。
然后回到函数 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
跟进 lookupHandlerMethod
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod
跟进其中的 addMatchingMappings
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#addMatchingMappings
org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingCondition
org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingPatterns
此时我们可以注意到 /admin/{name}
与 /admin/;whippet
能够匹配成功。 会返回/admin/;whippet
的页面,此时的 name 值为 ;wippet
,如果之前我们并没有修改代码,而是固定的页面的话 访问 /admin/page
自然是与 admin/;page
匹配不上的。
参考文章
CVE-2020-13933: Apache Shiro 权限绕过漏洞分析
shiro源码篇 - shiro的filter,你值得拥有