CVE-2026-76081 Overview
CVE-2026-76081 affects ZITADEL, an open source identity and access management platform. The flaw lives in the permission update logic that runs when multiple project roles are deleted in a single operation. Under this condition, the cascade removal skips entries, and affected User Grants on Granted Projects retain roles that administrators intended to revoke. The bug is scoped to grant-based user grants tied to projects shared across organizations. Direct user grants are not affected. The issue is classified under [CWE-193: Off-by-one Error] and is resolved in ZITADEL version 4.16.0.
Critical Impact
Users on Granted Projects can retain access rights that were supposed to be fully removed, resulting in silent authorization drift across organization boundaries.
Affected Products
- ZITADEL versions prior to 4.16.0
- Deployments using Granted Projects (projects shared between organizations)
- Instances that have performed multi-role deletions via ChangeProjectGrant
Discovery Timeline
- 2026-09-14 - CVE-2026-76081 published to the National Vulnerability Database
- 2026-09-16 - Last updated in NVD database
Technical Details for CVE-2026-76081
Vulnerability Analysis
The defect is an off-by-one style iteration error inside ZITADEL's project grant change flow. When ChangeProjectGrant (in internal/command/project_grant.go) cascades a multi-role removal down to associated user grants, the loop that recomputes permissions misses entries. As a result, some roles that should be stripped remain persisted in the eventstore event payloads. Because the eventstore is the source of truth, the stale roles survive projection rebuilds and continue to authorize the affected users. Only grant-based user grants (rows where ug.grant_id is set) can be corrupted. Direct user grants pass through single-role removal paths and are outside the scope of this flaw.
Root Cause
The root cause is faulty handling of batched role removals during project grant updates. The code path did not correctly reconcile the surviving granted_role_keys on the project grant against the roles array stored on each dependent user grant. Instead of intersecting the two sets, the routine dropped only a subset of the removed roles, leaving residue behind.
Attack Vector
Exploitation requires an authenticated actor with high privileges, typically an administrator managing project grants. The attack vector is network-based and does not require user interaction. An administrator who removes several roles from a Granted Project in one operation can inadvertently, or intentionally, leave users authorized for those roles across organizational boundaries. Downstream services that trust ZITADEL's authorization decisions will honor the stale grants.
// Patch excerpt: cmd/setup/73.go — repair migration for corrupted user grants
package setup
import (
"context"
_ "embed"
"fmt"
"github.com/zitadel/zitadel/backend/v3/instrumentation/logging"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
"github.com/zitadel/zitadel/internal/query/projection"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/usergrant"
)
var (
//go:embed 73.sql
fixUserGrantRoles string
)
// FixUserGrantRoles repairs grant-based user grants that kept roles which
// should have been cascade-removed (GHSA-v859-c572-qh5p). Only grant-based
// user grants (ug.grant_id set) are in scope: the bug can only manifest via
// ChangeProjectGrant's multi-role cascade to those grants.
Source: GitHub Commit 9d60e83
Detection Methods for CVE-2026-76081
Indicators of Compromise
- User grants on Granted Projects whose roles array contains values not present in the parent project grant's granted_role_keys.
- Audit events showing ChangeProjectGrant with multiple role removals that were not followed by matching role revocations on all dependent user grants.
- Authorization decisions granting access to resources whose backing role was removed from the Granted Project.
Detection Strategies
- Run the detection query from the v4.16.0 migration script (cmd/setup/73.sql) against projections.user_grants5 joined to projections.project_grants4 to enumerate grants whose stored roles exceed the currently valid role set.
- Compare historical project grant change events against user grant projections to identify skipped cascade removals.
- Review administrative logs for ChangeProjectGrant calls that removed two or more roles simultaneously prior to upgrading.
Monitoring Recommendations
- Alert on discrepancies between granted_role_keys on a project grant and roles on its dependent user grants.
- Log all project grant modification API calls, including the before-and-after role sets, and forward them to a centralized SIEM for correlation.
- Track access to resources gated by roles that were removed from Granted Projects and investigate any hits.
How to Mitigate CVE-2026-76081
Immediate Actions Required
- Upgrade ZITADEL to version 4.16.0 or later to trigger the automatic cleanup migration (setup step 73).
- Enumerate all Granted Projects where multiple roles were removed recently and manually review the dependent user grants.
- Revoke or re-issue affected user grants after confirming the intended role assignments with project owners.
Patch Information
The fix ships in Zitadel Release v4.16.0. The upgrade runs migration step 73, which recomputes the valid role set for each grant-based user grant and pushes corrective events into the eventstore. Full technical background is available in the GitHub Security Advisory GHSA-v859-c572-qh5p.
Workarounds
- No configuration workaround exists; upgrading is the only supported remediation path.
- Operators unable to upgrade immediately should manually audit user permissions on Granted Projects and remove any residual roles.
- Temporarily avoid removing multiple project roles in a single ChangeProjectGrant operation; delete roles one at a time until the patch is applied.
# Example: run the repair migration by upgrading to v4.16.0
# Docker deployment upgrade
docker pull ghcr.io/zitadel/zitadel:v4.16.0
docker stop zitadel && docker rm zitadel
docker run -d --name zitadel \
-p 8080:8080 \
ghcr.io/zitadel/zitadel:v4.16.0 start-from-init --masterkey "$MASTERKEY"
# Verify the setup step 73 migration executed
docker logs zitadel 2>&1 | grep -i "setup step 73\|FixUserGrantRoles"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

