CVE-2024-46999
Executive Summary
CVE-2024-46999 is a critical access control vulnerability in Zitadel, an open-source identity management platform. The flaw resides in the user grant deactivation mechanism. When an administrator deactivates a user grant (e.g., removing a user's role or permission to an application), the system fails to enforce this deactivation in the token generation process. Consequently, deactivated grants continue to be included in issued tokens, allowing users to retain unauthorized access to applications and resources. Additionally, the management and auth APIs do not correctly reflect the deactivated state, returning the grant as active or omitting state information entirely. This vulnerability has a CVSS score of 7.3 (High) and affects multiple versions prior to the patched releases. No public proof-of-concept (PoC) or exploit code is currently available, but the risk is significant due to the potential for persistent unauthorized access.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Zitadel is a Go-based application distributed as a Docker image and via GitHub releases. The vulnerability is not in a package manager (NPM, PyPI, Ruby) but in the core application logic. Users should verify the integrity of their Zitadel binaries or Docker images by checking SHA256 checksums provided in the official release notes. The supply chain risk is moderate; an attacker with access to the build pipeline could introduce similar flaws, but the primary threat is exploitation of the unpatched vulnerability in production deployments.
CVSS Breakdown & Threat Level
- CVSS Score: 7.3 (High)
- Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
- Attack Vector (AV): Network – The vulnerability can be exploited remotely.
- Attack Complexity (AC): Low – No special conditions are required.
- Privileges Required (PR): Low – An attacker needs a valid user account with a deactivated grant.
- User Interaction (UI): None – No user action is required.
- Scope (S): Unchanged – The exploit does not affect other components.
- Confidentiality (C): High – Unauthorized access to sensitive data is possible.
- Integrity (I): High – Unauthorized modification of resources is possible.
- Availability (A): None – The vulnerability does not affect availability.
Threat Level: High. The vulnerability allows a low-privileged user to maintain access to resources after their grant has been deactivated. This can lead to data breaches, privilege escalation, and lateral movement within the identity management system.
Deep Dive & Mechanics
The vulnerability stems from a logic flaw in how Zitadel handles user grant deactivation. When an administrator deactivates a user grant (e.g., removing a user's role in an application), the system should update the user's token to reflect this change. However, due to a missing or incorrect state check, the deactivation is not propagated to the token generation service. The token continues to include the deactivated grant, and the management/auth APIs do not return the correct state.
Root Cause: The deactivation event does not trigger a token revocation or refresh. The token cache or generation logic does not re-evaluate the grant's active status. This is likely due to a missing database query or a race condition where the grant state is not updated before the token is issued.
Attack Scenario:
- An administrator deactivates a user grant for a specific application.
- The user, who already has a valid token, continues to use it.
- The token still contains the deactivated grant, allowing the user to access the application.
- The user can also request a new token, which will also include the deactivated grant because the API does not reflect the deactivation.
Impact: Unauthorized access to applications, data exfiltration, and potential privilege escalation if the grant had administrative permissions.
Vulnerable vs. Patched Code
The fix involves ensuring that the token generation and API responses check the state field of the user grant. In vulnerable versions, the code likely omitted this check or used an incorrect condition.
Vulnerable Code (Conceptual):
// token generation logic
func generateToken(userGrant UserGrant) Token {
// Missing check for userGrant.State
token.Claims["grants"] = append(token.Claims["grants"], userGrant)
return token
}
Patched Code (Conceptual):
func generateToken(userGrant UserGrant) Token {
if userGrant.State == "ACTIVE" {
token.Claims["grants"] = append(token.Claims["grants"], userGrant)
}
return token
}
The patch also updates the management and auth APIs to return the correct state. For example, the API endpoint for listing user grants now filters out deactivated grants or includes a state field.
Exploitation & Lab Setup
Red Team
Prerequisites:
- A Zitadel instance running a vulnerable version (e.g., 2.54.9).
- A valid user account with a grant to an application.
- Administrative access to deactivate the grant (or simulate the scenario).
Steps:
- Setup: Deploy Zitadel using Docker:
docker run -d --name zitadel -p 8080:8080 ghcr.io/zitadel/zitadel:v2.54.9 - Create User and Grant: Use the Zitadel console or API to create a user and assign a grant to an application.
- Obtain Token: Authenticate as the user and obtain a JWT token.
- Deactivate Grant: As an admin, deactivate the user grant via the console or API.
- Verify Persistence: Use the old token to access the application. It should still work.
- Request New Token: Request a new token using the user's credentials. The new token should also include the deactivated grant.
- Exploit: Access protected resources using the token.
Example API Call (using curl):
# Get token
curl -X POST https://zitadel.example.com/oauth/v2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password&username=user@example.com&password=secret&scope=openid"
# Access resource
curl -H "Authorization: Bearer <token>" https://app.example.com/api/resource
Blue Team
Detection:
- Monitor for tokens that are used after the grant deactivation timestamp.
- Look for API calls to
/management/v1/grantsthat return inconsistent state. - Alert on repeated access to resources by users whose grants have been deactivated.
Mitigation:
- Immediately upgrade to a patched version.
- If upgrade is not possible, manually remove the user grants (not just deactivate) to ensure access is revoked.
- Implement token revocation policies and short token lifetimes.
Indicators of Compromise (IOCs)
- Log Entries: Zitadel audit logs showing token generation after grant deactivation.
- API Responses: Management API returning
state: "ACTIVE"for deactivated grants. - Token Claims: JWT tokens containing grants with
statefield missing or set toACTIVEwhen it should beINACTIVE. - Network Traffic: Repeated access to applications by users with deactivated grants.
Example IOC (JWT Token):
{
"sub": "user123",
"grants": [
{
"id": "grant456",
"role": "admin",
"state": "ACTIVE" // Should be "INACTIVE"
}
]
}
Detection Engineering
YARA Rule
rule CVE_2024_46999_Zitadel_Grant_Deactivation {
meta:
description = "Detects Zitadel tokens or logs indicating deactivated grants still marked as active"
author = "Valters IT"
date = "2024-10-27"
reference = "CVE-2024-46999"
strings:
$grant_active = /"state"\s*:\s*"ACTIVE"/ nocase
$grant_deactivated = /"state"\s*:\s*"INACTIVE"/ nocase
$token_grant = /"grants"\s*:\s*\[/ nocase
condition:
$token_grant and $grant_active and not $grant_deactivated
}
Sigma Rule
title: Zitadel User Grant Deactivation Bypass
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects Zitadel audit log entries where a token is generated after a grant deactivation event.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2024-46999
author: Valters IT
date: 2024-10-27
logsource:
product: zitadel
service: audit
detection:
selection:
event_type: "token.generated"
grant_state: "ACTIVE"
timeframe: 5m
condition: selection
falsepositives:
- Legitimate token refresh before deactivation propagation
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2024-46999 Zitadel Grant Deactivation Bypass - Token with Active Grant"; flow:to_server,established; content:"/oauth/v2/token"; http_uri; content:"grant_type=password"; http_client_body; pcre:"/\"state\":\"ACTIVE\"/"; classtype:attempted-admin; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Upgrade: Update Zitadel to the latest patched version (2.62.1, 2.61.1, 2.60.2, 2.59.3, 2.58.5, 2.57.5, 2.56.6, 2.55.8, or 2.54.10).
- Manual Removal: If upgrade is not possible, explicitly remove user grants instead of deactivating them. This ensures the grant is deleted from the database.
- Token Revocation: Force token revocation for all users after applying the patch.
Long-Term Mitigations:
- Implement a token revocation list (TRL) or use short-lived tokens with refresh token rotation.
- Regularly audit user grants and token usage.
- Enable logging and monitoring for grant state changes.
Workaround:
- Use the Zitadel API to delete grants instead of deactivating them:
curl -X DELETE https://zitadel.example.com/management/v1/grants/{grantId} \ -H "Authorization: Bearer <admin_token>"
Valters IT