0x00 漏洞復現(xiàn)
pom.xml
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
application.properties
server.context-path=/test
ShiroConfig
@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
bean.setLoginUrl("/login");
bean.setSuccessUrl("/index");
bean.setUnauthorizedUrl("/unauthorizedurl");
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("/hello/*", "authc");
//map.put("/hello/**", "authc"); //in version 1.7.0 will not trigger CVE-2020-17523
bean.setFilterChainDefinitionMap(map);
return bean;
}
spring
@RequestMapping("/hello/{name}")
public String hello2(@PathVariable String name) {
return "auth hello/{_" + name + "_}, there ";
}
觸發(fā)權(quán)限繞過的訪問請求如下:
http://127.0.0.1:8080/test/hello/a%252fa
response如下:auth hello/{a%2fa}, there
正常的訪問請求如下:
http://127.0.0.1:8080/test/hello/aa
response如下:跳轉(zhuǎn)到登錄頁面。
"/"的URL編碼為"%2f",在瀏覽器中"%"二次編碼為"%252f"伦忠。
觸發(fā)此漏洞的根源在于shiro在進行filter匹配的過程中背伴,對url進行了兩次解碼梭姓;而在spring的框架中,并未進行兩次URL解碼哈雏。因此兩者造成了不一致。
0x01 源碼分析
PathMatchingFilterChainResolver.java文件中的getchain函數(shù)中如下調(diào)用,獲取requestURI贩疙,導致requestURI被解析為"/hello/a/a"讹弯,而無法與pattern"/hello/*"匹配上。
String requestURI = getPathWithinApplication(request);
在WebUtils.java中getPathWithinApplication函數(shù)
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;
}
}
getRequestUri函數(shù)
public static String getRequestUri(HttpServletRequest request) {
String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
if (uri == null) {
uri = valueOrEmpty(request.getContextPath()) + "/" +
valueOrEmpty(request.getServletPath()) +
valueOrEmpty(request.getPathInfo());
}
return normalize(decodeAndCleanUriString(request, uri));
}
decodeAndCleanUriString函數(shù)
private static String decodeAndCleanUriString(HttpServletRequest request, String uri) {
uri = decodeRequestString(request, uri);
int semicolonIndex = uri.indexOf(';');
return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri);
}
decodeRequestString函數(shù)
public static String decodeRequestString(HttpServletRequest request, String source) {
String enc = determineEncoding(request);
try {
return URLDecoder.decode(source, enc);
} catch (UnsupportedEncodingException ex) {
if (log.isWarnEnabled()) {
log.warn("Could not decode request string [" + Encode.forHtml(source) + "] with encoding '" + Encode.forHtml(enc) +
"': falling back to platform default encoding; exception message: " + ex.getMessage());
}
return URLDecoder.decode(source);
}
}
因為采用ant風格的匹配这溅,所以组民,如果pattern配置為"/hello/**"則不會觸發(fā)此漏洞。
patch分析
diff
https://github.com/apache/shiro/compare/shiro-root-1.5.2...shiro-root-1.5.3
getPathWithinApplication函數(shù)進行了升級悲靴,不再調(diào)用會進行二次URL解碼的getRequestUri函數(shù)臭胜。
public static String getPathWithinApplication(HttpServletRequest request) {
return normalize(removeSemicolon(getServletPath(request) + getPathInfo(request)));
}
private static String getServletPath(HttpServletRequest request) {
String servletPath = (String) request.getAttribute(INCLUDE_SERVLET_PATH_ATTRIBUTE);
return servletPath != null ? servletPath : valueOrEmpty(request.getServletPath());
}
private static String getPathInfo(HttpServletRequest request) {
String pathInfo = (String) request.getAttribute(INCLUDE_PATH_INFO_ATTRIBUTE);
return pathInfo != null ? pathInfo : valueOrEmpty(request.getPathInfo());
}
private static String valueOrEmpty(String input) {
if (input == null) {
return "";
}
return input;
}
在shiro 1.5.3版本中,沒有地方再調(diào)用WebUtils.getRequestUri癞尚,該方法標識為已廢棄耸三。
那么廢棄了getRequestUri方法是否會觸發(fā)CVE-2020-1957呢?
答案是并不會浇揩,在getPathWithinApplication方法中并未直接獲取uri仪壮,而是通過getServletPath和getPathInfo分別獲取的。