spring-security 三种情况下的认证绕过
0x01: antMatchers 配置认证绕过
问题概览:
当使用 antMatchers
配合 spring-webmvc
这个常见搭配使用时,如果代码编写考虑不周,比如使用以下 spring-security
配置保护 /test
路由:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/test").access("hasRole('ADMIN')")
.antMatchers("/**").permitAll();
super.configure(http);
}
}
/test
路由对应的 TestController:
@RestController
public class TestController {
@RequestMapping(value = "/test", method = {RequestMethod.GET, RequestMethod.POST})
public Object test(){
return "ok";
}
}
此时由于 spring-webmvc
组件 spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java
代码中的 useTrailingSlashMatch
选项默认为 true
,导致攻击者可以访问 /test/
路由绕过 spring-security
对 /test
路由的保护,直接访问到 TestController
的效果。
漏洞存在于最新版(5.4.6) spring-security-web
和 spring-webmvc
(5.3.6) 的配合使用中,复测可使用如下配置:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
漏洞修复:
使用适配 spring-webmvc
组件的 MvcRequestMatcher
写法 mvcMatchers("/test").access("hasRole('ADMIN')")
或者使用 antMatchers("/test/**").access("hasRole('ADMIN')")
写法防止认证绕过。
个人点评:
这个安全问题我报告给了官方,但是官方回复说提供了 MvcRequestMatcher
写法来防止这个问题,官方文档有参考,不考虑是个安全问题。后面我仔细看了下,发现上面的漏洞,包括增加 MvcRequestMatcher
写法都是 cve-2016-5007 漏洞修复后的延伸。
github 搜索几种不同 RequestMatcher 代码写法片段的数量:
-
antMatchers 49k
-
mvcMatchers 1k
-
regexMatchers 549
可见 antMatchers
的使用占据绝对的优势,官方网站提供的 MvcRequestMatcher
写法使用量并不大。如果开发者一不注意,像示例中用 spring-security 匹配 /test
路由时在antMatchers
规则中写成 /test
而不是 /test/**
,那么就可能存在认证绕过漏洞。
0x02: regexMatchers 配置认证绕过
问题概览:
关键代码在 spring-security-web org.springframework.security.web.util.matcher.RegexRequestMatcher.java
类 matches 方法:
public boolean matches(HttpServletRequest request) {
if (this.httpMethod != null && request.getMethod() != null && this.httpMethod != valueOf(request.getMethod())) {
return false;
} else {
String url = request.getServletPath();
String pathInfo = request.getPathInfo();
String query = request.getQueryString();
if (pathInfo != null || query != null) {
StringBuilder sb = new StringBuilder(url);
if (pathInfo != null) {
sb.append(pathInfo);
}
if (query != null) {
sb.append('?').append(query);
}
url = sb.toString();
}
logger.debug(LogMessage.format("Checking match of request : '%s'; against '%s'", url, this.pattern));
return this.pattern.matcher(url).matches();
}
}
request.getServletPath
获得 url 路径后,还会尝试把 ? 和后面的参数拼接上去,作为 url,然后用 this.pattern.matcher(url).matches()
去和 spring-security-web 配置的 pattern 匹配。
此时,如果配置的 regex pattern 有问题,比如对下面/test
路由对应的 TestController:
@RestController
public class TestController {
@RequestMapping(value = "/test", method = {RequestMethod.GET, RequestMethod.POST})
public Object test(){
return "ok";
}
}
对于下面的几种匹配规则,当请求 /test?
路径时,上面 matches 方法中的 url 参数值都会是/test?
,导致认证绕过:
.regexMatchers("/test").access("hasRole('ADMIN')")
.regexMatchers("/test/").access("hasRole('ADMIN')")
.regexMatchers("/test/*").access("hasRole('ADMIN')")
.regexMatchers("/test/.*").access("hasRole('ADMIN')")
漏洞修复:
换下面的正则匹配即可:
.regexMatchers("/test.*?").access("hasRole('ADMIN')")
个人点评:
regexMatchers 使用量比较小,不过要是开发者想当然的用错误正则去匹配以为的 url 路径,就会导致意外的认证绕过漏洞。
0x03: useSuffixPatternMatch 低版本认证绕过
问题概览:
低版本 的 spring-webmvc 及其相关组件,包括:
spring-webmvc <= 5.2.4.RELEASE
spring-framework <= 5.2.6.RELEASE
spring-boot-starter-parent <= 2.2.5.RELEASE
在代码中定义的 useSuffixPatternMatch
配置默认值为 true
,表示使用后缀匹配模式匹配路径。
如 /path/abc
路由也会允许 /path/abcd.ef
、/path/abcde.f
等增加 .xxx
后缀形式的路径匹配成功。
漏洞修复:
使用高版本的 spring-webmvc 能有效避免问题。
个人点评:
在低版本 spring-webmvc 下十分好用。
评论