飘云阁安全论坛

 找回密码
 快速注册

QQ登录

只需一步,快速开始

查看: 734|回复: 1

[漏洞相关] 【转贴】CVE-2020-13933: Apache Shiro 权限绕过漏洞分析

[复制链接]
  • TA的每日心情
    开心
    2019-3-15 11:00
  • 签到天数: 262 天

    [LV.8]以坛为家I

    发表于 2020-9-23 22:23:39 | 显示全部楼层 |阅读模式
    本帖最后由 梦幻的彼岸 于 2020-9-23 22:24 编辑

    原文地址:
    https://cert.360.cn/report/detai ... 2dd642f2b436ed49cc7


    漏洞简述
    • 2020年08月18日, 360CERT监测发现 Apache Shiro 发布了 Apahce Shiro 权限绕过 的风险通告,该漏洞编号为 CVE-2020-13933,漏洞等级:高危,漏洞评分:8.0。
    • Apahce Shiro 由于处理身份验证请求时出错 存在 权限绕过漏洞,远程攻击者可以发送特制的HTTP请求,绕过身份验证过程并获得对应用程序的未授权访问。
    • 对此,360CERT建议广大用户及时将 Apache Shiro 升级到最新版本。与此同时,请做好资产自查以及预防工作,以免遭受黑客攻击。

    风险等级
    360CERT对该漏洞的评定结果如下
    评定方式 等级
    威胁等级 高危
    影响面 广泛
    360CERT评分 8.0分

    影响版本
    • Apache Shiro < 1.6.0

    漏洞详情
    • 相关概念
      shiro的架构图为:
      1.png
      其中最主要的是 Subject,SecurityManager,Realms。
      [AppleScript] 纯文本查看 复制代码
      Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;
      
      SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;
      
      Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
    • 漏洞原理
      利用idea搭建springboot+shiro进行测试,shiro版本为1.5.3。
      [AppleScript] 纯文本查看 复制代码
       
      <dependency>
                  <groupId>org.apache.shiro</groupId>
                  <artifactId>shiro-core</artifactId>
                  <version>1.5.3</version>
              </dependency>
      
              <dependency>
                  <groupId>org.apache.shiro</groupId>
                  <artifactId>shiro-web</artifactId>
                  <version>1.5.3</version>
              </dependency>
      
              <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.5.3</version>
      </dependency>

      构建测试代码:
      [AppleScript] 纯文本查看 复制代码
      @Configuration
      public class ShiroConfig {
          @Bean
          public MyRealm myRealm()
          {
              return new MyRealm();
      }
      
          @Bean
          public SecurityManager securityManager() {
              DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
              manager.setRealm(myRealm());
              return manager;
      }
      
          //filter工厂.设置对应的过滤条件和跳转条件
          @Bean
          public ShiroFilterFactoryBean shiroFilter() {
              ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
              // 设置 SecurityManager
              bean.setSecurityManager(securityManager());
              bean.setLoginUrl("/login");
              Map<String, String> filterMap = new LinkedHashMap<>();
              // anon:匿名用户可访问
              filterMap.put("/login","anon");
              // authc:认证用户可访问
              filterMap.put("/read/*", "authc");
              bean.setFilterChainDefinitionMap(filterMap);
              return bean;
          }
      }

      具体权限对应的定义在DefaultFilter类里:
      2.png
      访问/read/xx,被302重定向到了/login:
      3.png
      而/read/%3bxxx,能够绕过认证:
      4.png
      ant风格的路径仅出现一个*时才能成功,而**无法绕过,具体原因后面会说。

      先来看看shiro的拦截器基础类。
      5.png
      部分拦截器的作用:
      [AppleScript] 纯文本查看 复制代码
      PathMatchingFilter: 提供了基于Ant风格的请求路径匹配功能及拦截器参数解析的功能。
      
      OncePerRequestFilter: 过滤器基类,该基类保证每个请求在任何servlet容器上仅执行一次,另外提供enabled属性,表示是否开启该拦截器实例,默认enabled=true表示开启,如果不想让某个拦截器工作,可以设置为false即可。
      
      AbstractShiroFilter: 是Shiro的入口,根据URL配置的filter,选择并执行相应的filter chain。
    • shiro层
      shiro 过滤器链调用
      接着从源码来分析一下这个漏洞,首先是过滤器的调用,经过ApplicationFilterChain,请求被分派到OncePerRequestFilter过滤器进行拦截,首先调用getAlreadyFilteredAttributeName给我们的自定义过滤器打上标记,并通过判断当前请求中是否有该标记来判断该拦截器是否已经调用过,接着判断是否标记该拦截器不进行工作,
      6.png
      如果都没有,那么调用doFilterInternal方法,这里调用的是其子类AbstractShiroFilter#doFilterInternal方法,解析request和response。接着根据请求利用SecurityManager创建WebSubject接口类型的实例,这里采用的是建造者模式。
      7.png
      返回WebDelegatingSubject,然后调用Subject的execute方法,这里是一个回调,
      8.png
      接着在回调里调用executeChain,然后调用getExecutionChain,这里getFilterChainResolver返回PathMatchingFilterChainResolver,是在初始化ShiroFilterFactoryBean里设置的,具体流程不进行分析,该类用于解析出此次请求需要执行的Filter链,在PathMatchingFilterChainResolver内部由FilterChainManager维护着拦截器链,比如DefaultFilterChainManager实现维护着url模式与拦截器链的关系。
      9.png
      看下getChain,首先获取FilterChainManager,也就是DefaultFilterChainManager,DefaultFilterChainManager默认添加DefaultFilter中声明的拦截器。
      10.png
      11.png
    • shiro url 处理
      接着调用getPathWithinApplication方法。
      12.png
      跟到WebUtils#getPathWithinApplication方法, 这里Shiro对url的处理也是造成CVE-2020-11989的一个点。左边是修复之前的1.5.2版本,右边是当前版本1.5.3,采用getRequestUri,而getRequestUri里就进行了url的解码。
      13.png
      修复之后,采用了标准的url解析,不再对%2f解码。
      14.png
      getServletPath方法将%3b进行了解码,返回结果如下。
      15.png
      接着调用removeSemicolon,该方法查找;,并将;及其之后的部分删除,最后再调用normalize,用于操作.和/所带来的影响。本次测试返回/read/。
      16.png
      接着如果请求的不是/,就去除末尾的/,也就是/read。
      17.png
      接着获取filterChains所对应的filter,将处理后的url进行匹配。
      18.png
      19.png
    • shiro url 匹配
      在org.apache.shiro.util.AntPathMatcher#doMatch。 这里把我们定义的ant风格的path和请求的path都以/进行拆分存入数组,pattDirs和pathDirs。
      21.png
      pattern最后一位是*,于是返回false。
      22.png
      如果是**,会返回true,返回true的话,就会根据config里设置url所对应 filter过滤条件,最后返回ProxiedFilterChain:
      23.png
      如果是*,返回false,那么会return null。
      24.png
      resolved也就是null,最后就返回ApplicationFilterChain,在ApplicationFilterChain里没有任何权限校验。
      25.png
    • spring 层
      原则是Shiro会对Servlet容器的FilterChain进行代理,也就是正常情况下应该返回的ProxiedFilterChain,即先走Shiro自己的Filter体系,然后才会委托给Servlet容器的FilterChain进行Servlet容器级别的Filter链执行。但是我们利用*和%3b的配合绕过了,返回的依然是默认的ApplicationFilterChain,后续调用chain.doFilter。
    • spring url 处理
      spring利用DispatcherServlet来分派请求,他的一个主要作用就是通过HandlerMapping,将请求映射到处理器。在处理过程中会调用getHandler方法。
      26.png
      跟进getHandlerInternal方法。
      27.png
      这里会调用 UrlPathHelper#getLookupPathForRequest 方法获取请求的相对路径。
      28.png
      继续调用getPathWithinApplication方法获取地址。
      29.png
      然后调用getRequestUri,这里调用decodeAndCleanUriString方法很明显的对url进行解码,也就是/read/;xxxx,于是最终spring处理返回的url就是/read/;xxxx。
      30.png
      调用完getLookupPathForRequest之后,就是获取请求路径的映射了。
      31.png
    • spring 获取路径映射
      跟入lookupHandlerMethod,在里面会根据请求的url在RequestMappingHandlerMapping对象中去匹配自定义Controller里的方法,而这些映射关系都是在初始化RequestMappingHandlerMapping对象的过程中,根据相关注解获取到的。
      32.png
      最后调用getMatchingPatterns,在spring的AntPathMatcher#doMatch里,将请求和配置里的url进行匹配,如果匹配成功,返回true,匹配之后,从HandlerMapping中取出该路径所映射的方法,然后通过反射去执行方法。
      33.png

    版本修复
    • 在shiro 1.6.0版本中,针对/*这种ant风格的配置出现的问题,shiro在org.apache.shiro.spring.web.ShiroFilterFactoryBean.java中默认增加了/**的路径配置,以防止出现匹配不成功的情况。
      34.png
    • 而默认的/**配置对应了一个新增的类org.apache.shiro.web.filter.InvalidRequestFilter进行过滤,匹配到非法字符就会直接报错。
      35.png

    总结
    该漏洞产生的原因主要是shiro层在处理url上和spring上存在差异,主要是在处理;上的问题,通过构造含有;符号的url即可绕过shiro在权限上的处理,而spring不负责权限管控,所以最终会导致权限绕过。
    时间线
    • 2020-08-17 Apache Shiro发布通告
    • 2020-08-18 360-CERT 发布预警
    • 2020-09-22 360-CERT 发布分析


    参考链接
  • TA的每日心情
    开心
    2020-10-7 22:06
  • 签到天数: 6 天

    [LV.2]偶尔看看I

    发表于 2020-9-27 22:15:49 | 显示全部楼层
    这么好的贴子,咱没人回复?
    感谢楼主
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 快速注册

    本版积分规则

    快速回复 返回顶部 返回列表