shiro 权限绕过漏洞 CVE-2020-11989

0x00 漏洞复现

pom.xml

        
            org.apache.shiro
            shiro-core
            1.5.2
        
        
            org.apache.shiro
            shiro-spring
            1.5.2
        

application.properties

server.context-path=/test

ShiroConfig

@Bean
    ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager());
        bean.setLoginUrl("/login");
        bean.setSuccessUrl("/index");
        bean.setUnauthorizedUrl("/unauthorizedurl");
        Map map = new LinkedHashMap();
        map.put("/hello/*", "authc");
        //map.put("/hello/**", "authc"); //in version 1.7.0 will not trigger CVE-2020-17523
        bean.setFilterChainDefinitionMap(map);
        return bean;
    }

spring

    @RequestMapping("/hello/{name}")
    public String hello2(@PathVariable String name) {
        return "auth hello/{_" + name + "_}, there ";
    }

触发权限绕过的访问请求如下:
http://127.0.0.1:8080/test/hello/a%252fa
response如下:auth hello/{a%2fa}, there
正常的访问请求如下:
http://127.0.0.1:8080/test/hello/aa
response如下:跳转到登录页面。

"/"的URL编码为"%2f",在浏览器中"%"二次编码为"%252f"。
触发此漏洞的根源在于shiro在进行filter匹配的过程中,对url进行了两次解码;而在spring的框架中,并未进行两次URL解码。因此两者造成了不一致。

0x01 源码分析

PathMatchingFilterChainResolver.java文件中的getchain函数中如下调用,获取requestURI,导致requestURI被解析为"/hello/a/a",而无法与pattern"/hello/*"匹配上。

String requestURI = getPathWithinApplication(request);

在WebUtils.java中getPathWithinApplication函数

public static String getPathWithinApplication(HttpServletRequest request) {
        String contextPath = getContextPath(request);
        String requestUri = getRequestUri(request);
        if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) {
            // Normal case: URI contains context path.
            String path = requestUri.substring(contextPath.length());
            return (StringUtils.hasText(path) ? path : "/");
        } else {
            // Special case: rather unusual.
            return requestUri;
        }
    }

getRequestUri函数

    public static String getRequestUri(HttpServletRequest request) {
        String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
        if (uri == null) {
            uri = valueOrEmpty(request.getContextPath()) + "/" +
                  valueOrEmpty(request.getServletPath()) +
                  valueOrEmpty(request.getPathInfo());
        }
        return normalize(decodeAndCleanUriString(request, uri));
    }

decodeAndCleanUriString函数

    private static String decodeAndCleanUriString(HttpServletRequest request, String uri) {
        uri = decodeRequestString(request, uri);
        int semicolonIndex = uri.indexOf(';');
        return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri);
    }

decodeRequestString函数

    public static String decodeRequestString(HttpServletRequest request, String source) {
        String enc = determineEncoding(request);
        try {
            return URLDecoder.decode(source, enc);
        } catch (UnsupportedEncodingException ex) {
            if (log.isWarnEnabled()) {
                log.warn("Could not decode request string [" + Encode.forHtml(source) + "] with encoding '" + Encode.forHtml(enc) +
                        "': falling back to platform default encoding; exception message: " + ex.getMessage());
            }
            return URLDecoder.decode(source);
        }
    }

因为采用ant风格的匹配,所以,如果pattern配置为"/hello/**"则不会触发此漏洞。

patch分析

diff
https://github.com/apache/shiro/compare/shiro-root-1.5.2...shiro-root-1.5.3
getPathWithinApplication函数进行了升级,不再调用会进行二次URL解码的getRequestUri函数。

    public static String getPathWithinApplication(HttpServletRequest request) {
        return normalize(removeSemicolon(getServletPath(request) + getPathInfo(request)));
    }
    private static String getServletPath(HttpServletRequest request) {
        String servletPath = (String) request.getAttribute(INCLUDE_SERVLET_PATH_ATTRIBUTE);
        return servletPath != null ? servletPath : valueOrEmpty(request.getServletPath());
    }

    private static String getPathInfo(HttpServletRequest request) {
        String pathInfo = (String) request.getAttribute(INCLUDE_PATH_INFO_ATTRIBUTE);
        return pathInfo != null ? pathInfo : valueOrEmpty(request.getPathInfo());
    }

    private static String valueOrEmpty(String input) {
        if (input == null) {
            return "";
        }
        return input;
    }

在shiro 1.5.3版本中,没有地方再调用WebUtils.getRequestUri,该方法标识为已废弃。
那么废弃了getRequestUri方法是否会触发CVE-2020-1957呢?
答案是并不会,在getPathWithinApplication方法中并未直接获取uri,而是通过getServletPath和getPathInfo分别获取的。

References

https://xlab.tencent.com/cn/2020/06/30/xlab-20-002/

你可能感兴趣的:(shiro 权限绕过漏洞 CVE-2020-11989)