
Write-up and proof-of-concept for CVE-2026-94609, an authentik privilege-escalation flaw letting users with add_user_to_group join superuser groups via the Groups API.
Write-up and proof-of-concept for a privilege-escalation vulnerability in goauthentik/authentik, fixed in 2026.2.7 / 2026.5.7 / 2026.8.2 and assigned CVE-2026-94609 (GHSA-h6c5-mpvq-j4jc).
I was one of the credited reporters on this advisory (
@anthonyk2923— credit accepted). This repo documents the specific code path I independently found and reported, plus a PoC. The published advisory covers the full, broader issue (group hierarchy + role assignment), which was reported by multiple independent researchers and fixed together.
An account with only a routine, commonly-delegated permission —
authentik_core.add_user_to_group (add members to a group) — could add
itself or any other user directly into an existing group flagged
is_superuser=True via the standard Groups API, and that user immediately
became a real authentik superuser. This happened without ever holding
authentik_core.enable_group_superuser, the permission specifically
designed to gate this exact action.
authentik had already fixed the mirror-image version of this bug on the User side (assigning superuser groups to a user). The equivalent check was missing on the Group side (assigning users to a superuser group).
authentik models two intentionally separate RBAC permissions for group membership management:
authentik_core.add_user_to_group — meant for routine, delegated
membership management (e.g. a "helpdesk" or "team lead" role that manages
everyday team rosters).authentik_core.enable_group_superuser — meant to gate the far more
sensitive act of granting or joining a group whose is_superuser=True
flag makes every member a full authentik superuser.The separation exists precisely so an org can delegate ordinary group management widely, without also handing out the ability to mint superusers.
authentik had already fixed this exact confusion once, on the User side
of the relationship (PATCH /api/v3/core/users/{pk}/ via
UserSerializer.validate_groups()), tracked as
GHSA-h6x7-hjjc-wjc9 / CVE-2026-40172
(CVSS 8.1, High). The fix added:
# authentik/core/api/users.py (fixed)
def validate_groups(self, groups: list) -> list:
"""Require enable_group_superuser permission when adding a user to a superuser group."""
...
for group in groups:
if not group.is_superuser:
continue
if group in current_groups:
continue
if not request.user.has_perm("authentik_core.enable_group_superuser"):
raise ValidationError(...)
The symmetric code path on the Group side — adding members to a group, rather than adding groups to a user — never received the equivalent check.
GroupSerializer.validate_users() in authentik/core/api/groups.py only
checked add_user_to_group, and never inspected self.instance.is_superuser
at all:
# authentik/core/api/groups.py (vulnerable)
def validate_users(self, users: list) -> list:
"""Require add_user_to_group permission when adding new members via group PATCH."""
request: Request = self.context.get("request", None)
if not request:
return users
if not self.instance:
return users
current_user_pks = set(self.instance.users.values_list("pk", flat=True))
new_users = [u for u in users if u not in current_user_pks]
if not new_users:
return users
has_perm = request.user.has_perm(
"authentik_core.add_user_to_group"
) or request.user.has_perm("authentik_core.add_user_to_group", self.instance)
if not has_perm:
raise ValidationError(_("User does not have permission to add members to this group."))
return users
There is no if self.instance.is_superuser: require enable_group_superuser
guard anywhere in this method. As a result:
Any caller who holds
add_user_to_group— either globally, or object-scoped on just one specific superuser group — canPATCHthat group'susersfield to add any user (including themselves), and that user becomes a real authentik superuser immediately.
add_user_to_group is exactly the kind of permission authentik's own
fine-grained RBAC model encourages delegating broadly and independently of
enable_group_superuser — e.g. to a "user manager" or "helpdesk" custom
role. The existing test
authentik/core/tests/test_groups_api.py::test_patch_users_with_global_perm
already demonstrated that a global grant of add_user_to_group (no object
scoping at all) is sufficient to PATCH members into any group in the
instance — that test simply never exercised a group with
is_superuser=True, so the gap went uncaught.
This is the same underlying flaw the published advisory describes more broadly: groups form a hierarchy and superuser status is inherited from any parent group, but the checks gating who can change group membership, group parentage, and role assignment either didn't look for superuser status at all, or only looked at the group's own flag rather than the effective (inherited) one.
Complete privilege escalation from "any account holding a routine, commonly-delegated group-membership permission" to "full authentik superuser" — which controls every tenant, every downstream application's SSO, every user account, all secrets/certificates managed by authentik, and the entire instance configuration.
CVE-2026-94609.py
A standalone, live HTTP client-style PoC. It takes a target URL and an API
token for a low-privilege account (holding only add_user_to_group), and
optionally a target group/victim, then attempts the same escalation over
the real API:
# Check your own deployment for the bug, no mutation:
python3 exploit_CVE-2026-94609.py --url https://authentik.example.com \
--token <low-priv-api-token> --group "superadmins" --check-only
# List superuser groups reachable to this token:
python3 exploit_CVE-2026-94609.py --url https://authentik.example.com \
--token <low-priv-api-token> --list-groups
# Attempt the actual escalation (self, or a named/PK victim):
python3 exploit_CVE-2026-94609.py --url https://authentik.example.com \
--token <low-priv-api-token> --group "superadmins" --victim jdoe
Requires only pip install requests. Authorized use only — run this
against instances you are explicitly authorized to test.
| Branch | Affected | Patched |
|---|---|---|
| 2026.2.x | ≤ 2026.2.6 | 2026.2.7 |
| 2026.5.x | ≤ 2026.5.6 | 2026.5.7 |
| 2026.8.x | ≤ 2026.8.1 | 2026.8.2 |
Upgrade to a patched version. If you cannot upgrade immediately, per the official advisory's workaround: restrict permissions to create/modify groups, modify users, and add users to groups so that only full administrators hold them — do not delegate any of these to non-admin roles until patched.
This write-up and PoC are provided for educational and defensive security research purposes. See authentik's own license (MIT) for the underlying project.
| Vulnerability class | Privilege Escalation / Improper Access Control |
| CWE | CWE-269 (Improper Privilege Management), CWE-863 (Incorrect Authorization) |
| CVSS 3.1 | AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — High |
| Affected | authentik ≤ 2026.2.6, ≤ 2026.5.6, ≤ 2026.8.1 |
| Fixed in | 2026.2.7, 2026.5.7, 2026.8.2 |
| Reachable route | PATCH /api/v3/core/groups/{group_uuid}/ |
| Root cause | GroupSerializer.validate_users() never checked enable_group_superuser |