声明
出品|先知社区(ID:alter99)
以下内容,来自先知社区的alter99作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。
CVE-2020-1957
漏洞信息
漏洞编号:
CVE-2020-1957/CNVD-2020-20984/SHIRO-682
影响版本:shiro < 1.5.2
漏洞描述:利用Shiro和Spring对URL的处理的差异化,越权并成功访问。
漏洞补丁:Commit Commit Commit
参考:Shiro权限绕过漏洞详细分析Ruil1n师傅
漏洞分析
SHIRO-682
本漏洞起源于 SHIRO-682。在Spring中,/resource/xx与/resource/xx/都会被截成/resource/xx以访问相应资源;在shiro中,/resource/xx与/resource/xx/被视为两个不同路径。所以在Spring集成shiro时,只需要在访问路径后添加/就存在绕过权限校验的可能。
下面通过复现进行分析(分析、测试版本1.4.2):首先shiro.ini中[urls]配置如下:
[urls]
# anon:匿名拦截器,不需登录就能访问,一般用于静态资源,或者移动端接口。
# authc:登录拦截器,需要登录认证才能访问的资源。
/login.jsp = authc
/logout = logout
/toJsonPOJO = authc, perms["audit:list"]
/** = anon
输入/toJsonPOJO时,shiro对其进行判断,从shior.ini或其他配置中进行匹配。当匹配到/toJsonPOJO时,匹配成功,跳出循环。
此时,跳转至登陆界面。
输入/toJsonPOJO/时,shiro对其进行判断,当匹配到/toJsonPOJO时,匹配失败,继续匹配;当匹配到/**时,匹配成功,跳出循环。
接着到了springframework中的判断,这里/toJson-POJO/和/toJsonPOJO是可以匹配成功的
此时,成功绕过
其他绕过方式
除了上面的绕过方式,本CVE还存在另一个绕过。利用的是shiro和spring对url中的 ; 处理的差异进行绕过并成功访问。分析、测试版本1.4.2
绕过分析
首先进入Shiro中,首先在
org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver.class#getChain处下断点,进行调试,访问http://localhost:8080/xx/..;/toJsonPOJO
单步调试进入this.getPathWithinApplicat-ion(request),在WebUtils#getPathWithinApplicat-ion()中,通过getContextPath(request),获取到上下文信息后,再用getRequestUri(request)获取具体的uri。进入getRequestUri()方法,在return前,获取到的uri为/xx/..;/toJsonPOJO
接下来分析一下return normalize(decodeAndClean-UriString(request, uri));
首先进入decodeAndCleanUriString
传入的参数uri是/xx/..;/toJsonPOJO,然后通过语句int semicolonIndex= uri.indexOf(59);找出uri中分号的位置,59也就是;的ASCII码
如果uri中有分号,就返回分号前的字段,否则返回整个uri。
接着进入normalize,参数uri已经变成/xx/..,normalize内部对传入的路径进行标准化规范处理,相关操作包括替换反斜线、替换//为/等,最后得到返回的uri
此时return normalize(decodeAndCleanUriString-(request, uri));结果为/xx/..,也就是说getRequestUri(request)获取的uri为/xx/..
一路回到getChain,经过上面的步骤,得到requestURI值为/xx/..,接下来在while循环里使用pathMatches(pathPattern, requestURI)进行权限校验,此时只有/**能够与/xx/..匹配成功,/**是anon权限,不需要登陆就能访问,绕过了/toJsonPOJO的authc权限
此时Shiro部分的权限绕过了,那么Spring部分的路径是怎么匹配的呢?
url经过shiro的处理认证通过后,就会进入spring中进行解析,我们在UrlPathHelper#getLookupPath-ForRequest下断点
先进入getPathWithinApplication(),通过this.get-RequestUri(request)获取uri
获取到的uri值为/xx/..;/toJsonPOJO,在return之前进入decodeAndCleanUriString(request, uri)
传进来的参数uri为/xx/..;/toJsonPOJO,经过removeSemicolonContent(uri)后移除uri中/与/之间的的分号以及分号后面的内容;
经过decodeRequestString(request, uri)后对uri进行解码;经过getSanitized-Path(uri)后将路径中//替换为/。此时返回的uri值为/xx/../toJsonPOJO
步入getPathWithinServletMapping()后,传入的参数pathWithinApp值为/xx/../toJsonPOJO。
依次通过UrlPathHelper#getServletPath
HttpServletRequestWrapper#getServletPath
Request#getServletPath获取到我们实际访问的url:http://localhost:8080/toJsonPOJO后返回,最终实现绕过权限访问
经过测试当uri为
123;/..;345/;../.;/alter/..;/;/;///////;/;/;awdwadwa/toJsonPOJO时,Shiro对/123进行权限验证;
Spring的org.springframework.web.util.UrlPathHelper中,getPathWithinApplication(request)值为
/123/.././alter/../toJsonPOJO;
this.getPathWithinServletMapping(request, pathWithinApp)值为/toJsonPOJO,可以进行绕过
上面这个payload只能在较低版本的Spring Boot上使用。
根据Ruil1n 师傅介绍:当Spring Boot版本在小于等于2.3.0.RELEASE的情况下,alwaysUseFullPath为默认值false,这会使得其获取ServletPath,所以在路由匹配时相当于会进行路径标准化包括对%2e解码以及处理跨目录,这可能导致身份验证绕过。
而反过来由于高版本将alwaysUseFullPath自动配置成了true从而开启全路径,又可能导致一些安全问题。
所以在高版本上只能试着寻找逻辑上有没有漏洞,然后进行绕过。比如程序配置了访问路径/alter/** 为 anon,但是指定了其中的一个/alter/page为 authc。这时在不跳目录的情况下,可以使用如下请求绕过:http://127.0.0.1:8080/alter//;aaaa/;...///////;/;/;awdwadwa/page
漏洞修复
先是在Commit的PathMatchingFilter#pathsMatch和PathMatchingFilterCha-inResolver#getChain方法中添加了对访问路径后缀为/的支持
然后在Commit,除了endsWith还添加了equals的判断。是修复由于上一次提交,导致访问路径为/时抛出的异常。
在Commit中,shiro使用request.getContextPath()、request.getServletPath-()、request.getPathInfo()拼接构造uri替代request.getRequestURI()来修复; 绕过