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·41 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

•27 minutes ago•CVE-2026-115908
7.4

CVE-2026-115908: Arbitrary File Write via Lexical Symlink Validation Bypass in Rust zip Crate

CVE-2026-115908 (GHSA-hjqx-j4rw-g7xv) is a high-severity path traversal and link-following vulnerability in the Rust zip library (zip-rs/zip2) affecting versions >= 7.2.0 and < 9.0.0. Because ZipArchive::extract and ZipArchive::extract_unwrapped_root_dir validate symbolic link targets solely via lexical prefix comparisons rather than resolving intermediate filesystem links, an attacker who knows or can guess the absolute extraction path can construct multi-hop symlink chains that escape the destination directory, enabling arbitrary file creation and overwrite with the privileges of the extracting process.

Amit Schendel
Amit Schendel
0 views•10 min read
•about 1 hour ago•CVE-2026-115907
7.5

CVE-2026-115907: Denial of Service via Excessive Memory Pre-allocation in Rust zip Crate Legacy Decoders

CVE-2026-115907 is an uncontrolled resource consumption vulnerability in the Rust zip crate (versions 5.1.0 through 8.6.0, and 9.0.0 pre-releases) when compiled with the optional legacy-zip feature flag. The vulnerability resides in the legacy decompression decoders—Shrink, Reduce, and Implode—which directly pass an archive-declared 64-bit uncompressed size from a ZIP64 extended information extra field to Vec::reserve() before validating the compressed data stream. By supplying a malformed ZIP archive with a declared uncompressed size exceeding isize::MAX (such as u64::MAX), an unauthenticated attacker can trigger an unhandled capacity overflow panic in the Rust standard library allocator, terminating the calling thread or aborting the entire process.

Alon Barad
Alon Barad
2 views•12 min read
•about 2 hours ago•GHSA-VPP8-G58W-M8MP
7.5

GHSA-vpp8-g58w-m8mp: HTTP/2 Rapid Reset Denial of Service in Twisted Web

Twisted versions up to and including 26.4.0 are vulnerable to an unauthenticated remote denial-of-service attack via the HTTP/2 Rapid Reset technique (a variant of CVE-2023-44487). The HTTP/2 connection handler in twisted.web processes pipelined HEADERS and RST_STREAM frames sequentially within the same TCP segment. Because the request dispatch and synchronous rendering occur upon receiving the StreamEnded event before the subsequent StreamReset event is evaluated, an attacker can force the server to expend CPU cycles rendering responses while immediately evicting stream state to bypass concurrent stream limits.

Alon Barad
Alon Barad
7 views•8 min read
•about 3 hours ago•CVE-2026-118664
8.2

CVE-2026-118664: Quadratic Complexity Denial of Service in decode-uri-component

An inefficient algorithmic complexity vulnerability exists in the npm package decode-uri-component prior to version 0.5.1. When processing malformed URI strings containing invalid percent-escape sequences, the library falls back to a custom decoder that collects distinct percent-encoded runs into a dictionary and executes a full-string regular expression replacement for each entry. Supplying a payload with thousands of distinct percent-encoded sequences alongside a malformed escape triggers quadratic O(K * L) execution time, starving the single-threaded Node.js event loop and resulting in an unauthenticated Denial of Service.

Amit Schendel
Amit Schendel
4 views•9 min read
•about 4 hours ago•CVE-2026-115921
6.2

CVE-2026-115921: Null Pointer Dereference and Integer Conversion in ONNX ScanInferenceFunction

Open Neural Network Exchange (ONNX) versions 1.4.0 prior to 1.22.0 contain a null pointer dereference (CWE-476) and a signed-to-unsigned integer conversion error (CWE-195) in the shape inference functions for the Scan operator. When processing an untrusted ONNX model, the functions ScanInferenceFunction, ScanInferenceFunction_opset8, and ScanInferenceFunction_opset9 fail to validate the presence and value of the required num_scan_inputs attribute. Omitting the attribute results in a direct null pointer dereference that crashes the host process via SIGSEGV, while supplying a negative value triggers an integer wrap-around during type narrowing that leads to an unhandled allocation failure. Notably, models containing a negative num_scan_inputs value bypass standard validation via onnx.checker.check_model().

Amit Schendel
Amit Schendel
4 views•9 min read
•about 5 hours ago•CVE-2026-91403
9.1

CVE-2026-91403: Cryptographic Trust Validation Bypass in Oracle Java SE JSSE via Compromised eMudhra Intermediate CAs

CVE-2026-91403 is a critical cryptographic trust validation flaw (CWE-327) in the Java Secure Socket Extension (JSSE) component of Oracle Java SE, Oracle GraalVM, and OpenJDK builds. Triggered by the compromise of private signing keys for eight intermediate Certificate Authorities operated by eMudhra (emSign), unpatched Java runtime environments implicitly trust fraudulent TLS and code-signing certificates issued by these compromised intermediates. Because Java disables online certificate revocation checking (OCSP/CRL) by default, an unauthenticated network attacker in an Adversary-in-the-Middle (AiTM) position can intercept, decrypt, and modify encrypted TLS communications or bypass code signature verification without triggering security exceptions.

Alon Barad
Alon Barad
7 views•12 min read