CVE-2020-17523 shiro认证鉴权绕过复现
影响版本
- Apache Shiro < 1.7.1
环境搭建
参考 https://github.com/jweny/shiro-cve-2020-17523
漏洞分析
代码作者设定:
- 访问
/login
页面会提示please login
- 访问
/admin/*
,*
代表任意,网站会自动跳转至/login
页面,表示用户未登录鉴权失败 - 直接访问
/admin
会直接报错404,因为没有可以映射的url
代码在ShiroConfig
中配置了shiro过滤器,可参考下图:
ShiroFilterFactoryBean shiroFilterFactoryBean(){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
bean.setLoginUrl("/login");
bean.setSuccessUrl("/index");
bean.setUnauthorizedUrl("/unauthorizedurl");
Map<String, String> map = new LinkedHashMap<>();
map.put("/doLogin/", "anon");
map.put("/admin/*", "authc");
bean.setFilterChainDefinitionMap(map);
return bean;
}
漏洞利用:
访问/admin/%20
会绕过shiro的鉴权认证,url会由spring处理跳转至admin页面:
通过比较shiro1.7.0
和shiro1.7.1
的源码:https://sourcegraph.com/github.com/apache/shiro/-/compare/shiro-root-1.7.0...shiro-root-1.7.1
可以发现主要改动的代码为core/src/main/java/org/apache/shiro/util/AntPathMatcher.java
:
直接在IDEA中搜索AntPatchMatcher类:
在doMatch
函数第一行打上断点,在多次步进后进入到/admin/
和/admin/*
的匹配阶段:
可以发现pathDirs
中的空格已经丢失,所以这里的/admin/%20
就直接变成了/admin
分析StringUtils.tokenizeToStringArray
:
//trimTokens=true,str=/admin/ ,delimiters="/",ignoreEmptyTokens=true
public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
if (str == null) {
return null;
} else {
StringTokenizer st = new StringTokenizer(str, delimiters); //用于分割字符串
ArrayList tokens = new ArrayList();
while(true) {
String token;
do {
if (!st.hasMoreTokens()) { //判断是否有多余的分隔符
return toStringArray(tokens);
}
token = st.nextToken(); //获取当前位置到下一个分隔符之间的字符串
if (trimTokens) { //是否对token进行特殊字符的删除,包括空格、换行符、\t等,由于这里为true,所以获取到空格时,空格被去掉了。
token = token.trim();
}
} while(ignoreEmptyTokens && token.length() <= 0);
tokens.add(token);
}
}
}
所以这里的/admin/%20
未匹配到自定义的shiro过滤器中的/admin/*
,最后url直接映射到Spring的GetMapping里了,然后就直接返回了admin页面。
漏洞修复
升级到0.17.1版本,通过查看代码可以知道官方修复时将参数trimTokens 设置为false
。