VYPR
High severityNVD Advisory· Published Nov 19, 2018· Updated Sep 17, 2024

UAA Privilege Escalation

CVE-2018-15761

Description

Cloud Foundry UAA release, versions prior to v64.0, and UAA, versions prior to 4.23.0, contains a validation error which allows for privilege escalation. A remote authenticated user may modify the url and content of a consent page to gain a token with arbitrary scopes that escalates their privileges.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven
< 4.23.04.23.0

Affected products

2
  • Range: all versions
  • Cloud Foundry/UAA Releasev5
    Range: all versions

Patches

4
f34df0ec7b06

Release v64.0

https://github.com/cloudfoundry/uaa-releaseUAA Identity BotOct 30, 2018via osv
3 files changed · +58 0
  • .final_builds/packages/uaa/index.yml+4 0 modified
    @@ -239,6 +239,10 @@ builds:
         version: 6bf986583ead5ef377db34ba005e9906e6e8f49d
         blobstore_id: 78e565d2-9d82-4202-bcb8-edf1dc036eb0
         sha1: 4d607dd4bb2c5b411d80dbd265fc3a675a520d93
    +  6e13a5edd2a3cceb79d181bcac64771ea95fcccd:
    +    version: 6e13a5edd2a3cceb79d181bcac64771ea95fcccd
    +    blobstore_id: 14ab124d-2568-4ec2-4c2c-7e3090917b5f
    +    sha1: 5abbc685c74d54600b48ed300b71f96d6ee6f594
       72ae1ac3c77d562234fa32a002e2e720df64303e:
         version: 72ae1ac3c77d562234fa32a002e2e720df64303e
         blobstore_id: 421cba86-00e3-4305-8f5e-26c03044f58a
    
  • releases/uaa/index.yml+2 0 modified
    @@ -37,6 +37,8 @@ builds:
         version: "59"
       24dca834-abce-4612-b708-499ff3d05023:
         version: "44"
    +  259ac2dd-987c-41d2-6226-6a0e000f018a:
    +    version: "64.0"
       272a6cea-3bdd-498c-8a34-ac932149d11b:
         version: "30.7"
       2b93ae1f-1d14-4d9b-4bfb-a75b71b13975:
    
  • releases/uaa/uaa-64.0.yml+52 0 added
    @@ -0,0 +1,52 @@
    +name: uaa
    +version: "64.0"
    +commit_hash: 9dfeac2
    +uncommitted_changes: false
    +jobs:
    +- name: bbr-uaadb
    +  version: 3a3b455d3de6d7f3e5196f5fe8efe63f1b18b160
    +  fingerprint: 3a3b455d3de6d7f3e5196f5fe8efe63f1b18b160
    +  sha1: d41b9d10da857484c264f94fd373c5dab28d4029
    +- name: uaa
    +  version: 2590c68e0b957e34b86fe47794c8514cbc7950e5
    +  fingerprint: 2590c68e0b957e34b86fe47794c8514cbc7950e5
    +  sha1: 1c87934105a3e7333e4ed4eb0a71392530431351
    +- name: uaa_key_rotator
    +  version: 9f01e290f1086570487a3664a14ca86040d55141
    +  fingerprint: 9f01e290f1086570487a3664a14ca86040d55141
    +  sha1: 271367693e84103d1dbca989ebf3ea23bb3ba7c2
    +- name: uaa_postgres
    +  version: 79fe0a96f112f08932b265cabc43f473c77e607c
    +  fingerprint: 79fe0a96f112f08932b265cabc43f473c77e607c
    +  sha1: 9a1c568e14d1d32995f12c50c76370e4bae3698e
    +packages:
    +- name: golang-1.10-linux
    +  version: 1baefde79e99ed3d1df72084315333032a294e59
    +  fingerprint: 1baefde79e99ed3d1df72084315333032a294e59
    +  sha1: 212148ae3c30da937bea9403c857069c4530c4e3
    +  dependencies: []
    +- name: uaa
    +  version: 6e13a5edd2a3cceb79d181bcac64771ea95fcccd
    +  fingerprint: 6e13a5edd2a3cceb79d181bcac64771ea95fcccd
    +  sha1: 5abbc685c74d54600b48ed300b71f96d6ee6f594
    +  dependencies: []
    +- name: uaa_postgres
    +  version: 42df2bc21212094a1b61e3db697ab1c61260cb59
    +  fingerprint: 42df2bc21212094a1b61e3db697ab1c61260cb59
    +  sha1: 5f8106a0de854291aecfbb157a55e5ddf71bfe32
    +  dependencies: []
    +- name: uaa_rotator
    +  version: ea93fb4e077cc3886a5dbfb402e6480f94321af1
    +  fingerprint: ea93fb4e077cc3886a5dbfb402e6480f94321af1
    +  sha1: e04ec7a1daf684a4e6f2457803011eb90193312a
    +  dependencies:
    +  - golang-1.10-linux
    +- name: uaa_utils
    +  version: 90097ea98715a560867052a2ff0916ec3460aabb
    +  fingerprint: 90097ea98715a560867052a2ff0916ec3460aabb
    +  sha1: 9b7d3235eede336c7aa305ffb5847d44c6800a2d
    +  dependencies: []
    +license:
    +  version: 43aff6a01f4ba508f9cb013dcec9c612287e6c57
    +  fingerprint: 43aff6a01f4ba508f9cb013dcec9c612287e6c57
    +  sha1: 18461c918f686ee6cf8a3f777a2bbc2837e5fa96
    
206e2a3a8a08

Bump release version to 4.23.0

https://github.com/cloudfoundry/uaaUAA Identity BotOct 29, 2018via osv
1 file changed · +1 1
  • gradle.properties+1 1 modified
    @@ -1,4 +1,4 @@
    -version=4.23.0-SNAPSHOT
    +version=4.23.0
     
     # Required for LdapMockMvcTests when asserting it can find a user in a different language
     org.gradle.jvmargs=-Dfile.encoding=utf8 -XX:+StartAttachListener
    
95b7d9e7fae5

Validate authz parameters against original authz request

https://github.com/cloudfoundry/uaaDennis LeonOct 24, 2018via ghsa
3 files changed · +76 18
  • server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthorizationEndpoint.java+15 10 modified
    @@ -29,16 +29,7 @@
     import org.springframework.security.core.AuthenticationException;
     import org.springframework.security.core.GrantedAuthority;
     import org.springframework.security.oauth2.common.OAuth2AccessToken;
    -import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException;
    -import org.springframework.security.oauth2.common.exceptions.ClientAuthenticationException;
    -import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
    -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
    -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
    -import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException;
    -import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
    -import org.springframework.security.oauth2.common.exceptions.UnauthorizedClientException;
    -import org.springframework.security.oauth2.common.exceptions.UnsupportedResponseTypeException;
    -import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
    +import org.springframework.security.oauth2.common.exceptions.*;
     import org.springframework.security.oauth2.common.util.OAuth2Utils;
     import org.springframework.security.oauth2.provider.AuthorizationRequest;
     import org.springframework.security.oauth2.provider.ClientDetails;
    @@ -95,6 +86,7 @@
     import static org.cloudfoundry.identity.uaa.util.JsonUtils.hasText;
     import static org.cloudfoundry.identity.uaa.util.UaaUrlUtils.addFragmentComponent;
     import static org.cloudfoundry.identity.uaa.util.UaaUrlUtils.addQueryParameter;
    +import static org.springframework.security.oauth2.common.util.OAuth2Utils.SCOPE_PREFIX;
     
     /**
      * Authorization endpoint that returns id_token's if requested.
    @@ -369,6 +361,19 @@ public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
                 throw new InvalidRequestException("Changes were detected from the original authorization request.");
             }
     
    +        for (String approvalParameter : approvalParameters.keySet()) {
    +            if (approvalParameter.startsWith(SCOPE_PREFIX)) {
    +                String scope = approvalParameters.get(approvalParameter).substring(SCOPE_PREFIX.length());
    +                Set<String> originalScopes = (Set<String>) originalAuthorizationRequest.get("scope");
    +                if (!originalScopes.contains(scope)) {
    +                    sessionStatus.setComplete();
    +
    +                    return new RedirectView(getUnsuccessfulRedirect(authorizationRequest,
    +                            new InvalidScopeException("The requested scopes are invalid. Please use valid scope names in the request."), false), false, true, false);
    +                }
    +            }
    +        }
    +
             try {
                 Set<String> responseTypes = authorizationRequest.getResponseTypes();
                 String grantType = deriveGrantTypeFromResponseType(responseTypes);
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthorizationEndpointTest.java+23 0 modified
    @@ -19,12 +19,15 @@
     import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
     import org.springframework.web.bind.support.SimpleSessionStatus;
     import org.springframework.web.servlet.View;
    +import org.springframework.web.servlet.view.RedirectView;
     
     import java.util.*;
     
     import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.GRANT_TYPE_AUTHORIZATION_CODE;
     import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.GRANT_TYPE_IMPLICIT;
    +import static org.hamcrest.CoreMatchers.not;
     import static org.hamcrest.Matchers.containsString;
    +import static org.hamcrest.Matchers.instanceOf;
     import static org.hamcrest.Matchers.notNullValue;
     import static org.junit.Assert.assertEquals;
     import static org.junit.Assert.assertThat;
    @@ -174,6 +177,8 @@ public void approveUnmodifiedRequest() {
     
             View view = uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
             assertThat(view, notNullValue());
    +        assertThat(view, instanceOf(RedirectView.class));
    +        assertThat(((RedirectView)view).getUrl(), not(containsString("error=invalid_scope")));
         }
     
         @Test(expected = InvalidRequestException.class)
    @@ -282,6 +287,24 @@ public void testApproveWithModifiedAuthorities() {
             uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
         }
     
    +    @Test
    +    public void testApproveWithModifiedApprovalParameters() {
    +        AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +                "foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +        authorizationRequest.setApproved(false);
    +        model.put("authorizationRequest", authorizationRequest);
    +        model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", uaaAuthorizationEndpoint.unmodifiableMap(authorizationRequest));
    +
    +        Map<String, String> approvalParameters = new HashMap<>();
    +        approvalParameters.put("user_oauth_approval", "true");
    +        approvalParameters.put("scope.0", "foobar");
    +
    +        View view = uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +
    +        assertThat(view, instanceOf(RedirectView.class));
    +        assertThat(((RedirectView)view).getUrl(), containsString("error=invalid_scope"));
    +    }
    +
         private AuthorizationRequest getAuthorizationRequest(String clientId, String redirectUri, String state,
                                                              String scope, Set<String> responseTypes) {
             HashMap<String, String> parameters = new HashMap<>();
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/approvals/ApprovalsMockMvcTests.java+38 8 modified
    @@ -49,10 +49,7 @@
     import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
     import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
     import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
    -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
    -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
    -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;
    -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
     
     public class ApprovalsMockMvcTests extends AbstractTokenMockMvcTests {
     
    @@ -122,7 +119,7 @@ public void test_oauth_authorize_without_csrf() throws Exception {
                 post("/oauth/authorize")
                     .session(session)
                     .param(USER_OAUTH_APPROVAL, "true")
    -                .param("scope.0","test.scope1")
    +                .param("scope.0","scope.test.scope1")
             )
                 .andExpect(status().is4xxClientError());
     
    @@ -132,7 +129,7 @@ public void test_oauth_authorize_without_csrf() throws Exception {
                     .with(cookieCsrf().useInvalidToken())
                     .session(session)
                     .param(USER_OAUTH_APPROVAL, "true")
    -                .param("scope.0","test.scope1")
    +                .param("scope.0","scope.test.scope1")
             )
                 .andExpect(status().is4xxClientError());
     
    @@ -145,8 +142,8 @@ public void test_oauth_authorize_without_csrf() throws Exception {
                     .with(cookieCsrf())
                     .session(session)
                     .param(USER_OAUTH_APPROVAL, "true")
    -                .param("scope.0","test.scope1")
    -                .param("scope.1","test.scope2")
    +                .param("scope.0","scope.test.scope1")
    +                .param("scope.1","scope.test.scope2")
             )
                 .andExpect(status().isFound())
                 .andExpect(redirectedUrlPattern("**/*code=*"));
    @@ -163,6 +160,39 @@ public void test_oauth_authorize_without_csrf() throws Exception {
                 .andExpect(status().isFound()); //approval page no longer showing up
         }
     
    +    @Test
    +    public void test_oauth_authorize_modified_scope() throws Exception {
    +        String state = generator.generate();
    +
    +        MockHttpSession session = getAuthenticatedSession(user1);
    +        getMockMvc().perform(
    +            get("/oauth/authorize")
    +                .session(session)
    +                .param(RESPONSE_TYPE, "code")
    +                .param(STATE, state)
    +                .param(CLIENT_ID, client1.getClientId()))
    +            .andExpect(status().isOk()); //200 means the approvals page
    +
    +
    +        assertNotNull(session.getAttribute("authorizationRequest"));
    +        assertNotNull(session.getAttribute("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST"));
    +
    +        getMockMvc().perform(
    +            post("/oauth/authorize")
    +                .with(cookieCsrf())
    +                .session(session)
    +                .param(USER_OAUTH_APPROVAL, "true")
    +                .param("scope.0","scope.different.scope")
    +                .param("scope.1","scope.test.scope2")
    +        )
    +        .andDo(print())
    +        .andExpect(status().is3xxRedirection())
    +        .andExpect(redirectedUrlPattern("http://test.example.org/redirect?error=invalid_scope&error_description=The%20requested%20scopes%20are%20invalid.%20Please%20use%20valid%20scope%20names%20in%20the%20request*"));
    +
    +        assertNull(session.getAttribute("authorizationRequest"));
    +        assertNull(session.getAttribute("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST"));
    +    }
    +
         @Test
         public void test_get_approvals() throws Exception {
             test_oauth_authorize_without_csrf();
    
3f0730a015d1

Validate authorization request on approval

https://github.com/cloudfoundry/uaaDennis LeonOct 24, 2018via ghsa
4 files changed · +281 14
  • server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthorizationEndpoint.java+85 8 modified
    @@ -27,6 +27,7 @@
     import org.springframework.security.authentication.InsufficientAuthenticationException;
     import org.springframework.security.core.Authentication;
     import org.springframework.security.core.AuthenticationException;
    +import org.springframework.security.core.GrantedAuthority;
     import org.springframework.security.oauth2.common.OAuth2AccessToken;
     import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException;
     import org.springframework.security.oauth2.common.exceptions.ClientAuthenticationException;
    @@ -58,6 +59,7 @@
     import org.springframework.security.web.AuthenticationEntryPoint;
     import org.springframework.security.web.util.UrlUtils;
     import org.springframework.stereotype.Controller;
    +import org.springframework.util.ObjectUtils;
     import org.springframework.util.StringUtils;
     import org.springframework.web.HttpSessionRequiredException;
     import org.springframework.web.bind.annotation.ExceptionHandler;
    @@ -82,13 +84,7 @@
     import java.io.UnsupportedEncodingException;
     import java.net.URI;
     import java.security.Principal;
    -import java.util.Arrays;
    -import java.util.Collections;
    -import java.util.Date;
    -import java.util.HashMap;
    -import java.util.List;
    -import java.util.Map;
    -import java.util.Set;
    +import java.util.*;
     
     import static java.util.Arrays.stream;
     import static java.util.Collections.EMPTY_SET;
    @@ -108,7 +104,7 @@
      * https://github.com/fhanik/spring-security-oauth/compare/feature/extendable-redirect-generator?expand=1
      */
     @Controller
    -@SessionAttributes("authorizationRequest")
    +@SessionAttributes({"authorizationRequest", "org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST"})
     public class UaaAuthorizationEndpoint extends AbstractEndpoint implements AuthenticationEntryPoint {
     
         private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
    @@ -233,6 +229,8 @@ public ModelAndView authorize(Map<String, Object> model,
                     // so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
                     model.put("authorizationRequest", authorizationRequest);
                     model.put("original_uri", UrlUtils.buildFullRequestUrl(request));
    +                model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", unmodifiableMap(authorizationRequest));
    +
                     return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
                 }
             } catch (RedirectMismatchException e) {
    @@ -317,6 +315,36 @@ private ModelAndView switchIdp(Map<String, Object> model, ClientDetails client,
             return new ModelAndView("switch_idp", model, HttpStatus.UNAUTHORIZED);
         }
     
    +    Map<String, Object> unmodifiableMap(AuthorizationRequest authorizationRequest) {
    +        Map<String, Object> authorizationRequestMap = new HashMap<>();
    +
    +        authorizationRequestMap.put(OAuth2Utils.CLIENT_ID, authorizationRequest.getClientId());
    +        authorizationRequestMap.put(OAuth2Utils.STATE, authorizationRequest.getState());
    +        authorizationRequestMap.put(OAuth2Utils.REDIRECT_URI, authorizationRequest.getRedirectUri());
    +
    +        if (authorizationRequest.getResponseTypes() != null) {
    +            authorizationRequestMap.put(OAuth2Utils.RESPONSE_TYPE,
    +                    Collections.unmodifiableSet(new HashSet<>(authorizationRequest.getResponseTypes())));
    +        }
    +        if (authorizationRequest.getScope() != null) {
    +            authorizationRequestMap.put(OAuth2Utils.SCOPE,
    +                    Collections.unmodifiableSet(new HashSet<>(authorizationRequest.getScope())));
    +        }
    +
    +        authorizationRequestMap.put("approved", authorizationRequest.isApproved());
    +
    +        if (authorizationRequest.getResourceIds() != null) {
    +            authorizationRequestMap.put("resourceIds",
    +                    Collections.unmodifiableSet(new HashSet<>(authorizationRequest.getResourceIds())));
    +        }
    +        if (authorizationRequest.getAuthorities() != null) {
    +            authorizationRequestMap.put("authorities",
    +                    Collections.unmodifiableSet(new HashSet<GrantedAuthority>(authorizationRequest.getAuthorities())));
    +        }
    +
    +        return authorizationRequestMap;
    +    }
    +
         @RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
         public View approveOrDeny(@RequestParam Map<String, String> approvalParameters, Map<String, ?> model,
                                   SessionStatus sessionStatus, Principal principal) {
    @@ -334,6 +362,13 @@ public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
                 throw new InvalidRequestException("Cannot approve uninitialized authorization request.");
             }
     
    +        // Check to ensure the Authorization Request was not modified during the user approval step
    +        @SuppressWarnings("unchecked")
    +        Map<String, Object> originalAuthorizationRequest = (Map<String, Object>) model.get("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST");
    +        if (isAuthorizationRequestModified(authorizationRequest, originalAuthorizationRequest)) {
    +            throw new InvalidRequestException("Changes were detected from the original authorization request.");
    +        }
    +
             try {
                 Set<String> responseTypes = authorizationRequest.getResponseTypes();
                 String grantType = deriveGrantTypeFromResponseType(responseTypes);
    @@ -370,6 +405,48 @@ public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
     
         }
     
    +    private boolean isAuthorizationRequestModified(AuthorizationRequest authorizationRequest, Map<String, Object> originalAuthorizationRequest) {
    +        if (!ObjectUtils.nullSafeEquals(
    +                authorizationRequest.getClientId(),
    +                originalAuthorizationRequest.get(OAuth2Utils.CLIENT_ID))) {
    +            return true;
    +        }
    +        if (!ObjectUtils.nullSafeEquals(
    +                authorizationRequest.getState(),
    +                originalAuthorizationRequest.get(OAuth2Utils.STATE))) {
    +            return true;
    +        }
    +        if (!ObjectUtils.nullSafeEquals(
    +                authorizationRequest.getRedirectUri(),
    +                originalAuthorizationRequest.get(OAuth2Utils.REDIRECT_URI))) {
    +            return true;
    +        }
    +        if (!ObjectUtils.nullSafeEquals(
    +                authorizationRequest.getResponseTypes(),
    +                originalAuthorizationRequest.get(OAuth2Utils.RESPONSE_TYPE))) {
    +            return true;
    +        }
    +        if (!ObjectUtils.nullSafeEquals(
    +                authorizationRequest.isApproved(),
    +                originalAuthorizationRequest.get("approved"))) {
    +            return true;
    +        }
    +        if (!ObjectUtils.nullSafeEquals(
    +                authorizationRequest.getResourceIds(),
    +                originalAuthorizationRequest.get("resourceIds"))) {
    +            return true;
    +        }
    +        if (!ObjectUtils.nullSafeEquals(
    +                authorizationRequest.getAuthorities(),
    +                originalAuthorizationRequest.get("authorities"))) {
    +            return true;
    +        }
    +
    +        return !ObjectUtils.nullSafeEquals(
    +                authorizationRequest.getScope(),
    +                originalAuthorizationRequest.get(OAuth2Utils.SCOPE));
    +    }
    +
         protected String deriveGrantTypeFromResponseType(Set<String> responseTypes) {
             if (responseTypes.contains("token")) {
                 return GRANT_TYPE_IMPLICIT;
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthorizationEndpointTest.java+160 6 modified
    @@ -6,21 +6,26 @@
     import org.cloudfoundry.identity.uaa.oauth.token.CompositeToken;
     import org.junit.Before;
     import org.junit.Test;
    +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
     import org.springframework.security.core.Authentication;
    +import org.springframework.security.core.authority.AuthorityUtils;
    +import org.springframework.security.core.authority.SimpleGrantedAuthority;
    +import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
    +import org.springframework.security.oauth2.common.util.OAuth2Utils;
     import org.springframework.security.oauth2.provider.AuthorizationRequest;
    +import org.springframework.security.oauth2.provider.OAuth2Authentication;
     import org.springframework.security.oauth2.provider.OAuth2Request;
     import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
     import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
    +import org.springframework.web.bind.support.SimpleSessionStatus;
    +import org.springframework.web.servlet.View;
     
    -import java.util.Calendar;
    -import java.util.Collections;
    -import java.util.HashMap;
    -import java.util.HashSet;
    -import java.util.Set;
    +import java.util.*;
     
     import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.GRANT_TYPE_AUTHORIZATION_CODE;
     import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.GRANT_TYPE_IMPLICIT;
     import static org.hamcrest.Matchers.containsString;
    +import static org.hamcrest.Matchers.notNullValue;
     import static org.junit.Assert.assertEquals;
     import static org.junit.Assert.assertThat;
     import static org.mockito.ArgumentMatchers.any;
    @@ -35,6 +40,10 @@ public class UaaAuthorizationEndpointTest {
         private Set<String> responseTypes;
         private OpenIdSessionStateCalculator openIdSessionStateCalculator;
     
    +    private HashMap<String, Object> model = new HashMap<>();
    +    private SimpleSessionStatus sessionStatus = new SimpleSessionStatus();
    +    private UsernamePasswordAuthenticationToken principal = new UsernamePasswordAuthenticationToken("foo", "bar", Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")));
    +
         @Before
         public void setup() {
             oAuth2RequestFactory = mock(OAuth2RequestFactory.class);
    @@ -47,6 +56,7 @@ public void setup() {
             responseTypes = new HashSet<>();
     
             when(openIdSessionStateCalculator.calculate("userid", null, "http://example.com")).thenReturn("opbshash");
    +        when(authorizationCodeServices.createAuthorizationCode(any(OAuth2Authentication.class))).thenReturn("code");
         }
     
     
    @@ -150,4 +160,148 @@ public void buildRedirectURI_includesSessionStateForPromptEqualsNone() {
     
             assertThat(result, containsString("session_state=opbshash"));
         }
    -}
    \ No newline at end of file
    +
    +    @Test
    +    public void approveUnmodifiedRequest() {
    +        AuthorizationRequest authorizationRequest = getAuthorizationRequest("foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +        model.put("authorizationRequest", authorizationRequest);
    +        model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", uaaAuthorizationEndpoint.unmodifiableMap(authorizationRequest));
    +
    +        Map<String, String> approvalParameters = new HashMap<>();
    +        approvalParameters.put("user_oauth_approval", "true");
    +
    +        when(authorizationCodeServices.createAuthorizationCode(any(OAuth2Authentication.class))).thenReturn("code");
    +
    +        View view = uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +        assertThat(view, notNullValue());
    +    }
    +
    +    @Test(expected = InvalidRequestException.class)
    +    public void testApproveWithModifiedScope() {
    +        AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +                "foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +        model.put("authorizationRequest", authorizationRequest);
    +        model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", uaaAuthorizationEndpoint.unmodifiableMap(authorizationRequest));
    +
    +        authorizationRequest.setScope(Arrays.asList("read", "write"));		// Modify authorization request
    +        Map<String, String> approvalParameters = new HashMap<>();
    +        approvalParameters.put("user_oauth_approval", "true");
    +
    +        uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +    }
    +
    +    @Test(expected = InvalidRequestException.class)
    +    public void testApproveWithModifiedClientId() {
    +        AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +                "foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +        model.put("authorizationRequest", authorizationRequest);
    +        model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", uaaAuthorizationEndpoint.unmodifiableMap(authorizationRequest));
    +        authorizationRequest.setClientId("bar");		// Modify authorization request
    +        Map<String, String> approvalParameters = new HashMap<>();
    +        approvalParameters.put("user_oauth_approval", "true");
    +
    +        uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +    }
    +
    +    @Test(expected = InvalidRequestException.class)
    +    public void testApproveWithModifiedState() {
    +        AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +                "foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +        model.put("authorizationRequest", authorizationRequest);
    +        model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", uaaAuthorizationEndpoint.unmodifiableMap(authorizationRequest));
    +        authorizationRequest.setState("state-5678");		// Modify authorization request
    +        Map<String, String> approvalParameters = new HashMap<>();
    +        approvalParameters.put("user_oauth_approval", "true");
    +
    +        uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +    }
    +
    +    @Test(expected = InvalidRequestException.class)
    +    public void testApproveWithModifiedRedirectUri() {
    +        AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +                "foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +        model.put("authorizationRequest", authorizationRequest);
    +        model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", uaaAuthorizationEndpoint.unmodifiableMap(authorizationRequest));
    +        authorizationRequest.setRedirectUri("http://somewhere.com");		// Modify authorization request
    +        Map<String, String> approvalParameters = new HashMap<>();
    +        approvalParameters.put("user_oauth_approval", "true");
    +
    +        uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +    }
    +
    +    @Test(expected = InvalidRequestException.class)
    +    public void testApproveWithModifiedResponseTypes() {
    +        AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +                "foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +        model.put("authorizationRequest", authorizationRequest);
    +        model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", uaaAuthorizationEndpoint.unmodifiableMap(authorizationRequest));
    +        authorizationRequest.setResponseTypes(Collections.singleton("implicit"));		// Modify authorization request
    +        Map<String, String> approvalParameters = new HashMap<>();
    +        approvalParameters.put("user_oauth_approval", "true");
    +
    +        uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +    }
    +
    +    @Test(expected = InvalidRequestException.class)
    +    public void testApproveWithModifiedApproved() {
    +        AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +                "foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +        authorizationRequest.setApproved(false);
    +        model.put("authorizationRequest", authorizationRequest);
    +        model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", uaaAuthorizationEndpoint.unmodifiableMap(authorizationRequest));
    +        authorizationRequest.setApproved(true);		// Modify authorization request
    +        Map<String, String> approvalParameters = new HashMap<>();
    +        approvalParameters.put("user_oauth_approval", "true");
    +
    +        uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +    }
    +
    +    @Test(expected = InvalidRequestException.class)
    +    public void testApproveWithModifiedResourceIds() {
    +        AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +                "foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +        model.put("authorizationRequest", authorizationRequest);
    +        model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", uaaAuthorizationEndpoint.unmodifiableMap(authorizationRequest));
    +        authorizationRequest.setResourceIds(Collections.singleton("resource-other"));		// Modify authorization request
    +        Map<String, String> approvalParameters = new HashMap<>();
    +        approvalParameters.put("user_oauth_approval", "true");
    +
    +        uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +    }
    +
    +    @Test(expected = InvalidRequestException.class)
    +    public void testApproveWithModifiedAuthorities() {
    +        AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +                "foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +        model.put("authorizationRequest", authorizationRequest);
    +        model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", uaaAuthorizationEndpoint.unmodifiableMap(authorizationRequest));
    +        authorizationRequest.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("authority-other"));		// Modify authorization request
    +        Map<String, String> approvalParameters = new HashMap<>();
    +        approvalParameters.put("user_oauth_approval", "true");
    +
    +        uaaAuthorizationEndpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +    }
    +
    +    private AuthorizationRequest getAuthorizationRequest(String clientId, String redirectUri, String state,
    +                                                         String scope, Set<String> responseTypes) {
    +        HashMap<String, String> parameters = new HashMap<>();
    +        parameters.put(OAuth2Utils.CLIENT_ID, clientId);
    +        if (redirectUri != null) {
    +            parameters.put(OAuth2Utils.REDIRECT_URI, redirectUri);
    +        }
    +        if (state != null) {
    +            parameters.put(OAuth2Utils.STATE, state);
    +        }
    +        if (scope != null) {
    +            parameters.put(OAuth2Utils.SCOPE, scope);
    +        }
    +        if (responseTypes != null) {
    +            parameters.put(OAuth2Utils.RESPONSE_TYPE, OAuth2Utils.formatParameterList(responseTypes));
    +        }
    +        return new AuthorizationRequest(parameters, Collections.emptyMap(),
    +                parameters.get(OAuth2Utils.CLIENT_ID),
    +                OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)), null, null, false,
    +                parameters.get(OAuth2Utils.STATE), parameters.get(OAuth2Utils.REDIRECT_URI),
    +                OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.RESPONSE_TYPE)));
    +    }
    +}
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/approvals/ApprovalsMockMvcTests.java+3 0 modified
    @@ -115,6 +115,7 @@ public void test_oauth_authorize_without_csrf() throws Exception {
     
     
             assertNotNull(session.getAttribute("authorizationRequest"));
    +        assertNotNull(session.getAttribute("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST"));
     
             //no token
             getMockMvc().perform(
    @@ -136,6 +137,7 @@ public void test_oauth_authorize_without_csrf() throws Exception {
                 .andExpect(status().is4xxClientError());
     
             assertNotNull(session.getAttribute("authorizationRequest"));
    +        assertNotNull(session.getAttribute("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST"));
     
             //valid token
             getMockMvc().perform(
    @@ -150,6 +152,7 @@ public void test_oauth_authorize_without_csrf() throws Exception {
                 .andExpect(redirectedUrlPattern("**/*code=*"));
     
             assertNull(session.getAttribute("authorizationRequest"));
    +        assertNull(session.getAttribute("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST"));
     
             getMockMvc().perform(
                 get("/oauth/authorize")
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java+33 0 modified
    @@ -40,6 +40,7 @@
     import org.springframework.mock.web.MockHttpServletResponse;
     import org.springframework.mock.web.MockHttpSession;
     import org.springframework.security.core.Authentication;
    +import org.springframework.security.core.GrantedAuthority;
     import org.springframework.security.core.context.SecurityContext;
     import org.springframework.security.core.context.SecurityContextHolder;
     import org.springframework.security.crypto.codec.Base64;
    @@ -1534,6 +1535,7 @@ public void testOpenIdTokenHybridFlowWithNoImplicitGrantWhenLenientWhenAppNotApp
             authorizationRequest.setState(state);
     
             session.setAttribute("authorizationRequest", authorizationRequest);
    +        session.setAttribute("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", unmodifiableMap(authorizationRequest));
     
             MvcResult result = getMockMvc().perform(
                     post("/oauth/authorize")
    @@ -1570,6 +1572,7 @@ public void testOpenIdTokenHybridFlowWithNoImplicitGrantWhenStrictWhenAppNotAppr
             authorizationRequest.setResponseTypes(new TreeSet<>(Arrays.asList("code", "id_token")));
             authorizationRequest.setState(state);
             session.setAttribute("authorizationRequest", authorizationRequest);
    +        session.setAttribute("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", unmodifiableMap(authorizationRequest));
     
             MvcResult result = getMockMvc().perform(
                     post("/oauth/authorize")
    @@ -4261,4 +4264,34 @@ public void setAuthentication(Authentication authentication) {
                 this.authentication = authentication;
             }
         }
    +
    +    Map<String, Object> unmodifiableMap(AuthorizationRequest authorizationRequest) {
    +        Map<String, Object> authorizationRequestMap = new HashMap<>();
    +
    +        authorizationRequestMap.put(OAuth2Utils.CLIENT_ID, authorizationRequest.getClientId());
    +        authorizationRequestMap.put(OAuth2Utils.STATE, authorizationRequest.getState());
    +        authorizationRequestMap.put(OAuth2Utils.REDIRECT_URI, authorizationRequest.getRedirectUri());
    +
    +        if (authorizationRequest.getResponseTypes() != null) {
    +            authorizationRequestMap.put(OAuth2Utils.RESPONSE_TYPE,
    +                    Collections.unmodifiableSet(new HashSet<>(authorizationRequest.getResponseTypes())));
    +        }
    +        if (authorizationRequest.getScope() != null) {
    +            authorizationRequestMap.put(OAuth2Utils.SCOPE,
    +                    Collections.unmodifiableSet(new HashSet<>(authorizationRequest.getScope())));
    +        }
    +
    +        authorizationRequestMap.put("approved", authorizationRequest.isApproved());
    +
    +        if (authorizationRequest.getResourceIds() != null) {
    +            authorizationRequestMap.put("resourceIds",
    +                    Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getResourceIds())));
    +        }
    +        if (authorizationRequest.getAuthorities() != null) {
    +            authorizationRequestMap.put("authorities",
    +                    Collections.unmodifiableSet(new HashSet<GrantedAuthority>(authorizationRequest.getAuthorities())));
    +        }
    +
    +        return authorizationRequestMap;
    +    }
     }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.