前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Shiro 权限绕过学习

Shiro 权限绕过学习

作者头像
亿人安全
发布2022-06-30 15:31:46
1.1K0
发布2022-06-30 15:31:46
举报
文章被收录于专栏:红蓝对抗

0x00 前言

在看 java web 审计的文章发现在其中有介绍关于 Spring 与 Shiro 之间权限绕过的问题,正好之前没有学习过,所以趁着机会学习一下

0x01 漏洞环境

这里可以在之前 Shiro 内存马注入环境的基础上进行一些简单修改

链接:https://github.com/KpLi0rn/ShiroVulnEnv

首先将 pom.xml 中对 shiro 的版本进行修改

代码语言:javascript
复制
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>1.5.2</version>
    </dependency>

    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.5.2</version>
    </dependency>

0x02 前置知识

下图红框处是 Shiro 的 URL 匹配规则

匹配规则

Shiro 中的匹配规则是通过 AntPathMatcher 来进行实现的

代码语言:javascript
复制
?  匹配一个字符
*  匹配一个或多个字符
** 匹配一个或多个目录

0x01 CVE-2020-11989

漏洞产生的原因是因为 Spring 与 Shiro 之间对 url 的处理不同从而导致权限绕过

利用条件

  • Apache Shiro <= 1.5.2
  • Spring 框架中只使用 Shiro 鉴权
  • 需要后端特定的格式才可进行触发
    • 即:Shiro权限配置必须为 /xxxx/* ,同时后端逻辑必须是 /xxx/{variable} 且 variable 的类型必须是 String

漏洞环境

在上文的环境基础下,在 Shiro 包下的 ShiroConfig 中,添加红框处代码

map.put("/admin/*","authc"); 即当我们访问 /admin/xxxx 的路径的时候 Shiro 会对其进行权限校验

ps:这里的规则是 /admin/* 所以 Shiro 并不会对多个目录进行权限校验,例如:/admin/aaa/bbb 这种是不会对其进行权限校验的

然后在 UserController 中添加如下代码

这里一定要 /xxxx/{} 的形式,且参数为 String

代码语言:javascript
复制
@ResponseBody
    @GetMapping("/admin/{name}")
    public String namePage(@PathVariable String name){  
        return "Hello" + name;
    }

漏洞演示

正常访问 /admin/demo 的时候,由于 Shiro 的权限校验,从而会跳转到 /login 处

当我们访问 /admin/Hello%252fBKpLi0rn 时候发现绕过了权限校验

从访问的路由可以很容易的看出来主要是因为 %252f ,也就是两次 URL 解码之后的 /

漏洞分析

通过打断点发现我们的请求会先经过 Shiro 然后再到 Spring中

Shiro层

在 GetMapping 处打断点,开始 debug

通过上面断点的位置,我们可以直接定位到 Shiro 处理请求的位置,WebUtils#getPathWithinApplication

在 getRequestUri 函数中会对我们的 uri 进行处理,跟进该函数

发现如果不为空的话就会将我们的 uri 传入 decodeAndCleanUriString 函数

ps:中间件收到我们的 get 请求会先进行一次url解码,所以这里 Shiro 收到的是 Hello%2fBLi0rn

来到 decodeAndCleanUriString 函数,在该函数中将 uri 传入了decodeRequestString 函数中进行 uri 解码,跟进该函数

在 decodeRequestString 函数中会对 uri 进行一次 URL 解码

将解码之后的 uri 进行赋值,然后会判断其中是否含有分号,如果有的话就截取分号前的内容进行返回

这么做应该主要是为了应对如下这种情况:

http://www.xxxx.com/xxxx;jession=xxxxxx

然后将处理好的 uri 传递给了 normalize 函数

在 normalize 函数中会对 uri 进行一些处理

'\\' => '/'

'/./ => '/'

/../ 前面的内容和后面进行拼接

处理完之后进行返回

将返回赋到 requestUri

继续跟下去知道来到 PathMatchingFilterChainResolver#getChain 中,在该函数中会获取我们在 ShiroConfig 中的规则,调用 pathMatches 函数来进行匹配,跟进 pathMatches 函数

在 pathMatches 函数中可发现匹配是通过 AntPathMatcher 来实现的 ,跟进 matches 方法

最终调用了 AntPathMatcher#doMatch 方法

在 doMatch 中实现匹配

这里我们的规则是 /admin/*

但是我们此时的 path 为 /admin/Hello/Bli0rn

由于没有匹配成功,所以返回 false

最后回到 getChain 函数,由于规则都遍历了没有发现匹配的,就返回 null,至此 Shiro 的权限就绕过了

由于 getChain 中返回的是 null,所以这里的 resolved 也是 null

由于 resolved 为 null,只会返回默认的 ApplicationFilterChain,在默认的 ApplicationFilterChain 中是没有任何权限校验

至此 Shiro 层面的权限就成功绕过了

题外话

如果是正常的拦截情况的话,会返回 ProxiedFilterChain,即先走 Shiro 自身的 Filter,然后再委托给 Servlet 容器的 FilterChain 进行 Servlet 容器级别的 Filter 链执行

Spring 层

文章链接:http://www.51gjie.com/javaweb/921.html,https://www.anquanke.com/post/id/218270#h3-7

熟悉 Spring 的师傅应该都知道在 Spring 中 DispatcherServlet 是负责请求派发的,即将将对应的请求转发到对应的 Controller 来处理,其一个主要的作用是通过 HandlerMapping 将请求映射到处理器。在处理过程中会调用 getHandler 方法来获取一个可以处理该请求的Handler

org.springframework.web.servlet.DispatcherServlet#doDispatch 大约484行左右 调用了 getHandler 方法

在本函数内会遍历所有已加载的 handlerMappings ,通过调用HandlerMapping的getHandler方法来进行判断是否这个Handler可以处理当前请求

发现在 getHandler 中调用了 getHandlerInternal 函数,跟进该函数

在 getHandlerInternal 中,调用了 getUrlPathHelper().getLookupPathForRequest(request) 该方法会根据请求解析出具体的用于匹配Handler的url,这是一个很关键的步骤,寻找合适的Handler就是根据url来进行的

在 getLookupPathForRequest 函数中调用了 getPathWithinServletMapping 然后赋给了 rest ,如果 rest 为 "" 那么就调用 getPathWithinApplication 来根据我们传入的 request 获取 应用内的路径

getPathWithinServletMapping:返回给定请求的Servlet映射中的路径,即请求 url 中超出调用 Servlet 的部分,在官方文档中给出了 demo

跟进 getPathWithinApplication 函数,发现会调用 getRequestUri 来获取我们的 requestUri ,跟进该函数

发现首先会从上下文的 javax.servlet.include.request_uri 属性中获取,如果为 null 则调用 request.getRequestURI() 获取到我们的 uri,然后通过 decodeAndCleanUriString 进行了一次 url 解码

/admin/Hello%252fBKpLi0rn 变为了 /admin/Hello%2fBKpLi0rn

重新回到 getHandlerInternal 函数,可以看到 lookupPath 和 request 传入了 lookupHandlerMethod 函数中

这里的 lookupPath 其实就是获取到了我们请求对应的 uri,接下来就可以根据lookupPath来匹配Controller的Handler了

Spring 获取路径映射

参考文章:https://www.jianshu.com/p/1136212b9197

matches 会存储所有匹配到的方法,如果matches为空就进入该判断,调用 addMatchingMappings 函数来添加匹配的Handler

继续跟进 getMatchingMapping 函数

进行了一系列的判断,跟进 getMatchingCondition

到 getMatchingCondition 函数,可以看到 lookupPath 传入了 getMatchingPatterns 函数,跟进该函数

getMatchingPatterns 函数中会将我们的请求和配置的 url 进行比较,匹配成功就添加到 matches 中

上面的这些功能都是为了在所有匹配的Handler之后需要挑选一个最合适的Handler进行请求的处理,获取到合适的 Handler 之后就进行Handler的访问来处理请求了(后面就不跟了 Orz

最终

漏洞修复

在 Shiro 1.5.3 版本中对 getPathWithinApplication 进行了修改,取消了 url 解码的函数,所以我们这里的 uri 并不会被完全解码

0x02 CVE-2020-1957

其实应该是这个写在最前面的,但是发现的时候 11989 已经已经写完了 Orz

利用条件

  • Apache Shiro <= 1.5.1
  • Spring 框架中只使用 Shiro 鉴权

漏洞环境

修改 pom.xml 中的版本为 2.1.5.RELEASE

ps:之前的 2.4.5 复现失败了,会报404,换成 2.1.5 就可以了

修改 shiro 版本为 1.5.1

在 UserController 中添加如下代码

代码语言:javascript
复制
   @ResponseBody
    @RequestMapping(value = "/admin/index", method = RequestMethod.GET)
    public String admin() {
        return "admin secret bypass and unauthorized access";
    }

    @ResponseBody
    @RequestMapping(value = "/demo", method = RequestMethod.GET)
    public String demo() {
        return "demo";
    }

在 ShiroConfig 中变为如下规则

代码语言:javascript
复制
map.put("/doLogin", "anon");
map.put("/demo/**","anon");
map.put("/unauth", "user");
map.put("/admin/**","authc");
map.put("/**", "authc");

漏洞演示

在本实验 demo 中访问 /demo 是不需要权限的,但是访问 /admin/index 时会被 Shiro 进行验权从而跳转到 /login

但是通过 /demo/..;/admin/index 就可以绕过 shiro 的权限来访问到 /admin/index

可以看到成功绕过 shiro 进行权限校验

漏洞分析

在 getPathWithinApp 中调用了 getRequestUri 中获取我们请求的 uri

在 getRequestUri 中会调用 decodeAndCleanUriString

在 decodeAndCleanUriString 处,会获取 uri 中分号的索引,如果 uri 中存在分号那么就会截取分号前的字符串

后面会来到 PathMatchingFilterChainResolver#getChain 中进行权限匹配,此时我们的 requestURI 为 demo/.. 由于我们 Shiro 的规则为 /demo/** anon ,因此校验通过

所以 Shiro 这部分的绕过其实就是因为截取了分号前面,也就是我们这里的 /demo/.. 然后和 /demo/** 匹配上了,由于我们的 /demo/** 是没有任何权限限制的,因此就绕过了

在实际情况中应该 login 多一些,例如 /login/..;/admin/index

这样 Shiro 部分的权限就绕过了

后面就是 Spring 的部分

和上文一样我们来到了 getHandler 函数处

跟进 getHandlerInternal 函数

在 getHandlerInternal 函数中会调用 getLookupPathForRequest 来根据我们的请求返回对应的 uri

在 getLookupPathForRequest 函数中调用了 getPathWithinServletMapping 来获取请求的相对路径,跟进该方法

getPathWithinServletMapping:返回给定请求的Servlet映射中的路径,即请求 url 中超出调用 Servlet 的部分,在官方文档中给出了 demo

在 getPathWithinApplication 函数中调用了 getServletPath,获取请求对应的 Servlet 的路径

其实该方法中就是具体的对请求的url的处理

在 getServletPath 函数中,首先会从上下文中进行获取,如果获取结果为 null 就会调用 request.getServletPath 即返回请求的URL中调用Servlet的部分

我们这里跟进一下 getServletPath 函数,继续跟进

可以看到返回了 this.mappingData.wrapperPath 也就是 /admin/index (即 Tomcat 中 servlet-path 匹配后的结果)

http://dengchengchao.com/?p=1065

最后返回给 springboot

漏洞修复

https://github.com/apache/shiro/commit/3708d7907016bf2fa12691dff6ff0def1249b8ce#diff-98f7bc5c0391389e56531f8b3754081aR139

将原先的 request.getRequestURI() 替换成了 getContextPath() 、getServletPath() 、getPathInfo() 的组合,这样就能获取我们想要的了,从而避免因为获取差异性而导致绕过,这样就与返回给 springboot 的路径保持一致了

0x03 CVE-2020-13933

利用条件

  • Apache Shiro < 1.6.0
  • Spring 框架中只使用 Shiro 鉴权
  • 需要后端特定的格式才可进行触发
    • 即:Shiro权限配置必须为 /xxxx/* ,同时后端逻辑必须是 /xxx/{variable} 且 variable 的类型必须是 String

漏洞环境

同 CVE-2020-11989 环境,只要将版本改为 1.5.3 即可,修改 pom.xml

漏洞演示

/admin/%3BKpLi0rn

漏洞分析

在 1.5.3 版本之后,Shiro 不会进行 url 的二次解码,但是在 removeSemicolon 中仍存在绕过的可能性

跟进该函数

removeSemicolon 函数中,会获取第一次出现分号的索引,然后截取分号前的 uri

这种情况的处理应该是为了应对 www.xxxx.com/admin;jession=asfoasdo 这种情况

所以我们只要利用 /admin/;whatever 这样的结构就可以绕过 Shiro 的权限校验

红框处会将 uri 最后的 / 进行去除,所以此时的 requestURI 为 /admin 自然是不符合我们这里的 shiro 拦截规则的 /admin/*

然后在 Spring 中则是会将后面部分当作是参数进行获取从而输出

漏洞修复

https://github.com/apache/shiro/commit/dc194fc977ab6cfbf3c1ecb085e2bac5db14af6d

增加了 InvalidRequestFilter 类来对一些特殊情况进行处理

遇到特殊字符会直接报错

同时增加了 /** 的规则,来防止一些匹配不到的情况

0x04 CVE-2020-17523

利用条件

  • Apache Shiro < 1.7.1
  • Spring 框架中只使用 Shiro 鉴权
  • 需要后端特定的格式才可进行触发
    • 即:Shiro权限配置必须为 /xxxx/* ,同时后端逻辑必须是 /xxx/{variable} 且 variable 的类型必须是 String

漏洞环境

同 CVE-2020-11989 环境,pom.xml 中版本修改为 1.7.0 或及以下即可

漏洞演示

漏洞分析

该绕过发生在 AntPathMatcher#doMatch 中,其中利用了 tokenizeToStringArray 函数分割传入的 uri 为数值,跟进该函数

在该函数中会调用 trim 将我们的空格给去掉,从而导致 /admin/ 与 /admin/* 不匹配,因此绕过 shiro 权限校验

漏洞修复

增加了选项,tokenizeToStringArray 函数中默认不会进行 trim

结合上图传入的微 false,因此默认情况下 token = token.trim() 并不会被执行

0x05 总结

整体学习下来其实根本问题就是 Shiro 与 Spring 对请求处理不同从而导致我们可以构造特殊的 uri 从而绕过 Shiro 的权限校验

0x06 参考链接

https://hpdoger.cn/2021/02/08/title:%20Shiro%E6%9D%83%E9%99%90%E7%BB%95%E8%BF%87%E6%B1%87%E6%80%BB/

https://www.anquanke.com/post/id/218270#h3-7

http://www.lmxspace.com/2020/08/24/Apache-shiro-%E6%9D%83%E9%99%90%E7%BB%95%E8%BF%87%E6%BC%8F%E6%B4%9E%E6%B1%87%E6%80%BB/#1%E3%80%81CVE-2020-1957

https://www.jianshu.com/p/1136212b9197

http://www.51gjie.com/javaweb/911.html

https://xz.aliyun.com/t/8281#toc-5

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-10-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 亿人安全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 前言
  • 0x01 漏洞环境
  • 0x02 前置知识
    • 匹配规则
    • 0x01 CVE-2020-11989
      • 利用条件
        • 漏洞环境
          • 漏洞演示
            • 漏洞分析
              • Shiro层
              • Spring 层
              • Spring 获取路径映射
            • 漏洞修复
            • 0x02 CVE-2020-1957
              • 利用条件
                • 漏洞环境
                  • 漏洞演示
                    • 漏洞分析
                      • 漏洞修复
                      • 0x03 CVE-2020-13933
                        • 利用条件
                          • 漏洞环境
                            • 漏洞演示
                              • 漏洞分析
                                • 漏洞修复
                                • 0x04 CVE-2020-17523
                                  • 利用条件
                                    • 漏洞环境
                                      • 漏洞演示
                                        • 漏洞分析
                                          • 漏洞修复
                                          • 0x05 总结
                                          • 0x06 参考链接
                                          相关产品与服务
                                          脆弱性检测服务
                                          脆弱性检测服务(Vulnerability detection Service,VDS)在理解客户实际需求的情况下,制定符合企业规模的漏洞扫描方案。通过漏洞扫描器对客户指定的计算机系统、网络组件、应用程序进行全面的漏洞检测服务,由腾讯云安全专家对扫描结果进行解读,为您提供专业的漏洞修复建议和指导服务,有效地降低企业资产安全风险。
                                          领券
                                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档