CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-76081

CVE-2026-76081: Improper Role Revocation in ZITADEL Dynamic Project Grants

Amit Schendel
Amit Schendel
Senior Security Researcher

Sep 14, 2026·6 min read·8 visits

Executive Summary (TL;DR)

A slice mutation bug in ZITADEL's Go backend causes role cascading logic to skip adjacent roles during bulk revocation, leaving users with unauthorized permissions on shared projects.

CVE-2026-76081 is a logical vulnerability in ZITADEL's role cascading logic where updating a Project Grant to drop multiple adjacent roles simultaneously fails to clean up associated User Grants due to an in-place slice mutation error in Go.

Vulnerability Overview

ZITADEL leverages a microservices-inspired structure to support multi-tenant Identity and Access Management (IAM) deployments. Within this architecture, organizations often dynamically delegate specific administrative and operational capabilities to external partner organizations via a mechanism designated as a Project Grant.

When a Project Grant is constructed, the owning organization designates a precise subset of project roles available to the receiving organization. Administrators in the receiving organization then allocate these roles to local identities using User Grants. This multi-tiered permission topology creates a complex dependency graph that relies on accurate state synchronization during administrative modifications.

CVE-2026-76081 introduces a vulnerability inside this cascade path, classified under CWE-193 (Off-by-one Error). When an upstream project owner revokes multiple roles from a Project Grant, the downstream user grants must automatically prune the revoked roles. Due to a logical slice mutation defect in the cascade path, this cleanup process silently fails, leaving active permissions assigned to unauthorized users.

Root Cause Analysis

The root cause of the vulnerability lies in the implementation of the removeRoleFromUserGrant method inside internal/command/user_grant.go. When executing cascade deletions, the application iterates over the user's assigned roles using a standard Go for i, key := range loop while simultaneously mutating the slice in-place.

In Go, slice headers contain a pointer to an underlying array, a length, and a capacity. When an element is removed from a slice in-place using copy(slice[i:], slice[i+1:]) followed by truncation, the remaining elements shift to the left, decreasing their index positions by one. The loop index i, however, increments sequentially regardless of inner slice mutations.

This behavior leads to a classic index-skipping flaw. If two adjacent elements in the user's role list are targeted for removal, deleting the first element causes the second adjacent element to shift left into the current index position i. In the next iteration, the index increments to i+1, entirely bypassing the evaluation of the shifted element. As a consequence, the skipped role remains active in the database state.

Code Analysis

The original implementation of the role removal loop used an in-place modification pattern that was highly prone to indexing anomalies. The inner and outer loops directly modified existingUserGrant.RoleKeys using slice copies while counting forward.

// Vulnerable Implementation
keyExists := false
for i, key := range existingUserGrant.RoleKeys {
    for _, roleKey := range roleKeys {
        if key == roleKey {
            keyExists = true
            // Shifting elements left
            copy(existingUserGrant.RoleKeys[i:], existingUserGrant.RoleKeys[i+1:])
            // Truncating the slice
            existingUserGrant.RoleKeys[len(existingUserGrant.RoleKeys)-1] = ""
            existingUserGrant.RoleKeys = existingUserGrant.RoleKeys[:len(existingUserGrant.RoleKeys)-1]
            continue
        } 
    }
}

To resolve this structural flaw, the patch in version 4.16.0 completely removes the custom, forward-counting loop. It introduces slices.DeleteFunc from the standard Go library, which performs in-place filtering in a single, index-safe pass. It also refactors the list of target roles into a map (roleKeysToRemove map[string]bool) to optimize lookup performance to $O(N)$ time complexity.

// Patched Implementation
beforeLen := len(existingUserGrant.RoleKeys)
 
// Safely filter the slice using standard library mechanisms
existingUserGrant.RoleKeys = slices.DeleteFunc(existingUserGrant.RoleKeys, func(role string) bool {
    return roleKeysToRemove[role]
})
 
if beforeLen == len(existingUserGrant.RoleKeys) {
    return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-5m8g9", "Errors.UserGrant.RoleKeyNotFound")
}

Exploitation Methodology

Exploiting this vulnerability does not require complex binary-level manipulation or injection vectors. The exploit is executed entirely through valid administrative state changes that trigger the logical error in the backend database.

To demonstrate the vulnerability, consider an enterprise environment where Organization A delegates a project containing three roles (admin, viewer, and editor) to Organization B. An administrator in Organization B assigns all three roles to User_Beta via a dynamic User Grant.

When the project owner in Organization A decides to revoke both admin and viewer privileges from the Project Grant, they submit an update request to ZITADEL. The backend processes the change and attempts to cascade the revocation down to User_Beta. Because the roles admin and viewer are adjacent in the underlying storage array, the in-place slice mutation skips the viewer role. User_Beta retains active access authorized under the viewer scope, violating the access control policy defined by the project owner.

Database Migration and State Reconstruction

Because this vulnerability directly corrupts the persistent state of the database, deploying the patched binaries is insufficient on its own to remediate pre-existing permission desynchronizations. To solve this, ZITADEL developers implemented an automated database migration, designated as Setup Step 73 (FixUserGrantRoles).

This migration scans the database projections using a specific SQL query to identify active User Grants that contain role configurations that are no longer supported by their parent Project Grants. It excludes direct User Grants, which do not go through the multi-role cascade path and are unaffected.

WITH computed AS (
    SELECT
        ug.id,
        ug.resource_owner,
        ug.roles AS current_roles,
        COALESCE(
            ARRAY(
                SELECT r
                FROM unnest(ug.roles) AS r
                WHERE r = ANY(pg.granted_role_keys)
            ),
            ARRAY[]::TEXT[]
        ) AS valid_roles
    FROM projections.user_grants5 ug
    JOIN projections.project_grants4 pg
        ON pg.instance_id = ug.instance_id
       AND pg.grant_id = ug.grant_id
    WHERE ug.instance_id = $1
      AND ug.grant_id IS NOT NULL AND ug.grant_id <> ''
      AND ug.roles IS NOT NULL
      AND cardinality(ug.roles) > 0
)
SELECT id, resource_owner, valid_roles
FROM computed
WHERE cardinality(valid_roles) <> cardinality(current_roles);

For every misaligned User Grant returned by this query, the migration command handler generates and pushes a usergrant.NewUserGrantCascadeChangedEvent to the transactional event store. This process re-synchronizes the historical state and enforces the correct administrative boundaries.

Remediation and Mitigation Strategies

Remediation requires upgrading the ZITADEL deployment to a version that contains both the logic fixes and the automated database state correction.

Administrators on the v4.x branch must upgrade to version 4.16.0 or higher immediately. Upon startup, the updated application binary executes the migration scripts, scans for orphaned or desynchronized permissions, and automatically registers corrective events in the event store.

Administrators on the v3.x release line must migrate their deployments to the active v4.x release line, as the v3.x line has reached End-of-Life (EOL) and will not receive security backports. If an immediate upgrade is not feasible, administrators should run the diagnostic SQL query provided in the database migration analysis to manually identify affected accounts and execute manual updates via the ZITADEL Management API.

Official Patches

ZITADELOfficial patch implementing safe role cascading and Setup Step 73 migration.

Fix Analysis (1)

Technical Appendix

CVSS Score
5.5/ 10
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:H/A:N

Affected Systems

ZITADEL Identity Platform

Affected Versions Detail

Product
Affected Versions
Fixed Version
ZITADEL
ZITADEL
>= 4.0.0, < 4.16.04.16.0
ZITADEL
ZITADEL
>= 3.0.0, <= 3.4.124.16.0
AttributeDetail
CWE IDCWE-193 (Off-by-one Error)
Attack VectorNetwork (AV:N)
CVSS v3.1 Score5.5 (Medium)
ImpactImproper Permission Revocation
Exploit StatusProof of Concept Available
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1098Account Manipulation
Persistence
T1548Abuse Elevation Control Mechanism
Privilege Escalation
CWE-193
Off-by-one Error

An error where the software performs an operation on a loop index that results in skipping elements of a slice during dynamic structure mutation.

Known Exploits & Detection

GitHub Security Advisory (GHSA-v859-c572-qh5p)Detailed writeup and dynamic test suites verifying dynamic role retention on vulnerable releases.

Vulnerability Timeline

ZITADEL engineering commits fix and testing framework to code repository
2026-07-09
CVE-2026-76081 and GHSA-v859-c572-qh5p publicly disclosed
2026-09-14
ZITADEL v4.16.0 containing logical fix and setup migration published
2026-09-14

References & Sources

  • [1]ZITADEL Security Advisory GHSA-v859-c572-qh5p
  • [2]ZITADEL Logical Fix Commit
  • [3]ZITADEL Release v4.16.0
  • [4]CVE-2026-76081 Record

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 2 hours ago•CVE-2026-46696
3.3

CVE-2026-46696: Safe Mode Sandbox Bypass in October CMS via Session Store and Forwarded Builder Calls

CVE-2026-46696 identifies a critical sandbox bypass vulnerability in the October CMS platform that affects the Twig template security policy when safe mode is enabled. An authenticated backend user with permissions to modify CMS markup templates can chain unrestricted session store method access with Eloquent database query forwarding omissions. This chain allows the attacker to execute arbitrary raw SQL queries to read system secrets and subsequently write those secrets directly to the active session payload, achieving unauthorized administrative privilege escalation.

Alon Barad
Alon Barad
4 views•8 min read
•about 3 hours ago•CVE-2026-49400
3.3

CVE-2026-49400: PHP Object Injection Sandbox Escape in October CMS SessionMaker

A security vulnerability in October Content Management System (CMS) involves the deserialization of untrusted data (CWE-502) within the backend SessionMaker trait. Prior to the patched versions, October CMS stored widget session states as base64-encoded serialized PHP objects. When loading these states, the application consumed them using unserialize() without enforcing class restrictions (allowed_classes). In configurations where cms.safe_mode is enabled to sandbox users with markup editor privileges, an attacker can exploit this behavior to instantiate arbitrary PHP classes and execute arbitrary code via accessible gadget chains.

Amit Schendel
Amit Schendel
7 views•6 min read
•about 4 hours ago•CVE-2026-56668
8.1

CVE-2026-56668: Privilege Escalation and Cross-Client Audience Bypass in ZITADEL OAuth2 Token Exchange

A security vulnerability in ZITADEL's backend implementation of the OAuth2 Token Exchange endpoint allows authenticated clients to perform scope escalation and cross-client audience bypass. Prior to version 4.15.3, the Token Exchange flow lacked crucial validation logic, enabling low-privilege tokens to be exchanged for high-privilege tokens or tokens valid within other client applications, violating the OAuth2 delegation model.

Alon Barad
Alon Barad
7 views•7 min read
•about 6 hours ago•GHSA-2XMM-M4WV-3FJH
3.9

GHSA-2XMM-M4WV-3FJH: Incomplete Scheme Validation in October CMS Image Resizer

This report provides a technical analysis of GHSA-2XMM-M4WV-3FJH, an incomplete scheme validation vulnerability in the image resizing utility of October CMS. By exploiting this flaw, authenticated or privileged users can pass dangerous URI schemes to trigger deserialization of untrusted metadata.

Alon Barad
Alon Barad
4 views•5 min read
•about 8 hours ago•CVE-2026-59178
9.8

CVE-2026-59178: Authentication Bypass in ESPHome Device Builder Dashboard

An authentication bypass vulnerability in ESPHome Device Builder Dashboard allows unauthenticated remote attackers to gain administrative access. The flaw is caused by a backward compatibility break during an environment variable rename that silently disables dashboard authentication upon upgrade.

Alon Barad
Alon Barad
5 views•6 min read
•about 11 hours ago•CVE-2026-61534
9.1

CVE-2026-61534: Prototype Pollution in confetti yayson JSON:API Deserialization Engine

A critical prototype pollution vulnerability was discovered in the confetti yayson library prior to version 4.3.0. The library deserializes JSON:API structures into internal cache dictionaries mapped with standard JavaScript objects. An attacker can control the cache keys by supplying '__proto__' in properties like type or id, modifying the prototype of all JavaScript objects process-wide.

Amit Schendel
Amit Schendel
4 views•7 min read