shiro历史漏洞分析

声明

出品|先知社区(ID:alter99)

以下内容,来自先知社区的alter99作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。

CVE-2020-17510

漏洞信息

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

漏洞分析

这个漏洞还是对AntPathMatcher的继续绕过,在CVE-2020-11989和CVE-2020-13933分别尝试了/的双重URL编码和 ; 的URL 编码绕过,归根到底这种方式还是因为Shiro与Spring对URI处理的差异化导致的。那么字符 . 是不是也可以进行绕过呢?其实是可以的(测试环境Shiro 1.6.0,SpringBoot 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,绕过身份验证

图片

对于Spring来说,正如之前讲的,Spring Boot 版本在小于等于 2.3.0.RELEASE时,会对uri进行解码然后路径标准化,这样得到的路径为/hello,没有页面与之匹配。所以只有当 Spring Boot 版本在大于 2.3.0.RELEASE时标准化路径后/hello/%2e,然后解码/hello/.

图片

图片

下面的payload都可以使用:

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

漏洞修复

在Commit中发现org.apache.shiro.spring.web下新增了ShiroUrlPathHelper类,属于UrlPathHelper的子类,重写了getPathWithinApplication和getPa-thWithinServletMapping两个方法

图片

通过相关配置后,Spring就会使用Shiro的UrlPathHelper,这样两者判断逻辑一致,就不存在因差异性问题而导致的绕过了。

其实我认为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

图片

此时也成功绕过。

图片

所以这就存在一个矛盾:只有Spring Boot 版本在大于 2.3.0.RELEASE才能触发这个漏洞,修复之后由于版本问题,SpringBoot又不走那条语句。

另外在配置的时候,当Spring Boot版本在小于等于2.3.0.RELEASE,如2.1.5.RELEASE,时,this.get-UrlPathHelper()并不是ShiroUrlPathHelper,不清楚是不是配置问题还是版本兼容问题。

图片

CVE-2020-17523

漏洞信息

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

漏洞分析

如CVE-2020-17510那样,这个漏洞可以使用空格%20进行绕过。

我们输入路径为http://localhost:8080/hello/%20,进入getChain,经过路径获取后要进行权限的匹配与验证

图片

注意,获取的路径后面有空格,这里主要看一下/hello/和/hello/*比较时发生了什么经过:

pathMatches(pathPattern,requestURI)-> pathMatcher.matches(pattern,path)->match(pattern,source)-> doMatch(pattern, path, true) 来到了主要的判断方法doMatch()。

其中StringUtils.tokenizeToStringArray()方法是将它的参数,也就是传进来的两个路径拆解成字符串数组,然后进行比较。进入方法,可以看到当对空格进行转换时,直接trim为空。

图片

图片

这样就导致与shiro中的配置本意想违背,导致绕过。

图片

然后在Spring中的处理时,uri又包含空格,这样就能访问到/hello/%20页面

图片

漏洞修复

在Commit中,主要修复点AntPathMatcher.java,在tokenizeToStringArray方法中加了false和true两个参数

图片

可以看到,当第三个参数为false时,即trimTokens为false,此时就不会对token进行trim。

图片

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师傅

漏洞分析

根据threedr3am师傅博客提供的方向,看了一Shiro1.7.1前后PathMatchingFilterChainResolver-#getChain的对比

图片

图片

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

filterChainManager.proxy(originalChain, pathPattern);

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

filterChainManager.proxy(originalChain, requestURINoTrailingSlash);

但是正常访问,都会返回第一个proxy,什么时候才能绕过第一个比较并符合第二个比较呢?
可以看到,两者差别是对uri尾部斜线的处理,所以当在uri尾部加一个/,就会进入第二种比较方式。

图片

结合之前的多次调试再根据threedr3am师傅博客中的认证,可以知道shiro的认证鉴权会根据配置的先后顺序去依次实施,所以当我有如下配置时:

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

循环中先匹配到/admin/*(这里是通过while语句对去除尾部斜线的uri进行匹配)然后跳出循环进入到

filterChainManager.proxy(originalChain, requestURINoTrailingSlash);,

注意,这里真正的参数就是去除尾部斜线的uri,也就是/admin/page,所以在DefaultFilterChain-Manager#getChain中得到的权限是anon,这样就达到绕过目的。

图片

图片

漏洞修复

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

图片

CVE-2022-32532

漏洞信息

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

漏洞分析

这是最新的一个洞,看Shiro发布的公告显示,是由于RegexRequestMatcher的错误配置导致的问题。

简单了解了一下,RegexRequestMatcher和Ant-PathMatcher类似,都是Shiro用于路径匹配的配置,只是RegexRequestMatcher需要用户自己配置。

根据4ra1n师傅的分析,可以知道,正常正则表达式.并不包含\r和\n字符

图片

修改成如下代码就可修复问题

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

图片

那么回头看一下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时就会绕成验证,访问成功

图片

图片

图片

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

漏洞修复

在Commit可以看到,对compile方法设置了flag

图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值