此漏洞爆发应急于中秋前,因为网上到现在也没看到有师傅分析这个漏洞,所以在中秋后对此漏洞进行了自我理解式的具体分析~
0x00漏洞描述
Apache Shiro是阿帕奇(Apache)基金会的一套用于执行认证、授权、加密和会话管理的Java安全框架。
近日,Apache Shiro被披露出身份验证绕过漏洞,攻击者使用特制的 HTTP 请求可进行身份验证绕过。
0x01影响版本
Apache Shiro < 1.8.0
0x02环境搭建
基本环境采用:https://github.com/lenve/javaboy-code-samples/tree/master/shiro/shiro-basic ,拉入idea前修改shiro版本为1.7.1
配置路径拦截器,服务启动时该配置会被当做规则一样写入filterChains中。测试环境暂时最好以以下顺序和路径进行配置,原因是在shiro的鉴权配置中,使用的LinkedHashMap是一个有序的HashMap,而我认为shiro的认证鉴权会根据配置的先后顺序去依次拦截
配置访问路由
debug启动服务之后,访问 http://localhost:8080/admin/vv/page/
0x03漏洞分析
首先在PathMatchingFilterChainResolver
类中断点getChain
方法
fiterChains
是配置过的拦截器,当中有对访问路径做匹配拦截的规则,而request
当中的coyoteRequest
就是现在url所访问的路径
经过 getPathWithinApplication
和 WebUtils.getPathWithinApplication
方法得到 requestURL
即/admin/vv/page/
removeTrailingSlash
对路径末尾做做反斜杠的删除得到
由于拦截器的顺序,URL会经过pathMatches先匹配/admin/*/page
拦截规则
String pathPattern;
do {
if (!var7.hasNext()) {
return null;
}
pathPattern = (String)var7.next();
if (this.pathMatches(pathPattern, requestURI)) {
if (log.isTraceEnabled()) {
log.trace("Matched path pattern [{}] for requestURI [{}]. Utilizing corresponding filter chain...", pathPattern, Encode.forHtml(requestURI));
}
......
protected boolean pathMatches(String pattern, String path) {
PatternMatcher pathMatcher = this.getPathMatcher();
return pathMatcher.matches(pattern, path);
}
matches --> match --> 然后来到AntPathMatcher
类中的doMatch
方法,先通过StringUtils.tokenizeToStringArray
方法将路径以“/”对其拆分成数组
在这里对数组中的每个字符做*和强对比的循环
正因为URI后多加一个/,就能让requestURI
和pathPattern
匹配不上,直接进入else,并且能在else中的if使其pathPattern
和requestURINoTrailingSlash
成功匹配上。所以根据第一个拦截器的匹配不成功则又来到doMatch
匹配第二个拦截器。
protected boolean pathsMatch(String path, ServletRequest request) {
String requestURI = this.getPathWithinApplication(request);
log.trace("Attempting to match pattern '{}' with current requestURI '{}'...", path, Encode.forHtml(requestURI));
boolean match = this.pathsMatch(path, requestURI);
if (!match) {
if (requestURI != null && !"/".equals(requestURI) && requestURI.endsWith("/")) {
requestURI = requestURI.substring(0, requestURI.length() - 1);
}
if (path != null && !"/".equals(path) && path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
log.trace("Attempting to match pattern '{}' with current requestURI '{}'...", path, Encode.forHtml(requestURI));
match = this.pathsMatch(path, requestURI);
}
return match;
}
protected boolean pathsMatch(String pattern, String path) {
boolean matches = this.pathMatcher.matches(pattern, path);
log.trace("Pattern [{}] matches path [{}] => [{}]", new Object[]{pattern, path, matches});
return matches;
}
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
if (this.appliedPaths != null && !this.appliedPaths.isEmpty()) {
Iterator var3 = this.appliedPaths.keySet().iterator();
String path;
do {
if (!var3.hasNext()) {
return true;
}
path = (String)var3.next();
} while(!this.pathsMatch(path, request));
log.trace("Current requestURI matches pattern '{}'. Determining filter chain execution...", path);
Object config = this.appliedPaths.get(path);
return this.isFilterChainContinued(request, response, path, config);
} else {
if (log.isTraceEnabled()) {
log.trace("appliedPaths property is null or empty. This Filter will passthrough immediately.");
}
return true;
}
}
以上代码对/admin/vv/page/
做了末尾“/”的删除后得到/admin/vv/page
又重新进入拦截器进行路径匹配
与拦截器匹配成功则返回matches
为true
由于mtahces为true则代表拦截器匹配成功,故最后来到在AdviceFilter
类中doFilterInternal
方法,并且输出指定路由的页面信息
而访问 http://localhost:8080/admin/vv/page 是无法成功匹配至拦截器路径,因此以上的绕过则是利用“/”达到访问权限的绕过
以下为漏洞利用时函数调用栈的全部内容
doMatch:139, AntPathMatcher (org.apache.shiro.util)
match:97, AntPathMatcher (org.apache.shiro.util)
matches:93, AntPathMatcher (org.apache.shiro.util)
pathsMatch:159, PathMatchingFilter (org.apache.shiro.web.filter)
pathsMatch:127, PathMatchingFilter (org.apache.shiro.web.filter)
preHandle:195, PathMatchingFilter (org.apache.shiro.web.filter)
doFilterInternal:131, AdviceFilter (org.apache.shiro.web.servlet)
doFilter:125, OncePerRequestFilter (org.apache.shiro.web.servlet)
doFilter:66, ProxiedFilterChain (org.apache.shiro.web.servlet)
executeChain:108, AdviceFilter (org.apache.shiro.web.servlet)
doFilterInternal:137, AdviceFilter (org.apache.shiro.web.servlet)
doFilter:125, OncePerRequestFilter (org.apache.shiro.web.servlet)
doFilter:66, ProxiedFilterChain (org.apache.shiro.web.servlet)
executeChain:450, AbstractShiroFilter (org.apache.shiro.web.servlet)
call:365, AbstractShiroFilter$1 (org.apache.shiro.web.servlet)
doCall:90, SubjectCallable (org.apache.shiro.subject.support)
call:83, SubjectCallable (org.apache.shiro.subject.support)
execute:387, DelegatingSubject (org.apache.shiro.subject.support)
doFilterInternal:362, AbstractShiroFilter (org.apache.shiro.web.servlet)
doFilter:125, OncePerRequestFilter (org.apache.shiro.web.servlet)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:99, RequestContextFilter (org.springframework.web.filter)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:92, FormContentFilter (org.springframework.web.filter)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:93, HiddenHttpMethodFilter (org.springframework.web.filter)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:200, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
invoke:200, StandardWrapperValve (org.apache.catalina.core)
invoke:96, StandardContextValve (org.apache.catalina.core)
invoke:490, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:139, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:74, StandardEngineValve (org.apache.catalina.core)
service:343, CoyoteAdapter (org.apache.catalina.connector)
service:408, Http11Processor (org.apache.coyote.http11)
process:66, AbstractProcessorLight (org.apache.coyote)
process:836, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1747, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1142, ThreadPoolExecutor (java.util.concurrent)
run:617, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)
0x04修复建议
请检查所使用的软件版本是否受在受影响范围内,并从官方渠道升级到安全版本或更新版本。Apache Shiro官方网站:https://shiro.apache.org/index.html
- 本文作者: w1nk1
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/800
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!