shiro权限绕过方面漏洞分析
今天来学习权限绕过方面的漏洞,都尽量复现一遍,然后分析分析,我们这里用vulhub靶场吧…
Apache Shiro -目录遍历漏洞 CVE-2010-3863
漏洞信息
漏洞编号:CVE-2010-3863 / CNVD-2010-2715
影响版本:shiro < 1.1.0
和JSecurity 0.9.x
漏洞描述:Shiro
进行权限验证前未进行路径标准化,导致使用时可能绕过权限校验
漏洞补丁:Commit
漏洞分析
先分析一下Shiro
身份验证的流程:Shiro
使用org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain
方法获取和调用要执行的 Filter
,逻辑如下:
在getPathWithinApplication()
方法中调用 WebUtils.getPathWithinApplication()
方法,用来获取请求路径。
其中getContextPath(request)
方法获取 Context
路径(这里这个context可以理解为,当前路径)
getRequestUri(request)
方法获取URI
的值,并调用 decodeAndCleanUriString()
处理。
在decodeAndCleanUriString()
中对 ;
进行了截取。
此时contextPath
值为/samples_web_war
,requestUri
值为/samples_web_war/login.jsp
然后判断requestUri
是否以contextPath
开始,是的话将其替换为/
其实这里面可以吧requestUri
理解为路由
处理之后的请求 URL
将会使用 AntPathMatcher#doMatch
进行权限验证。
此时发现,Shiro
中对URI
并没有进行路径的标准化处理,这样当URI
中存在特殊字符时,就存在绕过风险
漏洞复现
[urls]
/login.jsp = authc
/logout = logout
/account/** = authc
/remoting.jsp = authc, perms["audit:list"]
/** = anon
Apache Shiro ‘login.jsp’安全绕过漏洞 CVE-2014-0074
漏洞信息
漏洞编号: CVE-2014-0074 / CNVD-2014-03861 / SHIRO-460
影响版本:shiro 1.x < 1.2.3
漏洞描述 :当程序使用LDAP
服务器并启用非身份验证绑定时,远程攻击者可借助空的用户名或密码利用该漏洞绕过身份验证。
漏洞补丁:Commit
漏洞分析
当使用了未经身份验证绑定的 LDAP
服务器时,允许远程攻击者通过空用户名或空密码绕过身份验证。
Shiro < 1.3.2 验证绕过漏洞 CVE-2016-6802
漏洞信息
漏洞编号:CVE-2016-6802 / CNVD-2016-07814
影响版本:shiro < 1.3.2
漏洞描述:Shiro
未对ContextPath
做路径标准化导致权限绕过
漏洞补丁: Commit
参考 : su18师傅
漏洞详解
本漏洞类似 CVE-2010-3863
,依旧是路径标准化导致的问题,不过之前是在 RequestURI
上,本漏洞是在 ContextPath
上。
之前提到,Shiro
调用 WebUtils.getPathWithinApplication()
方法获取请求路径。逻辑如下:
public String getPathWithinApplication(HttpServletRequest request) {
String contextPath = this.getContextPath(request);
String requestUri = this.getRequestUri(request);
String path = this.getRemainingPath(requestUri, contextPath, true);
if (path != null) {
return StringUtils.hasText(path) ? path : "/";
} else {
return requestUri;
}
}
其中调用 getContextPath()
方法,获取 contextPath
;调用 getRequestUri()
方法,获取 uri
;
在getContextPath()
方法调用 decodeRequestString
进行 URLDecode
。
由于获取的 ContextPath
没有标准化处理,如果出现一些特殊字符使ContextPath
与实际不符,都会导致在 StringUtils.startsWithIgnoreCase()
方法判断时失效,直接返回完整的RequestURI
。
复现
登录账户lonestarr
,该账户对页面remoting.jsp
没有访问权限,在跟路径前加任意路径,再加../
即可实现绕过
这么一看哈,其实,感觉,也没那么难?
我要成为厉害的人!
Shiro < 1.5.2 验证绕过漏洞 CVE-2020-1957
漏洞信息
漏洞编号:CVE-2020-1957 / CNVD-2020-20984 /SHIRO-682
影响版本:shiro < 1.5.2
漏洞描述:利用 Shiro
和 Spring
对 URL
的处理的差异化,越权并成功访问。
漏洞补丁:Commit Commit [Commit](
漏洞分析
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
对其进行判断,从shiro.ini
或其他配置中进行匹配。当匹配到/toJsonPOJO
时,匹配成功,跳出循环。
此时,跳转至登陆界面。
输入/toJsonPOJO/
时,shiro
对其进行判断,当匹配到/toJsonPOJO
时,匹配失败,继续匹配;当匹配到/**
时,匹配成功,跳出循环。
接着到了springframework
中的判断,这里/toJsonPOJO
和/toJsonPOJO
是可以匹配成功的
此时,成功绕过
其他绕过方式
除了上面的绕过方式,本 CVE
还存在另一个绕过。利用的是 shiro
和 spring
对 url
中的 ;
处理的差异进行绕过并成功访问。
分析、测试版本1.4.2
绕过分析
首先进入Shiro
中
首先在org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver.class#getChain
处下断点,进行调试,访问http://localhost:8080/xx/..;/toJsonPOJO
单步调试进入this.getPathWithinApplication(request)
,在WebUtils#getPathWithinApplication()
中,通过getContextPath(request)
,获取到上下文信息后,再用getRequestUri(request)
获取具体的uri
。进入getRequestUri()
方法,在return
前,获取到的uri
为/xx/..;/toJsonPOJO
接下来分析一下return normalize(decodeAndCleanUriString(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#getLookupPathForRequest
下断点
先进入getPathWithinApplication()
,通过this.getRequestUri(request)
获取uri
获取到的uri
值为/xx/..;/toJsonPOJO
,在return
之前进入decodeAndCleanUriString(request, uri)
传进来的参数uri
为/xx/..;/toJsonPOJO
,经过removeSemicolonContent(uri)
后移除uri
中/
与/
之间的的分号以及分号后面的内容;经过decodeRequestString(request, uri)
后对uri
进行解码;经过getSanitizedPath(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
我这里末尾加了/才成功。不过也无所谓了
Apache Shiro < 1.5.3 权限绕过漏洞 CVE-2020-11989
漏洞信息
漏洞编号:CVE-2020-11989 / SHIRO-782
影响版本:shiro < 1.5.3
漏洞描述:在Shiro < 1.5.3
的情况下,将Shiro
与Spring Controller
一起使用时,相应请求可能会导致身份验证绕过。
漏洞补丁:Commit
参考: 腾讯安全玄武实验室 Ruilin师傅 边界无限 淚笑师傅
这个漏洞有两种绕过方式,分别由腾讯安全玄武实验室的Ruilin
师傅和来自边界无限的淚笑师傅报告
shiro文件方面科普
Shiro主要的三个文件:
ShiroConfig,LoginController,Myrealm
权限配置:ShiroConfig
其中/doLogin无需权限验证即可访问,用于登录界面
而test/文件下目录需要登陆权限认证后才可以进行访问
登陆控制:LoginController
其中设置了访问目录返回的内容
Myrealm:其中储存着用户类信息像我们登陆所用的密码。
漏洞分析 —— 两次解码绕过
限制
这个场景下需要一些限制条件,首先配置文件的ant
风格需要是*
而不是**
,测试发现,?
也可以
另外controller
需要接收的request
参数(@PathVariable
)的类型需要是String
,否则将会出错。
复现
首先复现一下,测试版本 1.5.2
。
编写Controller
@RequestMapping("/toJsonList/{name}")
@ResponseBody
public List<User> toJsonList(@PathVariable String name){
System.out.println("返回json集合数据");
User user1 = new User();
user1.setName("alter1");
user1.setAge(15);
User user2 = new User();
user2.setName("alter2");
user2.setAge(12);
List<User> userList = new ArrayList<User>();
userList.add(user1);
userList.add(user2);
return userList;
}
配置对应的shiro.ini
[urls]
/toJsonList/* = authc
此时请求/toJsonList/aaa
那么将会被禁止。
但是这里我们可以通过url双编码的方式来绕过。
/ -> %2f ->%25%32%66
测试发现下面四种组合只有前两组可以绕过
yes
/toJsonList/a%25%32%66a
/toJsonList/%25%32%66
no
/toJsonList/%25%32%66a
/toJsonList/a%25%32%66
分析
首先要清楚Shiro
支持 Ant
风格的路径表达式配置。ANT
通配符有 3
种,如下所示:
通配符 | 说明 |
---|---|
? | 匹配任何单字符 |
* | 匹配0或者任意数量的字符 |
** | 匹配0或者更多的目录 |
解释一下就是/**
之类的配置,匹配路径下的全部访问请求,包括子目录及后面的请求,如:/admin/**
可以匹配 /admin/a
或者 /admin/b/c/d
等请求。
对于/*
的话 ,单个 *
不能跨目录,只能在两个 /
之间匹配任意数量的字符,如 /admin/*
可以匹配 /admin/a
但是不能匹配 /admin/b/c/d
。
那么问题来了,如果我们将其配置为/toJsonList/*
,但是我们访问形如/toJsonList/a/b
这种路径,此时就会绕过访问权限。
我们还记得为了修复CVE-2020-1957
,shiro在1.5.2
版本进行了更新,将request.getRequestURI()
修改为 request.getContextPath()
、request.getServletPath()
、request.getPathInfo()
拼接构造uri
。根据网上师傅们的总结,这几个方法的差异性如下:
request.getRequestURL()
:返回全路径;request.getRequestURI()
:返回除去Host
部分的路径;request.getContextPath()
:返回工程名部分,如果工程映射为/
,则返回为空;request.getServletPath()
:返回除去Host
和工程名部分的路径;request.getPathInfo()
:仅返回传递到Servlet
的路径,如果没有传递额外的路径信息,则此返回Null
;
第一次解码发生在request.getServletPath()
第二次解码发生在decodeAndCleanUriString()
-> decodeAndCleanUriString()
-> decodeRequestString()
-> URLDecoder.decode()
因此org.apache.shiro.web.util.WebUtils#getRequestUri
进行了两次解码,将/toJsonList/a%25%32%66a
解码成/toJsonList/a/a
接着就走到org.apache.shiro.util.AntPathMatcher#doMatch
进行权限验证,/toJsonList/a/a
不满足配置中的toJsonList/*
,因此成功绕过。
但还要看Spring
是怎么对其进行解析的
在org.springframework.web.uti.UrlPathHelper#getPathWithinApplication
中,将url
解析为/toJsonList/a%2fa
,这样其实就表示/toJsonList/{name}
中的name
值为a%2fa
。
分析完之后, 也就解释了为什么下面四种组合只有前两组可以绕过
(这种二次解码的方式我测试只适用于1.5.2
的版本,之前的版本使用a%25%32%66a
测试,因为只有一次解码,会跳转至登陆界面;a%2fa
测试直接返回400 Bad Request
)
漏洞分析 —— 根路径差异化解析绕过
限制
- 若
Shiro >= 1.5.2
的话,应用不能部署在根目录,如果为根目录则context-path
为空,CVE-2020-1957
更新补丁将URL
格式化。 Spring
控制器中没有另外的权限校验代码
复现
本次复现使用的是1.4.2
版本的shiro
所以应用根目录是什么都没有关系
配置为
/alter/* = authc
/** = anon
新增一个controller
@RequestMapping("/alter/test")
@ResponseBody
public List<User> test(){
User user1 = new User();
user1.setName("alter");
user1.setAge(15);
List<User> userList = new ArrayList<User>();
userList.add(user1);
return userList;
}
输入地址http://localhost:8080/;/shirodemo/alter/test
,org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain
会进行如下操作获取uri
此时uri
结果为/
,绕过配置/alter/* = authc
,符合配置/** = anon
,达到绕过目的。Spring
在处理uri
时直接进行路径标准化,去掉了分号
Shiro < 1.5.2
版本的话,根路径是什么没有关系
漏洞修复
Shiro
在 Commit 中修改了 URL
获取的逻辑,不单独处理 context-path
,这样不会导致绕过,同时也避免了二次 URL 解码的问题。
回退了 WebUtils#getRequestUri
的代码,并将其标记为 @Deprecated
可以看到,shiro
建议使用 getPathWithinApplication()
方法获取路径减去上下文路径,或直接调用 HttpServletRequest.getRequestURI()
方法获取。
在 WebUtils#getPathWithinApplication
方法,修改了使用 RequestUri
去除 ContextPath
的方式,改为使用 getServletPath(request) + getPathInfo(request))
。然后使用 removeSemicolon
方法处理分号问题,normalize
方法进行路径标准化。
Shiro < 1.6.0 验证绕过漏洞 CVE-2020-13933
漏洞信息
漏洞编号:CVE-2020-13933 / CNVD-2020-46579
影响版本:shiro < 1.6.0
漏洞描述:Shiro
由于处理身份验证请求时存在权限绕过漏洞,特制的HTTP
请求可以绕过身份验证过程并获得对应用程序的未授权访问。
漏洞补丁:Commit
漏洞分析
这个CVE其实就是对CVE-2020-11989 patch
的绕过。上一个CVE使用 getServletPath(request) + getPathInfo(request))
获取uri
,回顾一下:
request.getServletPath()
:返回除去Host
和工程名部分的路径;request.getPathInfo()
:仅返回传递到Servlet
的路径,如果没有传递额外的路径信息,则此返回Null
;
Shiro
在getChain
内进行权限验证,首先通过getPathWithinApplication(request)
获得uri
。从下图可以看到,更新后使用HttpServletRequest.getRequestURI()
方法获取uri
;然后使用removeSemicolon
去除uri
中的分号,这里去除的是分号及分号后面的内容;然后使用normalize
进行路径标准化。
此时得到的路径为/hello
,绕过了配置中的权限。
接着看Spring
是怎么处理路径的:
在org.springframework.web.util#UrlPathHelper
中的getPathWithinApplication
方法内,使用getRequestUri(request)
方法获取uri
与Shiro
处理的差异达到既绕过Shiro
权限验证又成功访问的目的。
漏洞修复
shiro
在1.6.0
版本中,org.apache.shiro.spring.web#ShiroFilterFactoryBean
中增加了/**
的默认路径配置,使其可以全局匹配进行过滤校验
默认的/**
配置对应一个全局的 filter
:InvalidRequestFilter
,这个类继承了 AccessControlFilter
。用来过滤特殊字符(分号、反斜线、非ASCII码字符),并返回 400
状态码。
Apache Shiro < 1.7.0 权限绕过漏洞 CVE-2020-17510
漏洞信息
漏洞编号:CVE-2020-17510 / CNVD-2020-60318
影响版本:shiro < 1.7.0
漏洞描述:第三种AntPathMatcher
的绕过方式
漏洞补丁:Commit
漏洞分析
这个漏洞还是对 AntPathMatcher
的继续绕过,在CVE-2020-11989
和CVE-2020-13933
分别尝试了 /
的双重 URL
编码和 ;
的 URL
编码绕过,归根到底这种方式还是因为Shiro
与Spring
对URI
处理的差异化导致的。那么字符 .
是不是也可以进行绕过呢?其实是可以的(测试环境Shiro 1.6.0
,SpringBoot 2.5.3
)
还是添加如下配置和Controller
map.put("/hello/*", "authc");
@GetMapping("/hello/{name}")
public String hello(@PathVariable String name) {
return "hello";
}
当Shiro
获得的uri
为/hello
时,是无法和/hello/*
匹配的,所以就在/hello
后面加上%2e
,这样Shiro
解码之后变成/hello/.
,然后路径标准化成为/hello
,绕过身份验证
对于Spring
来说,正如之前讲的,Spring Boot
版本在小于等于 2.3.0.RELEASE
时,会对uri
进行解码然后路径标准化,这样得到的路径为/hello
,没有页面与之匹配。所以只有当 Spring Boot
版本在大于 2.3.0.RELEASE
时标准化路径后/hello/%2e
,然后解码/hello/.
下面的payload
都可以使用:
/%2e
/%2e/
/%2e%2e
/%2e%2e/
漏洞修复
在Commit中发现org.apache.shiro.spring.web
下新增了ShiroUrlPathHelper
类,属于UrlPathHelper
的子类,重写了getPathWithinApplication
和getPathWithinServletMapping
两个方法
其实我认为1.7.1
才算真正的更新,因为它是依次对原uri
和去除uri
尾部斜线的uri
进行验证,这样就可以避免因直接去除尾部uri
导致/hello
和/hello/*
不匹配而导致的绕过问题。
补丁问题
问题一
根据官方发布的公告,发现其实需要配置shiro-spring-boot-web-starter
才有效
if you are NOT using Shiro’s Spring Boot Starter
(`shiro-spring-boot-web-starter`), you must configure add the
ShiroRequestMappingConfig auto configuration[1] to your application or
configure the equivalent manually[2].
[1] https://shiro.apache.org/spring-framework.html#SpringFramework-WebConfig
[2]https://github.com/apache/shiro/blob/shiro-root-1.7.0/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroRequestMappingConfig.java#L28-L30
由于我导入的dependency
如下
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</version>
</dependency>
如果直接将版本升为1.7.0
的话,其实并没有触发更新,原payload
还是可以绕过。
只有按照上面官网所述的两种配置方式修改后,才能防御成功
问题二
在旧版的SpringBoot 中,当我们需要获取当前请求地址的时候,直接通过如下方式获取:
//org.springframework.web.servlet.handler#getHandlerInternal
String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
但是在新版Spring里边,通过如下方式获取
String lookupPath = this.initLookupPath(request);
initLookupPath()代码如下:
protected String initLookupPath(HttpServletRequest request) {
if (this.usesPathPatterns()) {
request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE);
RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);
String lookupPath = requestPath.pathWithinApplication().value();
return UrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath);
} else {
return this.getUrlPathHelper().resolveAndCacheLookupPath(request);
}
}
如果this.usesPathPatterns() == true
的话,就可以绕开问题一中我们配置的ShiroUrlPathHelper
此时也成功绕过。
所以这就存在一个矛盾:只有Spring Boot
版本在大于 2.3.0.RELEASE
才能触发这个漏洞,修复之后由于版本问题,SpringBoot
又不走那条语句。
另外在配置的时候,当Spring Boot
版本在小于等于 2.3.0.RELEASE
,如2.1.5.RELEASE
,时,this.getUrlPathHelper()
并不是ShiroUrlPathHelper
,不清楚是不是配置问题还是版本兼容问题。
Apache Shiro < 1.7.1 权限绕过漏洞CVE-2020-17523
漏洞信息
漏洞编号:CVE-2020-17523 / CNVD-2021-09492
影响版本:shiro < 1.7.1
漏洞描述:Shiro 1.7.1
之前的版本,在将 Shiro
与 Spring
结合使用时,特制的 HTTP
请求可能会导致身份验证绕过。
漏洞补丁:Commit
漏洞分析
如CVE-2020-17510
那样,这个漏洞可以使用空格%20
进行绕过。
我们输入路径为http://localhost:8080/hello/%20
,进入getChain
,经过路径获取后要进行权限的匹配与验证
这里主要看一下/hello/
和/hello/*
比较时发生了什么
经过pathMatches(pathPattern, requestURI)
-> pathMatcher.matches(pattern, path)
-> match(pattern, source)
-> doMatch(pattern, path, true)
来到了主要的判断方法doMatch()
。
其中StringUtils.tokenizeToStringArray()
方法是将它的参数,也就是传进来的两个路径拆解成字符串数组,然后进行比较。
进入方法,可以看到当对空格进行转换时,直接trim
为空
这样就导致与shiro
中的配置本意想违背,导致绕过。
然后在Spring
中的处理时,uri
又包含空格,这样就能访问到/hello/%20
页面
漏洞修复
在Commit中,主要修复点AntPathMatcher.java
,在tokenizeToStringArray
方法中加了false
和true
两个参数
可以看到,当第三个参数为false
时,即trimTokens
为false
,此时就不会对token
进行trim
。
Apache shiro 认证机制不恰当 CVE-2021-41303
漏洞信息
漏洞编号:CVE-2021-41303 / SHIRO-825
影响版本:shiro < 1.8.0
漏洞描述:1.8.0
之前的 Apache Shiro
,在 Spring Boot
中使用 Apache Shiro
时,特制的 HTTP
请求可能会导致身份验证绕过。用户应该更新到 Apache Shiro 1.8.0
。
漏洞补丁:Commit
参考:[threedr3am师傅](https://threedr3am.github.io/2021/09/22/从源码diff分析Apache-Shiro 1.7.1版本的auth bypass(CVE-2021-41303)/)
漏洞分析
根据[threedr3am师傅](https://threedr3am.github.io/2021/09/22/从源码diff分析Apache-Shiro 1.7.1版本的auth bypass(CVE-2021-41303)/)博客提供的方向,看了一下Shiro 1.7.1前后PathMatchingFilterChainResolver#getChain
的对比
发现在1.7.1
版本中,先是对pathPattern和requestURI进行比较,比较成功,返回:
filterChainManager.proxy(originalChain, pathPattern);
否则对删除尾部斜线的pathPattern和requestURI进行比较,比较成功,跳出循环,返回:
filterChainManager.proxy(originalChain, requestURINoTrailingSlash);
但是正常访问,都会返回第一个proxy
,什么时候才能绕过第一个比较并符合第二个比较呢?
可以看到,两者差别是对uri
尾部斜线的处理,所以当在uri
尾部加一个/
,就会进入第二种比较方式。
结合之前的多次调试再根据threedr3am师傅
博客中的认证,可以知道shiro
的认证鉴权会根据配置的先后顺序去依次实施
所以当我有如下配置时:
map.put("/admin/*", "authc");
map.put("/admin/page", "anon");
循环中先匹配到/admin/*
(这里是通过while
语句对去除尾部斜线的uri
进行匹配),然后跳出循环,进入到filterChainManager.proxy(originalChain, requestURINoTrailingSlash);
,注意,这里真正的参数就是去除尾部斜线的uri
,也就是/admin/page
,所以在DefaultFilterChainManager#getChain
中得到的权限是anon
,这样就达到绕过目的。
漏洞修复
直接将filterChainManager.proxy
的第二个参数改为pathPattern
,直接传配置中的uri
了
Apache Shiro RegExPatternMatcher 权限绕过漏洞 CVE-2022-32532
漏洞信息
漏洞编号:CVE-2022-32532
影响版本:shiro < 1.9.1
漏洞描述:在1.9.1
之前的Apache Shiro
中,RegexRequestMatcher
可能会被错误配置,从而在某些servlet
容器上被绕过。应用程序使用RegExPatternMatcher
与.
的正则表达式可能容易被授权绕过。
漏洞补丁:Commit
参考:4ra1n师傅
###漏洞分析
这是最新的一个洞,看Shiro
发布的公告显示,是由于RegexRequestMatcher
的错误配置导致的问题。
简单了解了一下,RegexRequestMatcher
和AntPathMatcher
类似,都是Shiro
用于路径匹配的配置,只是RegexRequestMatcher
需要用户自己配置。
根据4ra1n师傅的分析,可以知道,正常正则表达式.
并不包含\r
和\n
字符
修改成如下代码就可修复问题
// flag为Pattern.DOTALL时,表达式 .可以匹配任何字符,包括行结束符。
Pattern pattern = Pattern.compile(regex,Pattern.DOTALL);
那么回头看一下RegexRequestMatcher
用于匹配的代码
public boolean matches(String pattern, String source) {
if (pattern == null) {
throw new IllegalArgumentException("pattern argument cannot be null.");
} else {
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(source);
return m.matches();
}
}
可以发现,当pattern
存在带.
的正则表达式并且source
中存在\r
或\n
字时,此时判断错误。
此时我们在配置完RegexRequestMatcher
之后增加如下Controller
@RequestMapping(path = "/alter/{value}")
public String alter(@PathVariable String value) {
System.out.println("绕过成功");
return "绕过成功"+value;
}
增加如下配置
//myFilter.java中设置成需要权限
manager.addToChain("/alter/.*", "myFilter");
这样正常访问/alter/aaa
是被拒绝的,但是当访问/alter/a%0aaa
或/alter/a%0daa
时就会绕成验证,访问成功
这个洞限制还是比较多的,既要服务器配置了RegExPatternMatcher
,又要设置带有.
的正则表达式
漏洞修复
在Commit可以看到,对compile
方法设置了flag
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。后续可能会有评论区,不过也可以在github联系我。