Apache Shiro before 1.5.2, when using Apache Shiro with Spring dynamic controllers, a specially crafted request may cause an authentication bypass. [1]
本漏洞有两种攻击方式,一个适用于shiro < 1.5.0 ,另一个是Shiro 1.5.0版本修复补丁考虑不全面而导致,适用于Shiro < 1.5.2
本篇文章将分别讨论这两种攻击方式。(我觉得已经算是两个漏洞了)
shiro < 1.5.0
配置环境:
shiro-1.4.2 //依赖的还是 javax
spring-boot:2.7.4 //3.xx版本 最低java 17且依赖的是jakarta
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.2</version>
</dependency>
shiro配置
配置分为两部分:
package com.codereview.cve.config;
import java.util.LinkedHashMap;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
@Bean
public IniRealm getIniRealm() {
return new IniRealm("classpath:shiro.ini");
}
@Bean
public SimpleCookie rememberMeCookie(){
return new SimpleCookie("rememberMe");
}
@Bean
DefaultWebSecurityManager securityManager(IniRealm iniRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm((Realm)iniRealm);
manager.setRememberMeManager(new CookieRememberMeManager());
return manager;
}
@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....
//对于未设置的url,默认策略是放行,不做任何限制
map.put("/print", "authc, perms[printer:print]");
map.put("/query", "authc, perms[printer:query]");
map.put("/login","authc");
bean.setFilterChainDefinitionMap(map);
return bean;
}
}
shiro.ini:
permission分为域
:printer 和操作
:print,query
# format: roleName = permission1, permission2, ..., permissionN
[roles]
user = printer:print
admin = printer:*
# format: username = password, role1, role2, ..., roleN
[users]
user1 = pswd123, user
admin1 = pswd321, admin
controller
假设存在一个打印机:
package com.codereview.cve.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PrinterController {
@GetMapping("/login")
public String login() {
return "This is a login page";
}
@GetMapping("/hello")
public String index() {
return "Hello World";
}
@RequestMapping ("/loginSuccess")
public String loginSuccess() {
return "login success!";
}
@GetMapping("/print")
public String print() {
return "you are printing content...";
}
@GetMapping("/query")
public String query() {
return "you are querying content...";
}
@GetMapping("/unauthorized")
public String unauthorized() {
return "you are unauthorized";
}
}
漏洞测试
-
首先,以user1身份进行登入:
根据重定向的Location可知,登入成功(记住这时的Cookie中的sessionid)
-
然后,访问打印路径:正常打印
-
访问查询路径:失败,重定向到未授权路径
-
但是,当我们在后面加上一个
/
符号时:越权访问到了查询操作!!!
漏洞分析
为什么会导致上述的问题?接下来将从源码角度分析造成该漏洞的原因。
首先我们已知shiro-web的大致流程如下:(详情:shiro-web 软件分析 - FreeBuf网络安全行业门户
图中标红位置,为这此漏洞的入口。
该方法源码如下:
public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
FilterChainManager filterChainManager = getFilterChainManager();
if (!filterChainManager.hasChains()) {
return null;
}
//获取请求路径
String requestURI = getPathWithinApplication(request);
//遍历filterChainManager管理的所有过滤链的名字
//也就是配置文件中配置的"/print","/query","/print"
for (String pathPattern : filterChainManager.getChainNames()) {
//检查是否匹配,如果匹配则返回pathPattern对应的过滤链
if (pathMatches(pathPattern, requestURI)) {
if (log.isTraceEnabled()) {
log.trace("省略");
}
return filterChainManager.proxy(originalChain, pathPattern);
}
}
return null;
}
源码调试
注意看此时requestURI: "/query/"
查看filterChainManager.getChainNames()
的返回值:与配置文件吻合
结果毫不意外的没有任何一个匹配:
在shiro中,当前路径没有匹配到配置路径时默认策略是直接放行,所以这个末尾添加了/
的恶意路径绕过了shiro的路径匹配。
但**为什么最终又能准确路由到对应的页面呢?**明明路径中多了一个/
?
那这就是spring-web的路径匹配问题了。[[2]](Apache Shiro权限绕过漏洞CVE-2020-1957-安全狗)
spring web的路径匹配
这一部分就不赘述了,根据引用[2],可知路径匹配入口方法:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
匹配方法还是将字符按/
分割成数组后按元素匹配,如“/query" 被分割成["/", "query"]
核心代码:
...
...
// 当匹配了"query"这个词后,匹配下一个部分,所以自加后值为2
pathIndex++;
//但是"query" 已经是pattern(/query) 的最后一部分了
if (isNoMorePattern()) {
if (matchingContext.determineRemainingPath) {
matchingContext.remainingPathIndex = pathIndex;
return true;
}
else {
//很“/query”与“/query/”明显长度不一致
if (pathIndex == matchingContext.pathLength) {
return true;
}
else {
//matchingContext是当前要匹配的路径 “/query/”,长度为3 : ["/","query","/"]
//如果选项允许,加上结尾是分割符则仍然匹配
return (matchingContext.isMatchOptionalTrailingSeparator() &&
(pathIndex + 1) == matchingContext.pathLength &&
matchingContext.isSeparator(pathIndex));
}
}
}
选项:
漏洞修复
该漏洞在shiro 1.5.0 时被修复
修复逻辑非常简单,就是在匹配时将请求路径和配置路径末尾的分割符/
都去掉
shiro<1.5.2
导致该漏洞的出现是shiro与spring对url的处理不一致导致的:
对象 | url处理 |
---|---|
shiro | 将url中分号; 以及后面部分全部去掉 |
spring | 如果分号后还有 带/ 的路径,则仅去除分号到/ 之间的内容,保留其余部分 (不包括分号) |
配置环境
shiro版本选择的是shiro 1.5.0
controller中加一个路由:
@GetMapping("/admin/info")
public String adminInfo() {
return "Admin Info";
}
路径配置加一条:
@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/info", "authc, roles[admin]");
map.put("/login","authc");
bean.setFilterChainDefinitionMap(map);
return bean;
}
漏洞测试
-
首先,重新登入一次:
-
然后正常访问路径:访问失败,重定向到未授权页面!
-
在路径中添加一个分号:访问成功,越权!!!
漏洞分析
shiro
入口还是org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain
方法。定位到获取requestURI的代码
跟进
跟进标红函数
跟进decodeAndCleanUriString:
代码逻辑很简单,就是剥离分号后面的部分,因此当我们的请求是/admin;/info
时,返回的是/admin
,而这个路径是没有配置的,所以shirofilter会放行,交给DispatcherServlet (spring web)
spring
入口:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
最后不断跟进initLookupPath(request)
找到处理分号的函数:
逻辑很简单,就是循环去除,分号和分割符之间的内容
测试了一下的确如此:
漏洞修复
左边是shiro 1.5.0,右边版本是shiro 1.5.2,
来看一下两边差异:
前者:
后者:
由图可知,request.getServletPath()对url中分号的处理与spring一致。
多的一个/
被normalize函数去除:
Reference
[1] Security Reports | Apache Shiro
[2] Apache Shiro权限绕过漏洞CVE-2020-1957-安全狗
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)