shiro权限绕过方面漏洞分析

shiro权限绕过方面漏洞分析

今天来学习权限绕过方面的漏洞,都尽量复现一遍,然后分析分析,我们这里用vulhub靶场吧…

Apache Shiro -目录遍历漏洞 CVE-2010-3863

漏洞信息

漏洞编号:CVE-2010-3863 / CNVD-2010-2715
影响版本:shiro < 1.1.0JSecurity 0.9.x
漏洞描述:Shiro进行权限验证前未进行路径标准化,导致使用时可能绕过权限校验
漏洞补丁:Commit

漏洞分析

先分析一下Shiro身份验证的流程:Shiro使用org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain 方法获取和调用要执行的 Filter,逻辑如下:
getPathWithinApplication()方法中调用 WebUtils.getPathWithinApplication()方法,用来获取请求路径。
其中getContextPath(request)方法获取 Context 路径(这里这个context可以理解为,当前路径)

1.png

getRequestUri(request) 方法获取URI 的值,并调用 decodeAndCleanUriString() 处理。

2.png

decodeAndCleanUriString()中对 ; 进行了截取。
此时contextPath值为/samples_web_warrequestUri值为/samples_web_war/login.jsp
然后判断requestUri是否以contextPath开始,是的话将其替换为/
其实这里面可以吧requestUri理解为路由

处理之后的请求 URL 将会使用 AntPathMatcher#doMatch 进行权限验证。
此时发现,Shiro中对URI并没有进行路径的标准化处理,这样当URI中存在特殊字符时,就存在绕过风险

漏洞复现

[urls]
/login.jsp = authc
/logout = logout
/account/** = authc
/remoting.jsp = authc, perms["audit:list"]
/** = anon

image-20240209011817441

Apache Shiro ‘login.jsp’安全绕过漏洞 CVE-2014-0074

漏洞信息

漏洞编号: CVE-2014-0074 / CNVD-2014-03861 / SHIRO-460
影响版本:shiro 1.x < 1.2.3
漏洞描述 :当程序使用LDAP服务器并启用非身份验证绑定时,远程攻击者可借助空的用户名或密码利用该漏洞绕过身份验证。
漏洞补丁:Commit

漏洞分析

当使用了未经身份验证绑定的 LDAP 服务器时,允许远程攻击者通过空用户名或空密码绕过身份验证。

Shiro < 1.3.2 验证绕过漏洞 CVE-2016-6802

漏洞信息

漏洞编号:CVE-2016-6802 / CNVD-2016-07814
影响版本:shiro < 1.3.2
漏洞描述:Shiro未对ContextPath做路径标准化导致权限绕过
漏洞补丁: Commit
参考 : su18师傅

漏洞详解

本漏洞类似 CVE-2010-3863,依旧是路径标准化导致的问题,不过之前是在 RequestURI 上,本漏洞是在 ContextPath 上。
之前提到,Shiro 调用 WebUtils.getPathWithinApplication() 方法获取请求路径。逻辑如下:

public String getPathWithinApplication(HttpServletRequest request) {
    String contextPath = this.getContextPath(request);
    String requestUri = this.getRequestUri(request);
    String path = this.getRemainingPath(requestUri, contextPath, true);
    if (path != null) {
        return StringUtils.hasText(path) ? path : "/";
    } else {
        return requestUri;
    }
}

其中调用 getContextPath() 方法,获取 contextPath ;调用 getRequestUri() 方法,获取 uri

getContextPath() 方法调用 decodeRequestString 进行 URLDecode
6802_2.png
由于获取的 ContextPath 没有标准化处理,如果出现一些特殊字符使ContextPath与实际不符,都会导致在 StringUtils.startsWithIgnoreCase() 方法判断时失效,直接返回完整的RequestURI

复现

登录账户lonestarr,该账户对页面remoting.jsp没有访问权限,在跟路径前加任意路径,再加../即可实现绕过
6802_3.png

6802_4.png

这么一看哈,其实,感觉,也没那么难?
我要成为厉害的人!

Shiro < 1.5.2 验证绕过漏洞 CVE-2020-1957

漏洞信息

漏洞编号:CVE-2020-1957 / CNVD-2020-20984 /SHIRO-682
影响版本:shiro < 1.5.2
漏洞描述:利用 ShiroSpringURL 的处理的差异化,越权并成功访问。
漏洞补丁:Commit Commit [Commit](

漏洞分析

SHIRO-682

本漏洞起源于 SHIRO-682。在 Spring 中,/resource/xx/resource/xx/ 都会被截成/resource/xx以访问相应资源;在 shiro 中,/resource/xx/resource/xx/被视为两个不同路径。所以在 Spring 集成 shiro 时,只需要在访问路径后添加 / 就存在绕过权限校验的可能。
下面通过复现进行分析(分析、测试版本1.4.2):
首先shiro.ini中[urls]配置如下:

[urls]
# anon:匿名拦截器,不需登录就能访问,一般用于静态资源,或者移动端接口。
# authc:登录拦截器,需要登录认证才能访问的资源。
/login.jsp = authc
/logout = logout
/toJsonPOJO = authc, perms["audit:list"]
/** = anon

输入/toJsonPOJO时,shiro对其进行判断,从shiro.ini或其他配置中进行匹配。当匹配到/toJsonPOJO时,匹配成功,跳出循环。
1957_1.png
此时,跳转至登陆界面。
输入/toJsonPOJO/时,shiro对其进行判断,当匹配到/toJsonPOJO时,匹配失败,继续匹配;当匹配到/**时,匹配成功,跳出循环。
1957_2.png
接着到了springframework中的判断,这里/toJsonPOJO/toJsonPOJO是可以匹配成功的
1957_3.png
此时,成功绕过
1957_4.png

其他绕过方式

除了上面的绕过方式,本 CVE 还存在另一个绕过。利用的是 shirospringurl 中的 ; 处理的差异进行绕过并成功访问。
分析、测试版本1.4.2

绕过分析

首先进入Shiro
首先在org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver.class#getChain处下断点,进行调试,访问http://localhost:8080/xx/..;/toJsonPOJO
1957_6.png

单步调试进入this.getPathWithinApplication(request),在WebUtils#getPathWithinApplication()中,通过getContextPath(request),获取到上下文信息后,再用getRequestUri(request)获取具体的uri。进入getRequestUri()方法,在return前,获取到的uri/xx/..;/toJsonPOJO
1957_5.png
接下来分析一下return normalize(decodeAndCleanUriString(request, uri));
首先进入decodeAndCleanUriString
传入的参数uri/xx/..;/toJsonPOJO,然后通过语句int semicolonIndex = uri.indexOf(59);找出uri中分号的位置,59也就是;ASCII码
如果uri中有分号,就返回分号前的字段,否则返回整个uri
1957_7.png
接着进入normalize,参数uri已经变成/xx/..normalize内部对传入的路径进行标准化规范处理,相关操作包括替换反斜线、替换///等,最后得到返回的uri
此时return normalize(decodeAndCleanUriString(request, uri));结果为/xx/..,也就是说getRequestUri(request)获取的uri/xx/..
1957_8.png

1957_9.png

一路回到getChain,经过上面的步骤,得到requestURI值为/xx/..,接下来在while循环里使用pathMatches(pathPattern, requestURI)进行权限校验,此时只有/**能够与/xx/..匹配成功,/**anon权限,不需要登陆就能访问,绕过了/toJsonPOJOauthc权限
1957_10.png

此时Shiro部分的权限绕过了,那么Spring部分的路径是怎么匹配的呢?
url经过shiro的处理认证通过后,就会进入spring中进行解析,我们在UrlPathHelper#getLookupPathForRequest下断点
1957_11.png
先进入getPathWithinApplication(),通过this.getRequestUri(request)获取uri
1957_12.png
获取到的uri值为/xx/..;/toJsonPOJO,在return之前进入decodeAndCleanUriString(request, uri)
1957_14.png
传进来的参数uri/xx/..;/toJsonPOJO,经过removeSemicolonContent(uri)后移除uri//之间的的分号以及分号后面的内容;经过decodeRequestString(request, uri)后对uri进行解码;经过getSanitizedPath(uri)后将路径中//替换为/。此时返回的uri值为/xx/../toJsonPOJO
1957_15.png

1957_16.png
步入getPathWithinServletMapping()后,传入的参数pathWithinApp值为/xx/../toJsonPOJO。依次通过UrlPathHelper#getServletPathHttpServletRequestWrapper#getServletPathRequest#getServletPath获取到我们实际访问的url:http://localhost:8080/toJsonPOJO后返回,最终实现绕过权限访问
1957_13.png

1957_17.png

经过测试当uri123;/..;345/;../.;/alter/..;/;/;///////;/;/;awdwadwa/toJsonPOJO时,Shiro/123进行权限验证;
1957_18.png
Springorg.springframework.web.util.UrlPathHelper中,getPathWithinApplication(request)值为/123/.././alter/../toJsonPOJO;
1957_19.png

1957_20.png
this.getPathWithinServletMapping(request, pathWithinApp)值为/toJsonPOJO,可以进行绕过
1957_21.png

上面这个 payload 只能在较低版本的 Spring Boot 上使用。
根据Ruil1n 师傅介绍:
Spring Boot 版本在小于等于 2.3.0.RELEASE 的情况下,alwaysUseFullPath 为默认值 false,这会使得其获取 ServletPath ,所以在路由匹配时相当于会进行路径标准化包括对 %2e 解码以及处理跨目录,这可能导致身份验证绕过。而反过来由于高版本将 alwaysUseFullPath 自动配置成了 true 从而开启全路径,又可能导致一些安全问题。
所以在高版本上只能试着寻找逻辑上有没有漏洞,然后进行绕过。比如程序配置了访问路径 /alter/**anon,但是指定了其中的一个 /alter/pageauthc。这时在不跳目录的情况下,可以使用如下请求绕过:
http://127.0.0.1:8080/alter//;aaaa/;...///////;/;/;awdwadwa/page

我这里末尾加了/才成功。不过也无所谓了

Apache Shiro < 1.5.3 权限绕过漏洞 CVE-2020-11989

漏洞信息

漏洞编号:CVE-2020-11989 / SHIRO-782
影响版本:shiro < 1.5.3
漏洞描述:在Shiro < 1.5.3的情况下,将ShiroSpring Controller一起使用时,相应请求可能会导致身份验证绕过。
漏洞补丁:Commit
参考: 腾讯安全玄武实验室 Ruilin师傅 边界无限 淚笑师傅

这个漏洞有两种绕过方式,分别由腾讯安全玄武实验室的Ruilin师傅和来自边界无限的淚笑师傅报告

shiro文件方面科普

Shiro主要的三个文件:
ShiroConfig,LoginController,Myrealm

权限配置:ShiroConfig
其中/doLogin无需权限验证即可访问,用于登录界面
而test/文件下目录需要登陆权限认证后才可以进行访问

image-20240212143229567

登陆控制:LoginController
其中设置了访问目录返回的内容

image-20240212143248394

Myrealm:其中储存着用户类信息像我们登陆所用的密码。
image-20240212143303608

漏洞分析 —— 两次解码绕过

限制

这个场景下需要一些限制条件,首先配置文件的ant风格需要是*而不是**,测试发现,?也可以
另外controller需要接收的request参数(@PathVariable)的类型需要是String,否则将会出错。
11989_3.png

复现

首先复现一下,测试版本 1.5.2

编写Controller

    @RequestMapping("/toJsonList/{name}")
    @ResponseBody
    public List<User> toJsonList(@PathVariable String name){
        System.out.println("返回json集合数据");
        User user1 = new User();
        user1.setName("alter1");
        user1.setAge(15);

        User user2 = new User();
        user2.setName("alter2");
        user2.setAge(12);

        List<User> userList = new ArrayList<User>();
        userList.add(user1);
        userList.add(user2);

        return userList;
    }

配置对应的shiro.ini

[urls]
/toJsonList/* = authc

此时请求/toJsonList/aaa那么将会被禁止。
11989_1.png
但是这里我们可以通过url双编码的方式来绕过。

/ -> %2f ->%25%32%66

11989_2.png
测试发现下面四种组合只有前两组可以绕过

yes
/toJsonList/a%25%32%66a
/toJsonList/%25%32%66

no
/toJsonList/%25%32%66a
/toJsonList/a%25%32%66

分析

首先要清楚Shiro支持 Ant 风格的路径表达式配置。ANT 通配符有 3 种,如下所示:

通配符 说明
? 匹配任何单字符
* 匹配0或者任意数量的字符
** 匹配0或者更多的目录

解释一下就是/** 之类的配置,匹配路径下的全部访问请求,包括子目录及后面的请求,如:/admin/** 可以匹配 /admin/a 或者 /admin/b/c/d 等请求。
对于/*的话 ,单个 * 不能跨目录,只能在两个 / 之间匹配任意数量的字符,如 /admin/* 可以匹配 /admin/a 但是不能匹配 /admin/b/c/d
那么问题来了,如果我们将其配置为/toJsonList/*,但是我们访问形如/toJsonList/a/b这种路径,此时就会绕过访问权限。

我们还记得为了修复CVE-2020-1957,shiro在1.5.2版本进行了更新,将request.getRequestURI() 修改为 request.getContextPath()request.getServletPath()request.getPathInfo() 拼接构造uri。根据网上师傅们的总结,这几个方法的差异性如下:

  • request.getRequestURL():返回全路径;
  • request.getRequestURI():返回除去Host部分的路径;
  • request.getContextPath():返回工程名部分,如果工程映射为/,则返回为空;
  • request.getServletPath():返回除去Host和工程名部分的路径;
  • request.getPathInfo():仅返回传递到Servlet的路径,如果没有传递额外的路径信息,则此返回Null

第一次解码发生在request.getServletPath()
11989_4.png
第二次解码发生在decodeAndCleanUriString() -> decodeAndCleanUriString() -> decodeRequestString() -> URLDecoder.decode()
11989_5.png
因此org.apache.shiro.web.util.WebUtils#getRequestUri进行了两次解码,将/toJsonList/a%25%32%66a解码成/toJsonList/a/a
接着就走到org.apache.shiro.util.AntPathMatcher#doMatch进行权限验证,/toJsonList/a/a不满足配置中的toJsonList/*,因此成功绕过。

但还要看Spring是怎么对其进行解析的
org.springframework.web.uti.UrlPathHelper#getPathWithinApplication中,将url解析为/toJsonList/a%2fa,这样其实就表示/toJsonList/{name}中的name值为a%2fa
11989_6.png

分析完之后, 也就解释了为什么下面四种组合只有前两组可以绕过
(这种二次解码的方式我测试只适用于1.5.2的版本,之前的版本使用a%25%32%66a测试,因为只有一次解码,会跳转至登陆界面;a%2fa测试直接返回400 Bad Request

漏洞分析 —— 根路径差异化解析绕过

限制

  1. Shiro >= 1.5.2 的话,应用不能部署在根目录,如果为根目录则 context-path 为空, CVE-2020-1957 更新补丁将 URL 格式化。
  2. Spring 控制器中没有另外的权限校验代码

复现

本次复现使用的是1.4.2版本的shiro所以应用根目录是什么都没有关系

配置为

/alter/* = authc
/** = anon

新增一个controller

@RequestMapping("/alter/test")
@ResponseBody
public List<User> test(){
    User user1 = new User();
    user1.setName("alter");
    user1.setAge(15);
    List<User> userList = new ArrayList<User>();
    userList.add(user1);
    return userList;
}

输入地址http://localhost:8080/;/shirodemo/alter/testorg.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain会进行如下操作获取uri
11989_7.png
此时uri结果为/,绕过配置/alter/* = authc,符合配置/** = anon,达到绕过目的。
Spring在处理uri时直接进行路径标准化,去掉了分号

11989_13.png

11989_8.png
Shiro < 1.5.2版本的话,根路径是什么没有关系
11989_9.png

漏洞修复

ShiroCommit 中修改了 URL 获取的逻辑,不单独处理 context-path,这样不会导致绕过,同时也避免了二次 URL 解码的问题。
回退了 WebUtils#getRequestUri 的代码,并将其标记为 @Deprecated
11989_10.png
可以看到,shiro建议使用 getPathWithinApplication() 方法获取路径减去上下文路径,或直接调用 HttpServletRequest.getRequestURI() 方法获取。

WebUtils#getPathWithinApplication 方法,修改了使用 RequestUri 去除 ContextPath 的方式,改为使用 getServletPath(request) + getPathInfo(request))。然后使用 removeSemicolon 方法处理分号问题,normalize 方法进行路径标准化。
11989_11.png

11989_12.png

Shiro < 1.6.0 验证绕过漏洞 CVE-2020-13933

漏洞信息

漏洞编号:CVE-2020-13933 / CNVD-2020-46579
影响版本:shiro < 1.6.0
漏洞描述:Shiro 由于处理身份验证请求时存在权限绕过漏洞,特制的HTTP请求可以绕过身份验证过程并获得对应用程序的未授权访问。
漏洞补丁:Commit

漏洞分析

这个CVE其实就是对CVE-2020-11989 patch的绕过。上一个CVE使用 getServletPath(request) + getPathInfo(request)) 获取uri,回顾一下:

  • request.getServletPath():返回除去Host和工程名部分的路径;
  • request.getPathInfo():仅返回传递到Servlet的路径,如果没有传递额外的路径信息,则此返回Null

ShirogetChain内进行权限验证,首先通过getPathWithinApplication(request)获得uri。从下图可以看到,更新后使用HttpServletRequest.getRequestURI() 方法获取uri;然后使用removeSemicolon去除uri中的分号,这里去除的是分号及分号后面的内容;然后使用normalize进行路径标准化。
13933_1.png
此时得到的路径为/hello,绕过了配置中的权限。
接着看Spring是怎么处理路径的:
org.springframework.web.util#UrlPathHelper中的getPathWithinApplication方法内,使用getRequestUri(request)方法获取uri
13933_2.png
Shiro处理的差异达到既绕过Shiro权限验证又成功访问的目的。
13933_3.png

漏洞修复

shiro1.6.0版本中,org.apache.shiro.spring.web#ShiroFilterFactoryBean中增加了/**的默认路径配置,使其可以全局匹配进行过滤校验
13933_4.png
默认的/**配置对应一个全局的 filterInvalidRequestFilter,这个类继承了 AccessControlFilter。用来过滤特殊字符(分号、反斜线、非ASCII码字符),并返回 400 状态码。
13933_5.png

Apache Shiro < 1.7.0 权限绕过漏洞 CVE-2020-17510

漏洞信息

漏洞编号:CVE-2020-17510 / CNVD-2020-60318
影响版本:shiro < 1.7.0
漏洞描述:第三种AntPathMatcher的绕过方式
漏洞补丁:Commit

漏洞分析

这个漏洞还是对 AntPathMatcher 的继续绕过,在CVE-2020-11989CVE-2020-13933分别尝试了 / 的双重 URL 编码和 ;URL 编码绕过,归根到底这种方式还是因为ShiroSpringURI处理的差异化导致的。那么字符 . 是不是也可以进行绕过呢?其实是可以的(测试环境Shiro 1.6.0SpringBoot 2.5.3
还是添加如下配置和Controller

map.put("/hello/*", "authc");
@GetMapping("/hello/{name}")
public String hello(@PathVariable String name) {
    return "hello";
}

Shiro获得的uri/hello时,是无法和/hello/*匹配的,所以就在/hello后面加上%2e,这样Shiro解码之后变成/hello/.,然后路径标准化成为/hello,绕过身份验证
17510_1.png
对于Spring来说,正如之前讲的,Spring Boot 版本在小于等于 2.3.0.RELEASE时,会对uri进行解码然后路径标准化,这样得到的路径为/hello,没有页面与之匹配。所以只有当 Spring Boot 版本在大于 2.3.0.RELEASE时标准化路径后/hello/%2e,然后解码/hello/.
17510_2.png
17510_3.png
下面的payload都可以使用:

/%2e
/%2e/
/%2e%2e
/%2e%2e/

漏洞修复

Commit中发现org.apache.shiro.spring.web下新增了ShiroUrlPathHelper类,属于UrlPathHelper的子类,重写了getPathWithinApplicationgetPathWithinServletMapping两个方法
17510_4.png

其实我认为1.7.1才算真正的更新,因为它是依次对原uri和去除uri尾部斜线的uri进行验证,这样就可以避免因直接去除尾部uri导致/hello/hello/*不匹配而导致的绕过问题。

补丁问题

问题一

根据官方发布的公告,发现其实需要配置shiro-spring-boot-web-starter才有效

 if you are NOT using Shiro’s Spring Boot Starter
(`shiro-spring-boot-web-starter`), you must configure add the
ShiroRequestMappingConfig auto configuration[1] to your application or
configure the equivalent manually[2].
[1] https://shiro.apache.org/spring-framework.html#SpringFramework-WebConfig
[2]https://github.com/apache/shiro/blob/shiro-root-1.7.0/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroRequestMappingConfig.java#L28-L30

由于我导入的dependency如下

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

如果直接将版本升为1.7.0的话,其实并没有触发更新,原payload还是可以绕过。
只有按照上面官网所述的两种配置方式修改后,才能防御成功

问题二

在旧版的SpringBoot 中,当我们需要获取当前请求地址的时候,直接通过如下方式获取:

//org.springframework.web.servlet.handler#getHandlerInternal
String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);

但是在新版Spring里边,通过如下方式获取

String lookupPath = this.initLookupPath(request);

initLookupPath()代码如下:

protected String initLookupPath(HttpServletRequest request) {
    if (this.usesPathPatterns()) {
        request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE);
        RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);
        String lookupPath = requestPath.pathWithinApplication().value();
        return UrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath);
    } else {
        return this.getUrlPathHelper().resolveAndCacheLookupPath(request);
    }
}

如果this.usesPathPatterns() == true的话,就可以绕开问题一中我们配置的ShiroUrlPathHelper
17510_5.png
此时也成功绕过。
17510_6.png

所以这就存在一个矛盾:只有Spring Boot 版本在大于 2.3.0.RELEASE才能触发这个漏洞,修复之后由于版本问题,SpringBoot又不走那条语句。
另外在配置的时候,当Spring Boot 版本在小于等于 2.3.0.RELEASE,如2.1.5.RELEASE,时,this.getUrlPathHelper()并不是ShiroUrlPathHelper,不清楚是不是配置问题还是版本兼容问题。
17510_7.png

Apache Shiro < 1.7.1 权限绕过漏洞CVE-2020-17523

漏洞信息

漏洞编号:CVE-2020-17523 / CNVD-2021-09492
影响版本:shiro < 1.7.1
漏洞描述:Shiro 1.7.1 之前的版本,在将 ShiroSpring 结合使用时,特制的 HTTP 请求可能会导致身份验证绕过。
漏洞补丁:Commit

漏洞分析

CVE-2020-17510那样,这个漏洞可以使用空格%20进行绕过。
我们输入路径为http://localhost:8080/hello/%20,进入getChain,经过路径获取后要进行权限的匹配与验证
17523_1.png
这里主要看一下/hello//hello/*比较时发生了什么
经过pathMatches(pathPattern, requestURI) -> pathMatcher.matches(pattern, path) -> match(pattern, source) -> doMatch(pattern, path, true) 来到了主要的判断方法doMatch()
其中StringUtils.tokenizeToStringArray()方法是将它的参数,也就是传进来的两个路径拆解成字符串数组,然后进行比较。
进入方法,可以看到当对空格进行转换时,直接trim为空
17523_2.png

17523_3.png
这样就导致与shiro中的配置本意想违背,导致绕过。
17523_4.png
然后在Spring中的处理时,uri又包含空格,这样就能访问到/hello/%20页面
17523_5.png

漏洞修复

Commit中,主要修复点AntPathMatcher.java,在tokenizeToStringArray方法中加了falsetrue两个参数
17523_6.png
可以看到,当第三个参数为false时,即trimTokensfalse,此时就不会对token进行trim
17523_7.png

Apache shiro 认证机制不恰当 CVE-2021-41303

漏洞信息

漏洞编号:CVE-2021-41303 / SHIRO-825
影响版本:shiro < 1.8.0
漏洞描述:1.8.0 之前的 Apache Shiro,在 Spring Boot 中使用 Apache Shiro 时,特制的 HTTP 请求可能会导致身份验证绕过。用户应该更新到 Apache Shiro 1.8.0
漏洞补丁:Commit
参考:[threedr3am师傅](https://threedr3am.github.io/2021/09/22/从源码diff分析Apache-Shiro 1.7.1版本的auth bypass(CVE-2021-41303)/)

漏洞分析

根据[threedr3am师傅](https://threedr3am.github.io/2021/09/22/从源码diff分析Apache-Shiro 1.7.1版本的auth bypass(CVE-2021-41303)/)博客提供的方向,看了一下Shiro 1.7.1前后PathMatchingFilterChainResolver#getChain的对比
41303_1.png

41303_2.png
发现在1.7.1版本中,先是对pathPattern和requestURI进行比较,比较成功,返回:

filterChainManager.proxy(originalChain, pathPattern);

否则对删除尾部斜线的pathPattern和requestURI进行比较,比较成功,跳出循环,返回:

filterChainManager.proxy(originalChain, requestURINoTrailingSlash);

但是正常访问,都会返回第一个proxy,什么时候才能绕过第一个比较并符合第二个比较呢?
可以看到,两者差别是对uri尾部斜线的处理,所以当在uri尾部加一个/,就会进入第二种比较方式。
41303_3.png
结合之前的多次调试再根据threedr3am师傅博客中的认证,可以知道shiro的认证鉴权会根据配置的先后顺序去依次实施
所以当我有如下配置时:

map.put("/admin/*", "authc");
map.put("/admin/page", "anon");

循环中先匹配到/admin/*(这里是通过while语句对去除尾部斜线的uri进行匹配),然后跳出循环,进入到filterChainManager.proxy(originalChain, requestURINoTrailingSlash);,注意,这里真正的参数就是去除尾部斜线的uri,也就是/admin/page,所以在DefaultFilterChainManager#getChain中得到的权限是anon,这样就达到绕过目的。
41303_4.png

41303_5.png

漏洞修复

直接将filterChainManager.proxy的第二个参数改为pathPattern,直接传配置中的uri
41303_6.png

Apache Shiro RegExPatternMatcher 权限绕过漏洞 CVE-2022-32532

漏洞信息

漏洞编号:CVE-2022-32532
影响版本:shiro < 1.9.1
漏洞描述:在1.9.1之前的Apache Shiro中,RegexRequestMatcher可能会被错误配置,从而在某些servlet容器上被绕过。应用程序使用RegExPatternMatcher.的正则表达式可能容易被授权绕过。
漏洞补丁:Commit
参考:4ra1n师傅

###漏洞分析

这是最新的一个洞,看Shiro发布的公告显示,是由于RegexRequestMatcher的错误配置导致的问题。
简单了解了一下,RegexRequestMatcherAntPathMatcher类似,都是Shiro用于路径匹配的配置,只是RegexRequestMatcher需要用户自己配置。
根据4ra1n师傅的分析,可以知道,正常正则表达式.并不包含\r\n字符
32532_1.png
修改成如下代码就可修复问题

// flag为Pattern.DOTALL时,表达式 .可以匹配任何字符,包括行结束符。
Pattern pattern = Pattern.compile(regex,Pattern.DOTALL);

32532_2.png
那么回头看一下RegexRequestMatcher用于匹配的代码

public boolean matches(String pattern, String source) {
    if (pattern == null) {
        throw new IllegalArgumentException("pattern argument cannot be null.");
    } else {
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(source);
        return m.matches();
    }
}

可以发现,当pattern存在带.的正则表达式并且source中存在\r\n字时,此时判断错误。
此时我们在配置完RegexRequestMatcher之后增加如下Controller

@RequestMapping(path = "/alter/{value}")
public String alter(@PathVariable String value) {
    System.out.println("绕过成功");
    return "绕过成功"+value;
}

增加如下配置

//myFilter.java中设置成需要权限
manager.addToChain("/alter/.*", "myFilter");

这样正常访问/alter/aaa是被拒绝的,但是当访问/alter/a%0aaa/alter/a%0daa时就会绕成验证,访问成功
32532_3.png

32532_4.png

32532_6.png
这个洞限制还是比较多的,既要服务器配置了RegExPatternMatcher,又要设置带有.的正则表达式

漏洞修复

Commit可以看到,对compile方法设置了flag
32532_5.png


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。后续可能会有评论区,不过也可以在github联系我。