Security context
High· 8.8GHSA-cxrj-66c5-9fmh CVE-2018-1258CWE-863Published Oct 17, 2018

Spring Framework when used in combination with any versions of Spring Security contains an authorization bypass

Research this vulnerability

Research is free — Hunters explains how the bug works, the root-cause code pattern, how the fix addresses it, and how to test whether a target is affected, in chat. Investigate & write exploit is a paid run — the engine reads the advisory and fix commits, then builds and validates a working proof-of-concept exploit with reproduction steps.

Affected versions

5.0.5.RELEASE → fixed in 5.0.6.RELEASE

Details

Spring Framework version 5.0.5 when used in combination with any versions of Spring Security contains an authorization bypass when using method security. An unauthorized malicious user can gain unauthorized access to methods that should be restricted.

The fix

Add accessDeniedHandler method to ExceptionHandlingSpec

Denys Ivano· May 7, 2018, 04:09 PM+16007b8fa90d96
config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java+17 0
@@ -171,6 +171,8 @@ public class ServerHttpSecurity {
private List<DelegateEntry> defaultEntryPoints = new ArrayList<>();
+ private ServerAccessDeniedHandler accessDeniedHandler;
+
private List<WebFilter> webFilters = new ArrayList<>();
private Throwable built;
@@ -526,6 +528,9 @@ public SecurityWebFilterChain build() {
exceptionTranslationWebFilter.setAuthenticationEntryPoint(
authenticationEntryPoint);
}
+ if(accessDeniedHandler != null) {
+ exceptionTranslationWebFilter.setAccessDeniedHandler(accessDeniedHandler);
+ }
this.addFilterAt(exceptionTranslationWebFilter, SecurityWebFiltersOrder.EXCEPTION_TRANSLATION);
this.authorizeExchange.configure(this);
}
@@ -793,6 +798,18 @@ public ExceptionHandlingSpec authenticationEntryPoint(ServerAuthenticationEntryP
return this;
}
+ /**
+ * Configures what to do when an authenticated user does not hold a required authority
+ * @param accessDeniedHandler the access denied handler to use
+ * @return the {@link ExceptionHandlingSpec} to configure
+ *
+ * @since 5.0.5
+ */
+ public ExceptionHandlingSpec accessDeniedHandler(ServerAccessDeniedHandler accessDeniedHandler) {
+ ServerHttpSecurity.this.accessDeniedHandler = accessDeniedHandler;
+ return this;
+ }
+
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
config/src/test/java/org/springframework/security/config/web/server/ExceptionHandlingSpecTests.java+143 0
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2002-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.config.web.server;
+
+import org.junit.Test;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;
+import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
+import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
+import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
+import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.Credentials.basicAuthenticationCredentials;
+import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
+
+/**
+ * @author Denys Ivano
+ * @since 5.0.5
+ */
+public class ExceptionHandlingSpecTests {
+ private ServerHttpSecurity http = ServerHttpSecurityConfigurationBuilder.httpWithDefaultAuthentication();
+
+ @Test
+ public void defaultAuthenticationEntryPoint() {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .csrf().disable()
+ .authorizeExchange()
+ .anyExchange().authenticated()
+ .and()
+ .exceptionHandling()
+ .and()
+ .build();
+
+ WebTestClient client = WebTestClientBuilder
+ .bindToWebFilters(securityWebFilter)
+ .build();
+
+ client
+ .get()
+ .uri("/test")
+ .exchange()
+ .expectStatus().isUnauthorized()
+ .expectHeader().valueMatches("WWW-Authenticate", "Basic.*");
+ }
+
+ @Test
+ public void customAuthenticationEntryPoint() {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .csrf().disable()
+ .authorizeExchange()
+ .anyExchange().authenticated()
+ .and()
+ .exceptionHandling()
+ .authenticationEntryPoint(redirectServerAuthenticationEntryPoint("/auth"))
+ .and()
+ .build();
+
+ WebTestClient client = WebTestClientBuilder
+ .bindToWebFilters(securityWebFilter)
+ .build();
+
+ client
+ .get()
+ .uri("/test")
+ .exchange()
+ .expectStatus().isFound()
+ .expectHeader().valueMatches("Location", ".*");
+ }
+
+ @Test
+ public void defaultAccessDeniedHandler() {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .csrf().disable()
+ .httpBasic().and()
+ .authorizeExchange()
+ .anyExchange().hasRole("ADMIN")
+ .and()
+ .exceptionHandling()
+ .and()
+ .build();
+
+ WebTestClient client = WebTestClientBuilder
+ .bindToWebFilters(securityWebFilter)
+ .filter(basicAuthentication())
+ .build();
+
+ client
+ .get()
+ .uri("/admin")
+ .attributes(basicAuthenticationCredentials("user", "password"))
+ .exchange()
+ .expectStatus().isForbidden();
+ }
+
+ @Test
+ public void customAccessDeniedHandler() {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .csrf().disable()
+ .httpBasic().and()
+ .authorizeExchange()
+ .anyExchange().hasRole("ADMIN")
+ .and()
+ .exceptionHandling()
+ .accessDeniedHandler(httpStatusServerAccessDeniedHandler(HttpStatus.BAD_REQUEST))
+ .and()
+ .build();
+
+ WebTestClient client = WebTestClientBuilder
+ .bindToWebFilters(securityWebFilter)
+ .filter(basicAuthentication())
+ .build();
+
+ client
+ .get()
+ .uri("/admin")
+ .attributes(basicAuthenticationCredentials("user", "password"))
+ .exchange()
+ .expectStatus().isBadRequest();
+ }
+
+ private ServerAuthenticationEntryPoint redirectServerAuthenticationEntryPoint(String location) {
+ return new RedirectServerAuthenticationEntryPoint(location);
+ }
+
+ private ServerAccessDeniedHandler httpStatusServerAccessDeniedHandler(HttpStatus httpStatus) {
+ return new HttpStatusServerAccessDeniedHandler(httpStatus);
+ }
+}

References