Java Shiro 权限绕过

您所在的位置:网站首页 apache拦截过滤配置 Java Shiro 权限绕过

Java Shiro 权限绕过

#Java Shiro 权限绕过| 来源: 网络整理| 查看: 265

前言:这篇作为Shiro的权限绕过的笔记

这篇文章作为之前笔记Filter权限绕过笔记的拓展,Filter权限绕过笔记:https://www.cnblogs.com/zpchcbd/p/14815501.html

参考文章:https://shiro.apache.org/security-reports.html 参考文章:https://www.anquanke.com/post/id/240033 参考文章:https://zhuanlan.zhihu.com/p/359199157 参考文章:https://blog.riskivy.com/shiro-权限绕过漏洞分析(cve-2020-1957)/

什么是Shiro

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

关于Shiro漏洞历史线

CVE-2016-4437(shiro-550) Apache Shiro before 1.2.5, when a cipher key has not been configured for the “remember me” feature, allows remote attackers to execute arbitrary code or bypass intended access restrictions via an unspecified request parameter.

CVE-2016-6802(权限绕过) Apache Shiro before 1.3.2 allows attackers to bypass intended servlet filters and gain access by leveraging use of a non-root servlet context path.

CVE-2019-12422(shiro-721) Apache Shiro before 1.4.2, when using the default “remember me” configuration, cookies could be susceptible to a padding attack.

CVE-2020-1957(权限绕过) Apache Shiro before 1.5.2, when using Apache Shiro with Spring dynamic controllers, a specially crafted request may cause an authentication bypass.

CVE-2020-11989(权限绕过) Apache Shiro before 1.5.3, when using Apache Shiro with Spring dynamic controllers, a specially crafted request may cause an authentication bypass.

CVE-2020-13933(权限绕过) Apache Shiro before 1.6.0, when using Apache Shiro, a specially crafted HTTP request may cause an authentication bypass.

CVE-2020-17510(权限绕过) Apache Shiro before 1.7.0, when using Apache Shiro with Spring, a specially crafted HTTP request may cause an authentication bypass.

If you are NOT using Shiro’s Spring Boot Starter (shiro-spring-boot-web-starter), you must configure add the ShiroRequestMappingConfig auto configuration to your application or configure the equivalent manually.

CVE-2020-17523(权限绕过) Apache Shiro before 1.7.1, when using Apache Shiro with Spring, a specially crafted HTTP request may cause an authentication bypass.

环境搭建

第一步

第二步

第三步:

第四步:

maven添加依赖

org.apache.shiro shiro-spring 1.4.2

前置知识点 在tomcat和shiro的filter中,哪个会先进行执行?后进行执行?

我这里看到堆栈的调用链,所以调用的过程是从下往上,那么就是tomcat相关的filter先执行,接着才是shiro的filter执行

关于Shiro拦截器

Shiro框架通过拦截器功能来实现对用户访问权限的控制和拦截。

Shiro中常见的拦截器有anon,authc等拦截器,还有其他的,这里是探讨关于authc拦截器的绕过。

1.anon为匿名拦截器,不需要登录就能访问,一般用于静态资源,或者移动端接口 2.authc为登录拦截器,需要登录认证才能访问的资源。 ...

关于shiro的拦截匹配模式

Shiro的URL路径表达式为Ant格式,org.springframework.util.AntPathMatcher

/hello:只匹配url,比如 http://demo.com/hello /h?:只匹配url,比如 http://demo.com/h+任意一个字符 /hello/*:匹配url下,比如 http://demo.com/hello/xxxx 的任意内容,不支持匹配多层路径 /hello/**:匹配url下,比如 http://demo.com/hello/xxxx http://demo.com/hello/xxxx/aaaa ,支持匹配多层路径

shiro是如何运行的?

我这里也不太懂shiro,这里就正常的看下shiro源码是如何运行的就可以了,简单的学下

shiroConfig需要通过ShiroFilterFactoryBean类来进行配置,一个正常的ShiroConfig.java如下所示

@Configuration public class ShiroConfig { @Bean MyRealm myRealm(){ return new MyRealm(); } @Bean public DefaultWebSecurityManager manager(){ DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(myRealm()); return manager; } @Bean public ShiroFilterFactoryBean filterFactoryBean(){ ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(manager()); factoryBean.setUnauthorizedUrl("/login"); factoryBean.setLoginUrl("/login"); Map map = new HashMap(); map.put("/login", "anon"); factoryBean.setFilterChainDefinitionMap(map); return factoryBean; } }

对于路径的访问权限的控制都是基于ShiroFilterFactoryBean类来进行配置的

@Bean public ShiroFilterFactoryBean filterFactoryBean(){ ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(manager()); factoryBean.setUnauthorizedUrl("/login"); factoryBean.setLoginUrl("/login"); Map map = new HashMap(); map.put("/login", "anon"); factoryBean.setFilterChainDefinitionMap(map); return factoryBean; }

这里可以来到ShiroFilterFactoryBean类中,看它是如何作用的,该类中存在一个getObject方法

public Object getObject() throws Exception { if (instance == null) { instance = createInstance(); } return instance; }

其中createInstance方法是来创建相关Shiro Filter,结果是返回一个继承了AbstractShiroFilter的SpringShiroFilter对象

protected AbstractShiroFilter createInstance() throws Exception { log.debug("Creating Shiro Filter instance."); // 创建shiro Filter的实例 SecurityManager securityManager = getSecurityManager(); if (securityManager == null) { String msg = "SecurityManager property must be set."; throw new BeanInitializationException(msg); } if (!(securityManager instanceof WebSecurityManager)) { String msg = "The security manager does not implement the WebSecurityManager interface."; throw new BeanInitializationException(msg); } FilterChainManager manager = createFilterChainManager(); PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver(); chainResolver.setFilterChainManager(manager); return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver); }

SpringShiroFilter这个对象是继承了AbstractShiroFilter,而这个AbstractShiroFilter中存在一个doFilterInternal,这个方法我自己有了解,因为Filter的内存文章中tomcat这个其中的InternaldoFilter就是去调用我们每个Filter对象中实现的doFilter方法的一个方法,而这里的doFilterInternal可能不太一样,它是通过executeChain来进行链式调用传入的chain参数,这个chain是一个FilterChain对象

其中executeChain方法来对传入的FilterChain对象进行处理调用doFilter方法

此时前面的tomcat的Filter对象中的doFilter已经走完了,tomcat的走完了才开始走shiro的filter(PS:此时tomcat的过滤器并不是完全走完,还有最后一个在shiro的后面)

接着就是getExecutionChain方法,其中就会通过FilterChainResolver的getChain方法来进行解析,从这里开始shiro的拦截器就开始进行发挥作用了,这里如果是被正确的拦截了,那么原始的chain则会被替换为shiro的filterChainManager.proxy(originalChain, pathPattern)所返回的FilterChain,如果没有被拦截则最后不会被替换,还是走原来的chain

那么shiro是如何判断是否被拦截的?可以继续看

继续跟到pathMatches方法中去看,他会用自己的一个路径匹配器PathMatcher来进行比较

最后doMatch方法获取 需要校验的路径 和 当前客户端访问的路径来进行比较,是否是需要进行拦截的

判断是否需要拦截,具体根据其中的matchStrings方法来进行判别,到这里一次请求的判断就结束了

接下来看漏洞分析...

CVE-2016-6802(SHIRO-682)

参考:https://issues.apache.org/jira/browse/SHIRO-682

影响版本: shiro org.apache.shiro shiro-spring 1.5.2

2、设置下context-path

在资源文件的application.properties文件中进行设置

server.servlet.context-path=/test

3、在多添加一个admin路径下的一个接口

TestController.java

@ResponseBody @RequestMapping(value="/admin/cmd", method = RequestMethod.GET) public String cmd(){ return "execute command endpoint!"; } @ResponseBody @RequestMapping(value="/admin", method = RequestMethod.GET) public String admin(){ return "secret key: admin888!"; }

ShiroConfig.java

@Bean public ShiroFilterFactoryBean filterFactoryBean(){ ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(manager()); factoryBean.setUnauthorizedUrl("/login"); factoryBean.setLoginUrl("/login"); Map map = new HashMap(); map.put("/login", "anon"); map.put("/admin", "authc"); map.put("/admin/*", "authc"); factoryBean.setFilterChainDefinitionMap(map); return factoryBean; }

漏洞复现

根据漏洞payload访问:http://127.0.0.1:8080/test;/admin/cmd,但是发现没有进行绕过成功,而是报错了

接着查了相关的文章,我这里继续把spring-boot的版本进行替换

org.springframework.boot spring-boot-starter-parent 2.2.6.RELEASE

再次访问http://127.0.0.1:8080/test;/admin/cmd,这里却发现了成功绕过

接着我在2.2.6的基础上,将context-path去掉,继续访问``http://127.0.0.1:8080/test;/admin/cmd`,这里发现又不行了

问题

1、为什么需要设置context-path,权限才能绕过?

2、为什么2.5.2的spring-boot版本不可以进行利用,却在2.2.6的版本上可以进行利用?

问题1 1、为什么需要设置context-path,权限才能绕过?

回想下对于shiro的1.5.2的漏洞代码修复,官方变动的地方为getRequestURI的方式,那么产生这个问题的肯定也是来自这里,所以这里仔细来分析这个地方,这里调试的环境为2.2.6

其实想下,它的修复方式就是通过如下来获取getRequestURI来进行获取请求路径,也就是如下代码,可以看到它是通过 ContextPath + ServletPath + getPathInfo 来进行拼接而成的

public static String getRequestUri(HttpServletRequest request) { String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE); if (uri == null) { uri = valueOrEmpty(request.getContextPath()) + "/" + valueOrEmpty(request.getServletPath()) + valueOrEmpty(request.getPathInfo()); } return normalize(decodeAndCleanUriString(request, uri)); }

当没有ContextPath设定的时候,我们访问http://127.0.0.1:8080/admin;/cmd,那么此时getRequestURI处理过后就是//admin/cmd,那么这个则会被shiro过滤器所拦截

上面的图中其实还没走decodeAndCleanUriString方法,这个方法走了之后//admin/cmd,也就会成为/admin/cmd

但是如果ContextPath有设定的时候,我们再来访问http://127.0.0.1:8080/test;/admin/cmd,注意的是我们这里设定的ContextPath为test,所以才这么访问,可以看到此时getRequestURI处理过后则是/test;//admin/cmd

那么继续走decodeAndCleanUriString方法,这里最后就拿到了一个/test

这里你就会发现获取ContextPath方法和getServletPath、getPathInfo方法不同的就是,它不会对;这个进行处理,导致decodeAndCleanUriString的时候将;后面的都去除掉,最后只剩下了一个/test

然后这里的/test,最后的结果就是在shiro过滤器中没有匹配到。

接着看spring中是如何运作的?

还是老样子,打断点在org/springframework/web/util/UrlPathHelper.java#getLookupPathForRequest方法中,可以看到这里分析的2.2.6

2.2.6默认走的就是getPathWithinServletMapping方法,这个方法返回的则是/admin/cmd

最后就是通过解析对应的controller的方法,获得对应的handler来进行反射调用

其实自己总结下:这个CVE的产生则是跟shiro和spring对ContextPath的处理方式不同产生的权限绕过,所以也就是这个漏洞为什么需要ContextPath的支持。

2、为什么2.5.2的SpringBoot版本不可以进行利用,却在2.2.6的版本上可以进行利用

继续走第二个问题,这里把SpringBoot的环境换成2.5.2进行调试

org.springframework.boot spring-boot-starter-parent 2.5.2

继续之前的过程,先看shiro过滤器这边的绕过,可以绕过,如下所示,获得是/test

接着看spring的路由走法,可以发现跟2.2.6的走法是不同的,主要受到了alwaysUseFullPath参数值的影响

alwaysUseFullPath为true和false的区别到底是怎么样的呢?

可以看到如果为true的话,下面的语句不会执行方法getPathWithinServletMapping了,而是直接返回一个getPathWithinApplication处理过后的结果,而单单只经过getPathWithinApplication方法处理的最后在调用对应方法的时候就调用不到

alwaysUseFullPath为true的时候结果为如下

alwaysUseFullPath为false的时候结果为如下

当Spring Boot版本在小于等于2.3.0.RELEASE的情况下,alwaysUseFullPath为默认值false,这会使得其获取ServletPath,所以在路由匹配时相当于会进行路径标准化包括对%2e解码以及处理跨目录,这可能导致身份验证绕过。而反过来由于高版本将alwaysUseFullPath自动配置成了true从而开启全路径,又可能导致一些安全问题。

官方修复方案

该漏洞是Shiro与Servlet对于ContextPath处理的差异,Shiro将ContextPath与其他路径拼接后代入了格式化方法进⾏处理,而该方法将分号后的所有部分都截断,这是漏洞的核心。

https://github.com/apache/shiro/compare/shiro-root-1.5.2...shiro-root-1.5.3

重新跟下,它是如何修复的?

访问http://127.0.0.1:8080/test;/admin/cmd

如下可以看到,这里的话在shiro处理路径的时候,getPathWithinApplication方法出来之后就是/admin/cmd,这里并不是将;后面的内容都去掉了

可以看下getPathWithinApplication比起上个版本是如何修复的?

不对Servletpath进行处理了,这里处理的只有getServletPath(request) + getPathInfo(request)

最后匹配了规则之后,走的就是shiro过滤器的地方

CVE-2020-13933

影响版本: shiro



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3