CVE-2021-21411 (published 2021-03-26T21:15Z)

OAuth2-Proxy is an open source reverse proxy that provides authentication with Google, Github or other providers. The `--gitlab-group` flag for group-based authorization in the GitLab provider stopped working in the v7.0.0 release. Regardless of the flag settings, authorization wasn't restricted. Additionally, any authenticated users had whichever groups were set in `--gitlab-group` added to the new `X-Forwarded-Groups` header to the upstream application. While adding GitLab project based authorization support in #630, a bug was introduced where the user session's groups field was populated with the `--gitlab-group` config entries instead of pulling the individual user's group membership from the GitLab Userinfo endpoint. When the session groups where compared against the allowed groups for authorization, they matched improperly (since both lists were populated with the same data) so authorization was allowed. This impacts GitLab Provider users who relies on group membership for authorization restrictions. Any authenticated users in your GitLab environment can access your applications regardless of `--gitlab-group` membership restrictions. This is patched in v7.1.0. There is no workaround for the Group membership bug. But `--gitlab-project` can be set to use Project membership as the authorization checks instead of groups; it is not broken.

Severity: MEDIUM
CVSS v2 base score: 5.5 (AV:N/AC:L/Au:S/C:P/I:P/A:N)
CVSS v3 base score: 5.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:N)

Weakness information:
cwe_id: CWE-863
cwe_name: Incorrect Authorization
description: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
extended_description: 
            Assuming a user with a given identity, authorization is the process of determining whether that user can access a given resource, based on the user's privileges and any permissions or other access-control specifications that apply to the resource.
            When access control checks are incorrectly applied, users are able to access data or perform actions that they should not be allowed to perform. This can lead to a wide range of problems, including information exposures, denial of service, and arbitrary code execution.
         
         

Fix information:
repo_url: https://github.com/oauth2-proxy/oauth2-proxy
commit_details: repo_url: https://github.com/oauth2-proxy/oauth2-proxy
author: Nick Meves
author_date: 2021-03-25 10:20:45-07:00
msg: Merge pull request from GHSA-652x-m2gr-hppm

* Populate session Groups from userinfo response

* Fix: gitlab tests

Co-authored-by: Wilfried OLLIVIER <wollivier@bearstech.com>
repository: repo_url: https://github.com/oauth2-proxy/oauth2-proxy
repo_name: oauth2-proxy/oauth2-proxy
description: A reverse proxy that provides authentication with Google, Azure, OpenID Connect and many more identity providers.
repo_language: Go
owner: oauth2-proxy
file_changes: filename: gitlab.go
change_type: ModificationType.MODIFY
diff: @@ -295,21 +295,13 @@ func (p *GitLabProvider) EnrichSession(ctx context.Context, s *sessions.SessionS
 
 	s.User = userInfo.Username
 	s.Email = userInfo.Email
-
-	p.addGroupsToSession(ctx, s)
+	for _, group := range userInfo.Groups {
+		s.Groups = append(s.Groups, fmt.Sprintf("group:%s", group))
+	}
 
 	p.addProjectsToSession(ctx, s)
 
 	return nil
-
-}
-
-// addGroupsToSession projects into session.Groups
-func (p *GitLabProvider) addGroupsToSession(ctx context.Context, s *sessions.SessionState) {
-	// Iterate over projects, check if oauth2-proxy can get project information on behalf of the user
-	for _, group := range p.Groups {
-		s.Groups = append(s.Groups, fmt.Sprintf("group:%s", group))
-	}
 }
 
 // addProjectsToSession adds projects matching user access requirements into the session state groups list
@@ -341,24 +333,20 @@ func (p *GitLabProvider) addProjectsToSession(ctx context.Context, s *sessions.S
 			} else {
 				logger.Errorf("Warning: user %q does not have the minimum required access level for project %q", s.Email, project.Name)
 			}
-		} else {
-			logger.Errorf("Warning: project %s is archived", project.Name)
+			continue
 		}
 
+		logger.Errorf("Warning: project %s is archived", project.Name)
 	}
-
 }
 
 // PrefixAllowedGroups returns a list of allowed groups, prefixed by their `kind` value
 func (p *GitLabProvider) PrefixAllowedGroups() (groups []string) {
-
 	for _, val := range p.Groups {
 		groups = append(groups, fmt.Sprintf("group:%s", val))
 	}
-
 	for _, val := range p.Projects {
 		groups = append(groups, fmt.Sprintf("project:%s", val.Name))
 	}
-
 	return groups
 }

programming_language: Go
filename: gitlab_test.go
change_type: ModificationType.MODIFY
diff: @@ -232,11 +232,11 @@ var _ = Describe("Gitlab Provider Tests", func() {
 				Expect(session.Groups).To(Equal(in.expectedValue))
 			},
 			Entry("project membership valid on group project", entitiesTableInput{
-				expectedValue: []string{"project:my_group/my_project"},
+				expectedValue: []string{"group:foo", "group:bar", "project:my_group/my_project"},
 				projects:      []string{"my_group/my_project"},
 			}),
 			Entry("project membership invalid on group project, insufficient access level level", entitiesTableInput{
-				expectedValue: nil,
+				expectedValue: []string{"group:foo", "group:bar"},
 				projects:      []string{"my_group/my_project=40"},
 			}),
 			Entry("project membership invalid on group project, no access at all", entitiesTableInput{
@@ -244,28 +244,28 @@ var _ = Describe("Gitlab Provider Tests", func() {
 				projects:      []string{"no_access_group/no_access_project=30"},
 			}),
 			Entry("project membership valid on personnal project", entitiesTableInput{
-				expectedValue: []string{"project:my_profile/my_personal_project"},
+				expectedValue: []string{"group:foo", "group:bar", "project:my_profile/my_personal_project"},
 				projects:      []string{"my_profile/my_personal_project"},
 			}),
 			Entry("project membership invalid on personnal project, insufficient access level", entitiesTableInput{
-				expectedValue: nil,
+				expectedValue: []string{"group:foo", "group:bar"},
 				projects:      []string{"my_profile/my_personal_project=40"},
 			}),
 			Entry("project membership invalid", entitiesTableInput{
-				expectedValue: nil,
+				expectedValue: []string{"group:foo", "group:bar"},
 				projects:      []string{"my_group/my_bad_project"},
 			}),
 			Entry("group membership valid", entitiesTableInput{
-				expectedValue: []string{"group:foo"},
+				expectedValue: []string{"group:foo", "group:bar"},
 				groups:        []string{"foo"},
 			}),
 			Entry("groups and projects", entitiesTableInput{
-				expectedValue: []string{"group:foo", "group:baz", "project:my_group/my_project", "project:my_profile/my_personal_project"},
+				expectedValue: []string{"group:foo", "group:bar", "project:my_group/my_project", "project:my_profile/my_personal_project"},
 				groups:        []string{"foo", "baz"},
 				projects:      []string{"my_group/my_project", "my_profile/my_personal_project"},
 			}),
 			Entry("archived projects", entitiesTableInput{
-				expectedValue: nil,
+				expectedValue: []string{"group:foo", "group:bar"},
 				groups:        []string{},
 				projects:      []string{"my_group/my_archived_project"},
 			}),

programming_language: Go