跳转至

(CVE-2020-11989)Apache Shiro \< 1.5.3 身份认证绕过漏洞

一、漏洞简介

二、漏洞影响

Apache Shiro \< 1.5.3

Spring 框架中只使用 Shiro 鉴权

三、复现过程

(一)双重编码绕过

双重编码绕过分析

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

%25%32%66 -&gt; %2f

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

3.jpg

%2f -&gt; /

可以看到这里就造成了和Spring的uri处理不一致的问题,也就导致了接下来的问题。

当进入到org.apache.shiro.util.AntPathMatcher#doMatch 去匹配是否符合我们之前定义的权限路由/hello/*

4.jpg

doMatch 的代码有点杂这里就不放了,感兴趣的可以自己去跟一下逻辑。简单来说就是这里可以看到path成了 /hello/a/a ,hello后有两个 / ,所以跳过了这次的判断,导致了身份验证绕过。

总结一下,当进入应用后我们的请求页面被解析成 /hello/a%2fa ,所以它可以进入到springcontroller中的 /hello/{name}

但是因为shiro再次做了url解码,导致判断的uri成为了 /hello/a/a 它不属于我们配置的权限判断地址 /hello/*

因此造成了绕过,核心原理可以归因为是shiro与spring对RFC标准实现的差异导致(实际上就是shiro实现错了)。

双重编码绕过复现

编写如下代码

@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&lt;String, String&gt; map = new LinkedHashMap&lt;&gt;();
        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 那么你将会被禁止。

1.jpg

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

/ -&gt; %2f -&gt;%25%32%66

GET /hello/a%25%32%66a HTTP/1.1
Host: www.0-sec.org: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

2.jpg

现在它成功了

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

(二)分号绕过

分号绕过分析

由于Shiro的权限校验是通过判断 URL 匹配来做的,如果能找到 Shiro 获取的 URL 与 Web 框架处理 URL 不一致的情况就能造成权限绕过。Shiro 中对于 URL 的获取及匹配在

org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain

以访问 /;/test/admin/page 举例,通过 getPathWithinApplication 函数得到的路径为 /

1.png

跟入该函数的处理逻辑org.apache.shiro.web.util.WebUtils#getPathWithinApplication

2.png

可以看到 org.apache.shiro.web.util.WebUtils#getRequestUri 获取到的是 /

3.png

这里分别通过 getContextPath() getServletPath() getPathInfo() 获取并拼接得到 /;/test//admin/page ,传入后 decodeAndCleanUriString 变成了 / , org.apache.shiro.web.util.WebUtils#decodeAndCleanUriString

4.png

在 decodeAndCleanUriString ,会根据 Ascii 为 59 的字符也就是 ; 进行 URL 的截断,所以最终返回了 /

回到最开始的 /;/test/admin/page 请求,该 request 请求会进入 Spring 中, Spring 处理 URL 函数如下org.springframework.web.util.UrlPathHelper#getPathWithinServletMapping5.png

7.png

在 getPathWithinApplication 处理下是能正确获取到 context-path 与路由,最终经过 getPathWithinServletMapping 函数格式化处理后,得到最终路径为 /admin/page ,所以我们可以正常访问到该页面

6.png

因此总结来说就是当 URL 进入到 Tomcat 时, Tomcat 判断 /;test/admin/page 为 test 应用下的 /admin/page 路由,进入到 Shiro 时被 ; 截断被认作为 / ,再进入 Spring 时又被正确处理为 test 应用下的 /admin/page 路由,最后导致 Shiro 的权限绕过。

分号绕过复现

测试 Demo :

https://github.com/ianxtianxt/springboot-shiro

权限配置如下,其中 /admin 下的路由需要登录才能访问

@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean(){
   ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
   bean.setSecurityManager(securityManager());
   bean.setLoginUrl("/login");
   bean.setSuccessUrl("/index");
   bean.setUnauthorizedUrl("/unauthorizedurl");
   Map&lt;String, String&gt; map = new LinkedHashMap&lt;&gt;();
   map.put("/doLogin", "anon");
   map.put("/admin/*", "authc");
   bean.setFilterChainDefinitionMap(map);
   return  bean;
}
---
@GetMapping("/admin/page")
public String admin() {
   return "admin page";
}

maven 打包项目为 test.war ,部署于 Tomcat 。该漏洞成功利用存在下面两个条件

  • 1.应用不能部署在根目录,也就是需要 context-path , server.servlet.context-path=/test ,如果为根目录则 context-path 为空,就会被 CVE-2020-1957 的 patch 将 URL 格式化,值得注意的是若 Shiro 版本小于 1.5.2 的话那么该条件就不需要。

    b. Spring 控制器中没有另外的权限校验代码

如果直接访问 /test/admin/page,会返回302跳转要求登录8.png

但是访问/;/test/admin/page, 就能直接绕过 Shiro 权限验证,访问到 /admin 路由中的信息

9.png

参考链接

http://www.liuhaihua.cn/archives/694110.html

https://mp.weixin.qq.com/s/yb6Tb7zSTKKmBlcNVz0MBA