Shiro权限绕过汇总
2021-2-8 00:0:0 Author: hpdoger.cn(查看原文) 阅读量:3 收藏

整体来说就是shiro与spring对uri处理差异导致的漏洞

影响范围

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

环境

下面三个CVE复现都是基于此源码:https://github.com/l3yx/springboot-shiro

配置tomcat+war即可。注意如果maven resolve失败的话,证明本地之前已经download过相应的依赖,需要手动到~/.m2/respository下删除
-w1252

丢出来filter的逻辑,以下操作通过ant风格的语法设置了去检查在访问/admin路由之后的一级目录的用户是否有权限。
-w730

路由控制器逻辑(Spring业务层)
-w730

漏洞复现

关于Shiro自定义Reaml处理流程看笔记“shiro安全框架基础”

这里需要了解一下配置shiroConfig-Filter里的URL是ant格式,路径是支持通配符表示的

?:匹配一个字符
*:匹配零个或多个字符串
**:匹配路径中的零个或多个路径

-w441

所以未认证的用户访问/admin/xxx的资源会被重定向到login
-w1373

漏洞分析

访问/admin/xxx%252fxxx/会进入/admin/{name}逻辑,其中对%252f是对/二次url编码
-w1264

当然这个场景下需要一些限制条件,首先权限ant风格的配置需要是*而不是**,同时controller需要接收的request参数(@PathVariable)的类型需要是String,否则将会出错。

GET请求的数据包如下

GET /srpingboot_shiro_war/admin/gss%252fe HTTP/1.1
Host: 192.168.31.101:8081
Upgrade-Insecure-Requests: 1

首先进入shiro的逻辑,WebUtils#getPathWithinApplication处理request请求
-w956

步入getRequestUri函数,getServletPath()经过一次urldecode之后赋值给uri,接着uri会步入函数decodeAndCleanUriString
-w1066

继续跟进decodeRequestString,此时传入的实参uri为/srpingboot_shiro_war//admin/gss%2fe
-w839

其中decodeRequestString也是对uri进行urldecode解码操作,decode后的结果如下
-w408

至此,我们在get请求传递的2次编码url已经被完全decode。下一步就是Shiro的filter对请求资源的权限校验,主要的步骤跟进AntPathMatcher.class#doMatch

-w1040

path的值是二次解码后的/admin/gss/epattern为Shiro配置中ant格式的匹配字符串。

doMatch函数的大致逻辑是以/分割pattern字符串与path字符串,存为数组pattDirspathDirs。而后循环遍历patDirs中的值,通过matchStrings函数与pathDirs数组中的值进行匹配,匹配失败会直接返回false(也就是证明没有权限问题)
-w1016

如果匹配到了则会每次使pattIdxStart++,例如这里的pattern数组分为[admin,*],则会依次匹配到admingss,那么pattIdxStart为2

pattIdxEnd是pattern数组的末尾索引,这里为1
-w670

而在之后的判断中,赋值后的pattIdxStart>pattIdxEnd返回false绕过了判断
-w723

也就是说,/*这种匹配只能命中/admin/gss这种格式,无法命中/admin/gss/xxx

绕过Shiro的权限判断后。进入Spring处理路由的流程如下,解码出来的路由为/admin/gss%2fe,当然可以进入@GetMapping("/admin/{name}")控制器中的逻辑
-w1440

漏洞修复

在1.5.3版本,采用标准的 getServletPath 和 getPathInfo 进行uri处理,同时取消了url解码,这样pattIdxStartpattIdxEnd就是相同的数值

public static String getPathWithinApplication(HttpServletRequest request) {
    return normalize(removeSemicolon(getServletPath(request) + getPathInfo(request)));
}

-w1044

影响范围

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

复现与分析

请求数据包如下

GET /srpingboot_shiro_war/admin/%3beee HTTP/1.1
Host: 192.168.31.101:8081
Upgrade-Insecure-Requests: 1

其实就是对11989的绕过,思路都是一样的。1.5.3修复之后不会二次解码url但是新增的removeSemicolon函数在分割”;”的时候又产生了问题
-w1127

步入removeSemicolon发现它就是返回”;”之前的内容,实际上是为了处理GET请求中”/admin/;jessid=xxxx”这样的需求
-w854

由于此时path值为/admin,当然不属于请求/admin/*下的资源,因此不会被鉴权
-w1207

之后进入Spring处理路由时仍匹配到/admin/;xxxx,进入业务逻辑
-w566

漏洞修复

-w877

也可以是把它看作CVE-2020-13933的绕过,1.7.0以前的版本AntPathMatcher.class#doMatch在处理空格时又又又又又把/admin/%20trim后作为/admin,而当作一层目录。导致/admin//admin/*不匹配

影响范围

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

复现与分析

GET /srpingboot_shiro_war/admin/%20 HTTP/1.1
Host: 192.168.31.101:8081
Upgrade-Insecure-Requests: 1

仍然跟到doMatchtokenizeToStringArray函数将path以反斜线分割为数组,这里我传入的path值为/admin
-w1147

跟进tokenizeToStringArray函数看具体实现,对于”/“分割之后的token进行了trim()操作
-w1068

tokenizeToStringArray的返回值会将path字符串分割为数组['admin'],可见此时的空格已被去除,path又被作为一层路径。

接下来的for循环在之前提到过。由于path只包含一个元素,因此我们的pathIdxStartpathIdxEnd初值均为0;经过一次循环后,pathIdxStart自增为1
-w1133

因此进入判断去匹配是否有**,很明显没有则返回false绕过权限
-w1116

原理上来说trim()会清空字符串前后所有的whitespace,空格只是其中的一种,但是在测试中发现除了空格以外的其他whitespace,例如%08、%09、%0a,spring+tomcat 处理时都会返回400

漏洞修复

-w1406

增加了选项,tokenizeToStringArray函数默认不进行trim的操作

这三个洞的精髓就在于:通过二次编码或者”;”绕过Shiro filter对/admin/*的ant。让shiro判断时认为/admin/;sds是一层目录,或/admin/x%252fe认为是三层目录从而绕过权鉴。而Spring在处理这样的uri时只是把它当作了二层目录,会被/admin/{xxx}匹配到从而分发到业务逻辑部分的代码中

当然这几种绕过有一个最大的局限性,就是Spring控制器的代码层在写路由注解时,使用通配符或者泛解模式,比如这里的/admin/{xxx}
-w589

如果路由写死为/admin/hpdoger则无法进入Spring相应代码逻辑


文章来源: 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/
如有侵权请联系:admin#unsafe.sh