di0xide@深蓝攻防实验室天玄战队
SpringSecurity 是在java开发中,很常用的一个库,用于身份验证、授权和安全性等功能上,在我们白盒测试的时候,可以通过分析代码是否使用SpringSecurity,从而来寻找是否存在权限绕过的漏洞。本文将介绍一些SpringSecurity常见的权限绕过漏洞。
这是CHATGPT对SpringSecurity库的介绍
antMatchers() 是 Spring Security 中用于匹配请求路径的方法。它基于 Ant 风格的路径匹配规则。在Spring Security中,antMatchers() 通常用于定义需要进行安全性配置的URL模式。
简单来说,antMatchers()是一个匹配路由的函数,当匹配到后面的路由后,会找到对应的权限规则。
? 匹配任意单个字符
* 匹配0个或多个字符
** 匹配0个或多个目录
例子:
antMatchers("/admin/**").access("hasRole('ADMIN')")
意思是:当访问的路由是/admin/**的时候,要求当前的权限是ADMIN
例子:
antMatchers(“/js/**”).permitAll()
意思是:放行所有js目录下的文件,无需权限校验
例子:
antMatchers(“/**/*.js”).permitAll()
也可以这么写,指的是放行所有的js
package com.example.springsecdemo2.demos;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class antMatchersConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/admin").access("hasRole('ADMIN')")
.antMatchers("/**").permitAll();
return http.build();
}
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
这里使用了antMatchers("/test").access("hasRole('ADMIN')"),只有ADMIN才可以访问test,但是没有去按照正常的写法: /test/** ,导致可以通过/test/,来绕过对/test 的校验
对应的controller
正常情况:
访问/admin,提示403
访问/admin/,通过校验
可以使用
mvcMatchers(“/test”).access(“hasRole(‘ADMIN’)”)
或者
antMatchers(“/test/**”).access(“hasRole(‘ADMIN’)”)
使用正则表达式进行匹配。
和 antMatchers()主要的区别就是参数,antMatchers()参数是 ant 表达式,regexMatchers()参数是正则表达式。
例子:
regexMatchers(“.+[.]js”).permitAll()
所有以.js结尾的都放行
例子:
regexMatchers("/manager.*?").access("hasRole('ADMIN')"
manager开头的路由,必须ADMIN权限才可以访问
public class AuthConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.regexMatchers("/manager").access("hasRole('ADMIN')")
.regexMatchers("/**").access("anonymous");
······
这里的意思是通过正则匹配到admin路由后,判断权限是否为ADMIN,但是由于正则没有写完整。攻击者可以使用/admin? 的方式或者/admin/ 来绕过权限校验
http://127.0.0.1:8012/test?
http://127.0.0.1:8012/test/
正常访问:
bypass:
将正则写完整
.regexMatchers("/manager.*?").access("hasRole('ADMIN')")
除了上面两个旧的匹配函数,目前官方在推requestMatcher函数来进行匹配路由,会比较安全。
spring-webmvc版本在<=4.3.25的情况下suffixPatternMatch默认为True。
该方法为是否启用后辍匹配,如果启用,则映射到/users的方法也可以匹配到/users.*,
/users 和/users.*是相等的。
网上说的都是小于5.3配置就为true,经过测试5.2.15的代码里,默认是False
5.0.4 也不行
经过测试,需要满足条件:springboot <= 1.5.22.RELEASE
对应的mvc版本,即为 <=4.3.25
此时
Spring MVC版本在<=4.3.25的情况下suffixPatternMatch默认为True。
新建项目,选择spring版本为低版本
然后在pom.xml中修改版本
1 <properties> 2 <java.version>1.8</java.version> 3 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 4 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 5 <spring-boot.version>1.5.22.RELEASE</spring-boot.version> 6 </properties>
然后可以看到Spring MVC版本以及相关组件的版本<=4.3.25
在这种情况下,无论配置文件怎么写路由校验,都会出现漏洞,例如:
场景1 :
假设这里/admin 系列的路由都不允许访问
package com.example.springsecdemo2.demos.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class AuthController {
@RequestMapping(value = {"/admin"}, method = {RequestMethod.GET})
@ResponseBody
public String admin() {
return "hello admin";
}
@RequestMapping(value = {"/test"}, method = {RequestMethod.GET})
@ResponseBody
public String test() {
return "hello test";
}
}
/admin这个路由看起来似乎也没有什么问题,常规的绕过方式也不起作用
但在低版本(1.x)的springboot上还是能绕过,使用/admin. 即可
http://127.0.0.1:8080/admin.123456789
使用新版本即可
HttpSecurity | Controller | bypass |
---|---|---|
.regexMatchers("/admin/.*").access("hasRole('ADMIN')") | /admin/api/ | ❌ |
.regexMatchers("/admin/.*").access("hasRole('ADMIN')") | /admin/** | ✅ |
Spring Security 是 Spring 家族中的一个安全管理框架。在特定版本中,使用regexMatchers去匹配路由的时候,会调用RegexRequestMatcher(),然而因为错误配置会导致使用==.==可以进行权限绕过。
影响版本:
Spring Security 5.6.x < 5.6.4
Spring Security 5.5.x < 5.5.7
Spring Security 5.4.x <5.4.11
说明
当然,该漏洞有些鸡肋,因为需要同时满足2个条件:
这种就会绕过,
但是当访问写死的/admin/api的时候,虽然能绕过权限,但是无法匹配到路由
Spring Security 5.6.x < 5.6.4
Spring Security 5.5.x < 5.5.7
Spring Security 5.4.x <5.4.11
/admin/%0a
/admin/%0d
更新版本
当Spring Security 版本处于特定版本时,可能会存在通过 forward 或 include 调度类型绕过授权规则的风险。
影响版本未确认
具体来说,当使用Spring Security的时候,一般会创建一个SecurityFilterChain来为特定的路由设置权限,路由A为匿名访问权限,路由B为高权限,如果在Controller中定义低权限A路由return的时候,使用了forward 或 include 请求,将会导致将低权限的请求转发或包含到一个更高权限的安全端点,从而实现越权。
==HttpSecurity==
==Controller==
package com.example.springsecdemo2.demos;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class antMatchersConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/forward").permitAll()
.antMatchers("/admin/**").access("hasRole('ADMIN')");
// .antMatchers("/**").permitAll();
// .antMatchers("/test/**").permitAll();
// @formatter:on
return http.build();
}
// @formatter:off
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
// @formatter:on
}
package com.example.springsecdemo2.demos.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class AuthController {
@RequestMapping(value = {"/forward"}, method = {RequestMethod.GET})
// @GetMapping("/forward")
public String forward() {
return "include:/admin";
//return "forward:/admin";
}
@RequestMapping(value = {"/admin/api"}, method = {RequestMethod.GET})
@ResponseBody
public String adminapi() {
return "hello adminapi";
}
@RequestMapping(value = {"/admin/**"}, method = {RequestMethod.GET})
@ResponseBody
public String admin() {
return "hello admin";
}
@RequestMapping(value = {"/test/api"}, method = {RequestMethod.GET})
@ResponseBody
public String testapi() {
return "hello testapi";
}
@RequestMapping(value = {"/test/**"}, method = {RequestMethod.GET})
@ResponseBody
public String test() {
return "hello test";
}
// @RequestMapping(value = {"/**"}, method = {RequestMethod.GET})
// @ResponseBody
// public String guest() {
// return "hello guest";
// }
}
通过访问低权限路由,然后转发请求到高权限上,实现越权,常出现在配置不当
升级版本
在Spring Security中,ServerHttpSecurity和HttpSecurity分别用于配置WebFlux和Web MVC应用程序的安全性。
1import org.springframework.context.annotation.Bean;
2import org.springframework.security.config.web.server.ServerHttpSecurity;
3import org.springframework.security.web.server.SecurityWebFilterChain;
4public class SecurityConfig {
5
6@Bean
7public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
8 return http
9 .authorizeExchange()
10 .pathMatchers("/public/**").permitAll()
11 .pathMatchers("/admin/**").hasRole("ADMIN")
12 .anyExchange().authenticated()
13 .and()
14 .build();
15}
16}
17
18#### 1.7.1.2 HttpSecurity:
19- `HttpSecurity`用于配置基于Spring MVC的应用程序的安全性。它适用于使用传统的Spring MVC框架构建的Web应用程序。
20- 在使用`HttpSecurity`时,你可以配置基于URL模式的安全性,例如通过使用`.antMatchers()`和`.authorizeRequests()`。
21```java
22import org.springframework.context.annotation.Bean;
23import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
24import org.springframework.security.config.annotation.web.builders.HttpSecurity;
25import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
26
27@EnableWebSecurity
28public class SecurityConfig extends WebSecurityConfigurerAdapter {
29
30 @Override
31 protected void configure(HttpSecurity http) throws Exception {
32 http
33 .authorizeRequests()
34 .antMatchers("/public/**").permitAll()
35 .antMatchers("/admin/**").hasRole("ADMIN")
36 .anyRequest().authenticated()
37 .and()
38 .formLogin()
39 .and()
40 .httpBasic();
41 }
42}
43
总的来说,ServerHttpSecurity用于响应式Web应用程序(WebFlux),而HttpSecurity用于传统的基于Spring MVC的Web应用程序。
https://spring.io/security/cve-2023-34034/
在Spring Security配置中对WebFlux使用 ** 作为匹配会导致Spring Security和Spring WebFlux之间的模式匹配不一致,并可能导致安全绕过。
通俗来说,就是在HttpSecurity 配置的时候,.antMatchers("admin/**").access("hasRole('ADMIN')"), 匹配规则里没有以==/== 开始,从而导致了Spring Security和Spring WebFlux之间的模式匹配不一致,可能导致绕过
影响版本:
Spring Security:
6.1.0 to 6.1.1
6.0.0 to 6.0.4
5.8.0 to 5.8.4
5.7.0 to 5.7.9
5.6.0 to 5.6.11
==HttpSecurity.java==
return http
.authorizeExchange()
.pathMatchers("/**").permitAll()
.pathMatchers("admin/**").hasRole("ADMIN")
.anyExchange().authenticated()
.and()
.build();
==Controller.java==
@RequestMapping(value = {"/admin/**"}, method = {RequestMethod.GET})
@ResponseBody
public String admin() {
return "hello admin";
}
(环境一直报错,没能复现成功)
就可能会导致权限绕过(其实也不能算权限绕过,因为这样子配置,本身就是错误的,会导致配置失效)
正常访问即可
升级版本,写规则的时候以/开始