影響版本
- 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
。