聲明
由于傳播、利用此文所提供的信息而造成的任何直接或者間接的后果及損失,均由使用者本人負(fù)責(zé),雷神眾測以及文章作者不為此承擔(dān)任何責(zé)任。
雷神眾測擁有對此文章的修改和解釋權(quán)。如欲轉(zhuǎn)載或傳播此文章,必須保證此文章的完整性,包括版權(quán)聲明等全部內(nèi)容。未經(jīng)雷神眾測允許,不得任意修改或者增減此文章內(nèi)容,不得以任何方式將其用于商業(yè)目的。
No.1
簡述
前段時(shí)間太忙了,忙到很多東西,只是記錄了筆記,沒有成文,剛好最近階段又出來了shiro權(quán)限繞過漏洞,因此本文將這三個(gè)權(quán)限繞過的洞進(jìn)行對比,他們的編號分別是 CVE-2020-1957、CVE-2020-11989、CVE-2020-13933 。
No.2
漏洞細(xì)節(jié)
1、CVE-2020-1957
原理
首先在 admin 位置下斷點(diǎn),可以看到,我們網(wǎng)絡(luò)請求,是先經(jīng)過shiro 處理之后,再轉(zhuǎn)發(fā)到springboot進(jìn)行路由分發(fā)工作。
這里直接定位到 shiro處理url的方法位置:WebUtils# getPathWithinApplication
public static String getPathWithinApplication(HttpServletRequest request) {
String contextPath = getContextPath(request);
String requestUri = getRequestUri(request);
if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) {
// Normal case: URI contains context path.
String path = requestUri.substring(contextPath.length);
return (StringUtils.hasText(path) ? path : "/");
} else {
// Special case: rather unusual.
return requestUri;
}
}
實(shí)際上繼續(xù)跟進(jìn) getRequestUri(request);這個(gè)方法,可以清楚的看到,實(shí)際上調(diào)用的是getRequestURI 方法來獲取路由中的URI 請求。
這里的 URI就是我們傳入的/xxx/..;/hello/aaaa,也就是說回到 getRequestUri(request);當(dāng)中,會帶著這個(gè)傳入的URI進(jìn)入decodeAndCleanUriString 進(jìn)行處理。
public static String getRequestUri(HttpServletRequest request) {
String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
if (uri == ) {
uri = request.getRequestURI;
}
return normalize(decodeAndCleanUriString(request, uri));
}
在 decodeAndCleanUriString方法中會根據(jù)我們的傳入的URI中;進(jìn)行截?cái)嗵幚恚簿褪钦f經(jīng)過處理之后,返回的結(jié)果變成了/xxx/..
而 normalize 方法就會對我們傳入的path進(jìn)行一些處理,從注釋上,也能知道這部分代碼處理了什么東西:
?替換\為/
?替換/./為/
?替換/../為/
?...
private static String normalize(String path, boolean replaceBackSlash) {
if (path == )
return ;
// Create a place for the normalized path
String normalized = path;
if (replaceBackSlash && normalized.indexOf('\') >= 0)
normalized = normalized.replace('\', '/');
if (normalized.equals("/."))
return "/";
// Add a leading "/" if necessary
if (!normalized.startsWith("/"))
normalized = "/" + normalized;
// Resolve occurrences of "//" in the normalized path
while (true) {
int index = normalized.indexOf("//");
if (index < 0)
break;
normalized = normalized.substring(0, index) +
normalized.substring(index + 1);
}
// Resolve occurrences of "/./" in the normalized path
while (true) {
int index = normalized.indexOf("/./");
if (index < 0)
break;
normalized = normalized.substring(0, index) +
normalized.substring(index + 2);
}
// Resolve occurrences of "/../" in the normalized path
while (true) {
int index = normalized.indexOf("/../");
if (index < 0)
break;
if (index == 0)
return ; // Trying to go outside our context
int index2 = normalized.lastIndexOf('/', index - 1);
normalized = normalized.substring(0, index2) +
normalized.substring(index + 3);
}
// Return the normalized path that we have completed
return (normalized);
}
而這里經(jīng)過處理,我們的 URI 依然是/xxx/..,接著就會回到PathMatchingFilterChainResolver# getChain方法,進(jìn)行權(quán)限匹配,我們的路徑是/hello/**下需要進(jìn)行權(quán)限認(rèn)證,由于路徑不匹配,所以權(quán)限校驗(yàn)自然過了。
這里在提一嘴,可以看看 PathMatchingFilterChainResolver# getChain方法這一小段代碼,這一段代碼修復(fù)的是 Shiro-682 (https://issues.Apache.org/jira/browse/SHIRO-682),具體描述可以點(diǎn)入鏈接查看。簡單翻譯一下就是在spring web下,通過請求 /resource/menus和/resource/menus/都是能夠訪問到資源的,但是shiro的路徑正則只會匹配到/resource/menus,忽略了 /resource/menus/ ,所以這就繞過了。
好了,這里提一下這個(gè)地方,再回到我們剛剛上面的情況里,由于我們傳入的 URI/xxx/..與權(quán)限認(rèn)證的URI/hello/**不匹配,繞過了權(quán)限驗(yàn)證之后,進(jìn)入 springboot當(dāng)中進(jìn)行路由分發(fā),而在spring當(dāng)中UrlPathHelper# getPathWithinServletMapping 這個(gè)方法負(fù)責(zé)處理我們傳入的URI:xxx/..;/hello/aaaa,結(jié)果是返回servletPath。
public String getPathWithinServletMapping(HttpServletRequest request) {
String pathWithinApp = getPathWithinApplication(request);
String servletPath = getServletPath(request);
String sanitizedPathWithinApp = getSanitizedPath(pathWithinApp);
String path;
// If the app container sanitized the servletPath, check against the sanitized version
if (servletPath.contains(sanitizedPathWithinApp)) {
path = getRemainingPath(sanitizedPathWithinApp, servletPath, false);
}
else {
path = getRemainingPath(pathWithinApp, servletPath, false);
}
...
// Otherwise, use the full servlet path.
return servletPath;
}
}
看看 servletPath是怎么來的,這玩意的取值是通過request.getServletPath;獲取到的,也就是說這里的結(jié)果是/hello/aaaa。這里通過springboot進(jìn)行分發(fā),自然獲取到后臺接口內(nèi)容,整個(gè)流程:
用戶發(fā)起請求/xxx/..;/hello/aaaa----->shiro處理之后返回/xxx/..通過校驗(yàn)的----->springboot處理/xxx/..;/hello/aaaa返回/hello/aaaa,最后訪問到需要權(quán)限校驗(yàn)的資源。
public String getServletPath(HttpServletRequest request) {
String servletPath = (String) request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE);
if (servletPath == ) {
servletPath = request.getServletPath;
}
if (servletPath.length > 1 && servletPath.endsWith("/") && shouldRemoveTrailingServletPathSlash(request)) {
// On WebSphere, in non-compliant mode, for a "/foo/" case that would be "/foo"
// on all other servlet containers: removing trailing slash, proceeding with
// that remaining slash as final lookup path...
servletPath = servletPath.substring(0, servletPath.length - 1);
}
return servletPath;
}
修復(fù)
shiro在1.5.2當(dāng)中把之前的通過 getRequestURI獲取URI的方式變成了getContextPath 、getServletPath 、getPathInfo 的組合。
這么處理之后自然變成了想要的東西。
2、CVE-2020-11989
原理
這里的 shiro攔截器需要變成map.put("/hello/*", "authc");,這里有兩種poc,都是可以繞過
/hello/a%25%32%66a
/;/test/hello/aaa
我們知道在shiro中的WebUtils# getPathWithinApplication這里會處理我們傳入的url,在 getRequestUri方法會調(diào)用decodeAndCleanUriString進(jìn)行處理。
public static String getRequestUri(HttpServletRequest request) {
String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
if (uri == ) {
uri = valueOrEmpty(request.getContextPath) + "/" +valueOrEmpty(request.getServletPath) +valueOrEmpty(request.getPathInfo);
}
return normalize(decodeAndCleanUriString(request, uri));
}
在 decodeAndCleanUriString當(dāng)中會調(diào)用decodeRequestString針對URI進(jìn)行一次URL解碼。
private static String decodeAndCleanUriString(HttpServletRequest request, String uri) {
uri = decodeRequestString(request, uri);
int semicolonIndex = uri.indexOf(';');
return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri);
}
public static String decodeRequestString(HttpServletRequest request, String source) {
String enc = determineEncoding(request);
try {
return URLDecoder.decode(source, enc);
} catch (UnsupportedEncodingException ex) {
if (log.isWarnEnabled) {
...
}
return URLDecoder.decode(source);
}
}
所以這里的poc/hello/a%25%32%66a------>傳入到shiro自動解碼一次變成//hello/a%2fa------>經(jīng)過 decodeRequestString 變成//hello/a/a由于這里我們的攔截器是map.put("/hello/*", "authc");,這里需要了解一下shiro的URL是ant格式,路徑是支持通配符表示的
?:匹配一個(gè)字符
*:匹配零個(gè)或多個(gè)字符串
**:匹配路徑中的零個(gè)或多個(gè)路徑
/*只能命中/hello/aaa這種格式,無法命中/hello/a/a,所以經(jīng)過 shiro 進(jìn)行權(quán)限判斷的時(shí)候自然無法命中。
而在spring當(dāng)中,理解的 servletPath是/hello/a%2fa,所以自然命中@GetMapping("/hello/{name}")這個(gè)mapping,又springboot轉(zhuǎn)發(fā)到響應(yīng)的路由當(dāng)中。
另一種利用方式來自這里《Apache Shiro權(quán)限繞過漏洞分析(CVE-2020-11989)》(https://xz.aliyun.com/t/7964),這里提到了
1.應(yīng)用不能部署在根目錄,也就是需要context-path,server.servlet.context-path=/test,如果為根目錄則context-path為空,就會被CVE-2020-1957的patch將URL格式化,值得注意的是若Shiro版本小于1.5.2的話那么該條件就不需要。
這里原因在于需要繞過 getRequestUri當(dāng)中的格式化uri,當(dāng)context-path為空的時(shí)候,處理結(jié)果為//hello/aaaa
public static String getRequestUri(HttpServletRequest request) {
String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
if (uri == ) {
uri = valueOrEmpty(request.getContextPath) + "/" +
valueOrEmpty(request.getServletPath) +
valueOrEmpty(request.getPathInfo);
}
return normalize(decodeAndCleanUriString(request, uri));
}
當(dāng) context-path不為空的時(shí)候,處理結(jié)果為/;/test/hello/aaaa,然后我們知道decodeAndCleanUriString會根據(jù);進(jìn)行截?cái)啵財(cái)嘀蟮慕Y(jié)果是/自然無法命中攔截器map.put("/hello/*", "authc");,所以自然就繞過了。
修復(fù)
在1.5.3版本,采用標(biāo)準(zhǔn)的 getServletPath和getPathInfo進(jìn)行uri處理,同時(shí)取消了url解碼。
public static String getPathWithinApplication(HttpServletRequest request) {
return normalize(removeSemicolon(getServletPath(request) + getPathInfo(request)));
}
3、CVE-2020-13933
原理
/hello/%3baaaa
上面的代碼進(jìn)來之后,通過 getPathWithinApplication處理之后變成了/hello/;aaaa
而 removeSemicolon會根據(jù);進(jìn)行截?cái)啵祷氐?strong>uri自然是/hello/
private static String removeSemicolon(String uri) {
int semicolonIndex = uri.indexOf(';');
return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri);
}
這個(gè) uri自然無法命中攔截器map.put("/hello/*", "authc");自然就過了
修復(fù)
加了一個(gè) filter類InvalidRequestFilter來針對一些東西進(jìn)行處理。
private static final List<String> SEMICOLON = Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B"));
private static final List<String> BACKSLASH = Collections.unmodifiableList(Arrays.asList("\", "%5c", "%5C"));
No.3
小結(jié)
總結(jié)來看,就是利用 shiro 解析uri 和spring解析uri之間的差異來挖這個(gè)洞。
招聘啟事
安恒雷神眾測SRC運(yùn)營(實(shí)習(xí)生)
————————
【職責(zé)描述】
1. 負(fù)責(zé)SRC的微博、微信公眾號等線上新媒體的運(yùn)營工作,保持用戶活躍度,提高站點(diǎn)訪問量;
2. 負(fù)責(zé)白帽子提交漏洞的漏洞審核、Rank評級、漏洞修復(fù)處理等相關(guān)溝通工作,促進(jìn)審核人員與白帽子之間友好協(xié)作溝通;
3. 參與策劃、組織和落實(shí)針對白帽子的線下活動,如沙龍、發(fā)布會、技術(shù)交流論壇等;
4. 積極參與雷神眾測的品牌推廣工作,協(xié)助技術(shù)人員輸出優(yōu)質(zhì)的技術(shù)文章;
5. 積極參與公司媒體、行業(yè)內(nèi)相關(guān)媒體及其他市場資源的工作溝通工作。
【任職要求】
1. 責(zé)任心強(qiáng),性格活潑,具備良好的人際交往能力;
2. 對網(wǎng)絡(luò)安全感興趣,對行業(yè)有基本了解;
3. 良好的文案寫作能力和活動組織協(xié)調(diào)能力。
簡歷投遞至 strategy@dbappsecurity.com.cn
設(shè)計(jì)師(實(shí)習(xí)生)
————————
【職位描述】
負(fù)責(zé)設(shè)計(jì)公司日常宣傳圖片、軟文等與設(shè)計(jì)相關(guān)工作,負(fù)責(zé)產(chǎn)品品牌設(shè)計(jì)。
【職位要求】
1、從事平面設(shè)計(jì)相關(guān)工作1年以上,熟悉印刷工藝;具有敏銳的觀察力及審美能力,及優(yōu)異的創(chuàng)意設(shè)計(jì)能力;有 VI 設(shè)計(jì)、廣告設(shè)計(jì)、畫冊設(shè)計(jì)等專長;
2、有良好的美術(shù)功底,審美能力和創(chuàng)意,色彩感強(qiáng);精通Photoshop/illustrator/coreldrew/等設(shè)計(jì)制作軟件;
3、有品牌傳播、產(chǎn)品設(shè)計(jì)或新媒體視覺工作經(jīng)歷;
【關(guān)于崗位的其他信息】
企業(yè)名稱:杭州安恒信息技術(shù)股份有限公司
辦公地點(diǎn):杭州市濱江區(qū)安恒大廈19樓
學(xué)歷要求:本科及以上
工作年限:1年及以上,條件優(yōu)秀者可放寬
簡歷投遞至 strategy@dbappsecurity.com.cn
安全招聘
————————
公司:安恒信息
崗位:Web安全 安全研究員
部門:戰(zhàn)略支援部
薪資:13-30K
工作年限:1年+
工作地點(diǎn):杭州(總部)、廣州、成都、上海、北京
工作環(huán)境:一座大廈,健身場所,醫(yī)師,帥哥,美女,高級食堂…
【崗位職責(zé)】
1.定期面向部門、全公司技術(shù)分享;
2.前沿攻防技術(shù)研究、跟蹤國內(nèi)外安全領(lǐng)域的安全動態(tài)、漏洞披露并落地沉淀;
3.負(fù)責(zé)完成部門滲透測試、紅藍(lán)對抗業(yè)務(wù);
4.負(fù)責(zé)自動化平臺建設(shè)
5.負(fù)責(zé)針對常見WAF產(chǎn)品規(guī)則進(jìn)行測試并落地bypass方案
【崗位要求】
1.至少1年安全領(lǐng)域工作經(jīng)驗(yàn);
2.熟悉HTTP協(xié)議相關(guān)技術(shù)
3.擁有大型產(chǎn)品、CMS、廠商漏洞挖掘案例;
4.熟練掌握php、JAVA、asp.net代碼審計(jì)基礎(chǔ)(一種或多種)
5.精通Web Fuzz模糊測試漏洞挖掘技術(shù)
6.精通OWASP TOP 10安全漏洞原理并熟悉漏洞利用方法
7.有過獨(dú)立分析漏洞的經(jīng)驗(yàn),熟悉各種Web調(diào)試技巧
8.熟悉常見編程語言中的至少一種(Asp.net、Python、php、java)
【加分項(xiàng)】
1.具備良好的英語文檔閱讀能力;
2.曾參加過技術(shù)沙龍擔(dān)任嘉賓進(jìn)行技術(shù)分享;
3.具有CISSP、CISA、cssLP、ISO27001、ITIL、PMP、COBIT、Security+、CISP、OSCP等安全相關(guān)資質(zhì)者;
4.具有大型SRC漏洞提交經(jīng)驗(yàn)、獲得年度表彰、大型CTF奪得名次者;
5.開發(fā)過安全相關(guān)的開源項(xiàng)目;
6.具備良好的人際溝通、協(xié)調(diào)能力、分析和解決問題的能力者優(yōu)先;
7.個(gè)人技術(shù)博客;
8.在優(yōu)質(zhì)社區(qū)投稿過文章;
崗位:安全紅隊(duì)武器自動化工程師
薪資:13-30K
工作年限:2年+
工作地點(diǎn):杭州(總部)
【崗位職責(zé)】
1.負(fù)責(zé)紅藍(lán)對抗中的武器化落地與研究;
2.平臺化建設(shè);
3.安全研究落地。
【崗位要求】
1.熟練使用Python、java、c/c++等至少一門語言作為主要開發(fā)語言;
2.熟練使用Django、flask 等常用web開發(fā)框架、以及熟練使用MySQL、mongoDB、redis等數(shù)據(jù)存儲方案;
3:熟悉域安全以及內(nèi)網(wǎng)橫向滲透、常見web等漏洞原理;
4.對安全技術(shù)有濃厚的興趣及熱情,有主觀研究和學(xué)習(xí)的動力;
5.具備正向價(jià)值觀、良好的團(tuán)隊(duì)協(xié)作能力和較強(qiáng)的問題解決能力,善于溝通、樂于分享。
【加分項(xiàng)】
1.有高并發(fā)tcp服務(wù)、分布式等相關(guān)經(jīng)驗(yàn)者優(yōu)先;
2.在github上有開源安全產(chǎn)品優(yōu)先;
3:有過安全開發(fā)經(jīng)驗(yàn)、獨(dú)自分析過相關(guān)開源安全工具、以及參與開發(fā)過相關(guān)后滲透框架等優(yōu)先;
4.在freebuf、安全客、先知等安全平臺分享過相關(guān)技術(shù)文章優(yōu)先;
5.具備良好的英語文檔閱讀能力。
簡歷投遞至 strategy@dbappsecurity.com.cn
崗位:紅隊(duì)武器化Golang開發(fā)工程師
薪資:13-30K
工作年限:2年+
工作地點(diǎn):杭州(總部)
【崗位職責(zé)】
1.負(fù)責(zé)紅藍(lán)對抗中的武器化落地與研究;
2.平臺化建設(shè);
3.安全研究落地。
【崗位要求】
1.掌握C/C++/Java/Go/Python/JavaScript等至少一門語言作為主要開發(fā)語言;
2.熟練使用Gin、Beego、Echo等常用web開發(fā)框架、熟悉MySQL、Redis、MongoDB等主流數(shù)據(jù)庫結(jié)構(gòu)的設(shè)計(jì),有獨(dú)立部署調(diào)優(yōu)經(jīng)驗(yàn);
3.了解Docker,能進(jìn)行簡單的項(xiàng)目部署;
3.熟悉常見web漏洞原理,并能寫出對應(yīng)的利用工具;
4.熟悉TCP/IP協(xié)議的基本運(yùn)作原理;
5.對安全技術(shù)與開發(fā)技術(shù)有濃厚的興趣及熱情,有主觀研究和學(xué)習(xí)的動力,具備正向價(jià)值觀、良好的團(tuán)隊(duì)協(xié)作能力和較強(qiáng)的問題解決能力,善于溝通、樂于分享。
【加分項(xiàng)】
1.有高并發(fā)tcp服務(wù)、分布式、消息隊(duì)列等相關(guān)經(jīng)驗(yàn)者優(yōu)先;
2.在github上有開源安全產(chǎn)品優(yōu)先;
3:有過安全開發(fā)經(jīng)驗(yàn)、獨(dú)自分析過相關(guān)開源安全工具、以及參與開發(fā)過相關(guān)后滲透框架等優(yōu)先;
4.在freebuf、安全客、先知等安全平臺分享過相關(guān)技術(shù)文章優(yōu)先;
5.具備良好的英語文檔閱讀能力。
簡歷投遞至 strategy@dbappsecurity.com.cn