標題:Apache Shiro 身份驗證繞過漏洞復現(cve-2020-1957)

taibeihacker

Moderator

0x00 漏洞描述​

Apache Shiro 1.5.2之前版本中存在安全漏洞。攻擊者可藉助特製的請求利用該漏洞繞過身份驗證。 Shiro框架通過攔截器功能來對用戶訪問權限進行控制,如anon, authc等攔截器。 anon為匿名攔截器,不需要登錄即可訪問;authc為登錄攔截器,需要登錄才可以訪問。 Shiro的URL路徑表達式為Ant格式,路徑通配符*表示匹配零個或多個字符串,/*可以匹配/hello,但是匹配不到/hello/,因為*通配符無法匹配路徑。假設/hello接口設置了authc攔截器,訪問/hello會進行權限判斷,但如果訪問的是/hello/,那麼將無法正確匹配URL,直接放行,進入到spring攔截器。 spring中的/hello和/hello/形式的URL訪問的資源是一樣的,從而實現了權限繞過。

0x01 漏洞影响​

Apache Shiro 1.5.2

0x02 Shiro拦截器​

Shiro框架通過攔截器功能來實現對用戶訪問權限的控制和攔截。 Shiro中常見的攔截器有anon,authc等攔截器。 1.anon為匿名攔截器,不需要登錄就能訪問,一般用於靜態資源,或者移動端接口
2.authc為登錄攔截器,需要登錄認證才能訪問的資源。用戶可以在Shiro.ini編寫匹配URL配置,將會攔截匹配的URL,並執行響應的攔截器。從而實現對URL的訪問控制,URL路徑表達式通常為ANT格式。如下配置,訪問/index.html主頁的時候,Shiro將不會對其進行登錄判斷,anon攔截器不需要登錄就能進行訪問。而對於/user/xiaoming 等/user/xiaogang等接口,authc攔截器將會對其進行登錄判斷,有登錄認證才能訪問資源。 [urls]
/index.html=anon
/user/**=authc
Shiro的URL路徑表達式為Ant 格式,路徑通配符支持?***。匹配一個字符
*:匹配零個或多個字符串
**:匹配路徑中的零個或多個路徑
其中*表示匹配零個或多個字符串,/*可以匹配/hello,但匹配不到/hello/因為*通配符無法匹配路徑。假設/hello接口設置了authc攔截器,訪問/hello將會被進行權限判斷,如果請求的URI為/hello/呢,/*URL路徑表達式將無法正確匹配,放行。然後進入到spring(Servlet)攔截器,spring中/hello形式和/hello/形式的URL訪問的資源是一樣的。

0x03 环境搭建​

下載demo代碼:https://github.com/lenve/javaboy-code-samples/tree/master/shiro/shiro-basic導入ideaShiro版本1.4.2 dependency groupIdorg.apache.shiro/groupId artifactIdshiro-web/artifactId version1.4.2/version /dependency dependency groupIdorg.apache.shiro/groupId artifactIdshiro-spring/artifactId version1.4.2/version /dependency修改ShiroConfig配置文件,添加authc攔截器的攔截正則@Bean ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean(); //map.put('/*', 'authc'); map.put('/hello/*', 'authc'); bean.setFilterChainDefinitionMap(map); return bean; }修改路由控制器方法@GetMapping('/hello/{currentPage}') public String hello(@PathVariable Integer currentPage) { return 'hello';}通過idea進行編譯,可獲得war包這里通過docker快速搭建漏洞環境:docker pull vulfocus/shiro-cve_2020_1957
1049983-20201129040308848-1645994369.png
docker images
1049983-20201129040310600-1437781155.png
docker run -d -p 8080:8080 -v /var/run/docker.sock:/var/run/docker.sock -e VUL_IP=192.168.1.14 29136b1d3c61
1049983-20201129040311058-1699321640.png
-v /var/run/docker.sock:/var/run/docker.sock 為docker 交互連接。 -e DOCKER_URL 為Docker 連接方式,默認通過unix://var/run/docker.sock 進行連接,也可以通過tcp://xxx.xxx.xxx.xxx:2375 進行連接(必須開放2375 端口)。 -v /vulfocus-api/db.sqlite3:db.sqlite3 映射數據庫為本地文件。 -e VUL_IP=xxx.xxx.xxx.xxx 為Docker 服務器IP ,不能為127.0.0.1。http://192.168.1.14:8080/login
1049983-20201129040312412-1520455583.png

0x04 漏洞复现​

1、Shiro1.4.2版本繞過權限訪問/hello/1接口,可以看到被authc攔截器攔截了,將會跳轉到登錄接口進行登錄。
1049983-20201129040312899-107128082.png
訪問/hello/1/,成功繞過authc攔截器,獲取到了資源。
1049983-20201129040313397-685140363.png
2、Shiro1.4.2版本繞過漏洞分析漏洞初始成因可以定位到PathMatchingFilterChainResolver的getChain函數下,該函數作用根據URL路徑匹配中配置的url路徑表達式來匹配輸入的URL,判斷是否匹配攔截器,匹配成功將會返迴響應的攔截器執行鏈,讓ShiroFither執行權限操作的。其對於URL路徑表達式和輸入URL的匹配主要通過pathMathches函數進行匹配。
nzave1w2qhe22266.png
pathMatches函數其最終會調用shiro.util.AntPathMatcher類中doMatch的對於ant格式的pathPattern和requestURI進行匹配。 //pathMatches:135, PathMatchingFilterChainResolver (org.apache.shiro.web.filter.mgt)
protected boolean pathMatches(String pattern, String path) {
PatternMatcher pathMatcher=this.getPathMatcher();
return pathMatcher.matches(pattern, path);
}
doMatch:109, AntPathMatcher (org.apache.shiro.util),當Shiro 的Ant格式的pathPattern 中的的*通配符是不支持匹配路徑的,所以/hello/*不能成功匹配/hello/1/,也就不會觸發authc攔截器進行權限攔截。從而成功繞過了Shiro攔截器,而後再進入到spring攔截器中,/hello/1/與/hello/1能獲取到相同的資源。
1049983-20201129040314275-1093796335.jpg
3、Shiro≤1.5.1版本繞過在1.5.1版本中,/hello/會直接跳轉到登錄
1049983-20201129040314835-1924922284.png
繞過payload,/fdsf;/./hello/1,成功繞過。
1049983-20201129040315283-20701281.png
或者其他payload,xxxx/./hello/1,成功繞過(shiro的1.5.1及其之前的版本)
1049983-20201129040315783-1999845617.png
其中以上處理過程:客戶端請求URL:/xxxx/./hello/1shrio 內部處理得到校驗URL為/xxxx/.校驗通過springboot 處理/xxxx/./hello/1, 最終請求/hello, 成功訪問了後台請求.4.Shiro≤1.5.1版本漏洞分析問題同樣可以定位到getChain函數中對於requestURI的獲取中,如下圖所示,this.getPathWithinApplication(request)獲取的requestURI為/fdsf,而不是我們輸入的/fdsf;/./hello/1,從而導致後面的URI路徑模式匹配返回False,從而再次繞過了shiro攔截器。
ak55la4wjn222271.png
getPathWithinApplication函數中會調用WebUtils (org.apache.shiro.web.util)中的getRequestUri函數獲取RequestUri。 public static String getRequestUri(HttpServletRequest request) {
String uri=(String)request.getAttribute('javax.servlet.include.request_uri');
if (uri==null) {
uri=request.getRequestURI();
}
return normalize(decodeAndCleanUriString(request, uri));
}
RequestUri函數中最終調用decodeAndCleanUriString函數對URI進行清洗。
private static String decodeAndCleanUriString(HttpServletRequest request, String uri) {
uri=decodeRequestString(request, uri);
int semicolonIndex=uri.indexOf(59);//獲取;號的位置
return semicolonIndex !=-1 ? uri.substring(0, semicolonIndex) : uri;
}
如果URI中存在;號的話,則會刪除其後面的所有字符。 /fdsf;/./hello/1/最終也就變成了/fdsf。
vo3qxw1i2rq22272.png
5.漏洞總結在web容器中,Shiro的攔截器是先與spring(Servlet)執行,兩者攔截器對於URI模式匹配的差異,導致Shiro攔截器的繞過,而Shiro對其進行了兩次修復,其第一次在shiro1.4.2版本出現漏洞後為刪除requestURI後面的/號進行URL路徑匹配,算是簡單的修復了添加/號繞過的方式,而後在1.5.2版本中通過requestURI自主拼接的方式修復了/fdsf;/./hello/1/等使用了;號方式的繞過

0x05 修复方案​

1.升級1.5.2版本及以上在shiro1.5.2版本已加入的過濾器規則:@Test void testGetRequestUriWithServlet() { dotTestGetPathWithinApplicationFromRequest('/', '/servlet', '/foobar', '/servlet/foobar') dotTestGetPathWithinApplicationFromRequest('', '/servlet', '/foobar', '/servlet/foobar') dotTestGetPathWithinApplicationFromRequest('', 'servlet', '/foobar', '/servlet/foobar') dotTestGetPathWithinApplicationFromRequest('/', 'servlet', '/foobar', '/servlet/foobar') dotTestGetPathWithinApplicationFromRequest('//', 'servlet', '/foobar', '/servlet/foobar') dotTestGetPathWithinApplicationFromRequest('//', '//servlet', '//foobar', '/servlet/foobar') dotTestGetPathWithinApplicationFromRequest('/context-path', '/servlet', '/foobar', '/servlet/foobar') dotTestGetPathWithinApplicationFromRequest('//context-path', '//servlet', '//foobar', '/servlet/foobar') dotTestGetPathWithinApplicationFromRequest('//context-path', '/servlet', '/./servlet/other', '/servlet/other') dotTestGetPathWithinApplicationFromRequest('//context-path', '/asdf', '/./servlet/other', '/servlet/other') dotTestGetPathWithinApplicationFromRequest('//context-path', '/asdf', ';/./servlet/other', '/asdf') dotTestGetPathWithinApplicationFromRequest('/context%2525path', '/servlet', '/foobar', '/servlet/foobar') dotTestGetPathWithinApplicationFromRequest('/c%6Fntext%20path', '/servlet', '/foobar', '/servlet/foobar') dotTestGetPathWithinApplicationFromRequest('/context path', '/servlet', '/foobar', '/servlet/foobar') dotTestGetPathWithinApplicationFromRequest('', null, null, '/') dotTestGetPathWithinApplicationFromRequest('', 'index.jsp', null, '/index.jsp') }
2.盡量避免使用*通配符作為動態路由攔截器的URL路徑表達式。

0x06 参考文献​

https://www.zhihuifly.com/t/topic/2822 https://paper.seebug.org/1196/https://xz.aliyun.com/t/8281
 
返回
上方