Apache Shiro 身份验证绕过漏洞 (CVE-2020-11989)
Apache Shiro是一个强大且易用的Java安全框架,它可以用来执行身份验证、授权、密码和会话管理。目前常见集成于各种应用中进行身份验证,授权等。
漏洞详情
腾讯安全玄武实验室研究员发现在Apache Shiro 1.5.3之前的版本,将Apache Shiro与Spring控制器一起使用时,特制请求可能会导致身份验证绕过。
漏洞发现者
该漏洞由腾讯安全玄武实验室的Ruilin发现并报告,此外来自边界无限的淚笑也独立向官方报告了此漏洞点。
风险等级
高风险
漏洞风险
身份验证绕过等
影响版本
Apache Shiro 1.5.3之前的版本
漏洞细节
漏洞位置主要出现在org.apache.shiro.web.util.WebUtils#getPathWithinApplication
中调用的getRequestUri
方法。
该漏洞可以用以下方法复现,首先编写如下配置代码。
1 | @Configuration |
这里配置了map.put("/hello/*", "authc");
同时可以去编写对应的controller
1 | @GetMapping("/hello/{name}") |
以上操作代表着我通过ant风格的语法设置了去检查在访问/hello
路由之后的一级目录的用户是否有权限。
如果请求/hello/aaa
那么将会被禁止。
但是这里我们可以通过url双编码的方式来绕过。
1 | / -> %2f ->%25%32%66 |
1 | GET /hello/a%25%32%66a HTTP/1.1 |
成功绕过身份验证。
当然这个场景下需要一些限制条件,首先权限ant风格的配置需要是*
而不是**
,同时controller需要接收的request参数(@PathVariable)的类型需要是String,否则将会出错。
我们可以来具体分析一下过程:
当请求进入应用后会进行第一次的url解码。
1 | %25%32%66 -> %2f |
接着当进入到shiro的org.apache.shiro.web.util.WebUtils#getPathWithinApplication
采用的是getRequestUri
方法同时里面会调用到decodeAndCleanUriString
-> decodeRequestString
进行再一次的解码。
也就造成了
1 | %2f -> / |
可以看到这里就造成了和Spring的uri处理不一致的问题,也就导致了接下来的问题。
当进入到org.apache.shiro.util.AntPathMatcher#doMatch
去匹配是否符合我们之前定义的权限路由/hello/*
这套流程判断后发现pattIdxStart > pattIdxEnd
,也可以通过观察当前变量来理解,如下
这里可以看到path成了/hello/a/a
有两个/
,所以根据上图所示代码跳过了这次的判断,导致了身份验证绕过。
总结一下,当进入应用后我们的请求页面被解析成/hello/a%2fa
,所以它可以进入到spring controller中的/hello/{name}
,但是因为shiro再次做了url解码,导致判断的uri成为了/hello/a/a
它不属于我们配置的权限判断地址/hello/*
。
此绕过核心原理可以归因为shiro与spring对RFC标准实现的差异。
此外,getRequestUri
在标准化中使用;
截断造成的利用场景可以参考分析文章。
官方修复
采用了标准的getServletPath(request) + getPathInfo(request)
同时不再进行url解码。
漏洞时间线
- 2020-5-27 腾讯安全玄武实验室研究员向Apache Shiro官方报告此漏洞位置
- 2020-6-16 Apache Shiro官方开始处理此漏洞
- 2020-6-22 Apache Shiro官方发布漏洞公告与致谢信息
安全建议
修复方案
升级至Apache Shiro 1.5.3 或更高版本。
漏洞防护方案
- 通过WAF检测请求的uri中是否包含
%25%32%66
关键词 - 通过WAF检测请求的uri开头是否为
/;
关键词
注意:增加WAF拦截请首先判断该类关键词是否不影响业务正常运行。