CVE-2024-47060
Executive Summary
CVE-2024-47060 is a vulnerability in Zitadel, an open-source identity management platform. The flaw allows unauthorized access to applications and resources even after an organization or project has been deactivated. This occurs because the application lifecycle is not tightly coupled with the organization's lifecycle; deactivating an organization does not automatically deactivate its associated applications. As a result, users from other organizations can still log in and access these applications, leading to potential data exposure and unauthorized access. The CVSS score is 4.3 (Medium), and the vulnerability is classified under CWE-862 (Missing Authorization). Patches are available in multiple versions, and users are advised to upgrade or manually disable applications.
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 distributed as a Go binary and Docker image. The vulnerability affects the core application logic, not a specific package manager. Users should verify the integrity of their Zitadel installation by checking the official release signatures and hashes. The patch is available in the official GitHub repository.
CVSS Breakdown & Threat Level
- CVSS Score: 4.3 (Medium)
- Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
- Attack Vector: Network
- Attack Complexity: Low
- Privileges Required: Low
- User Interaction: None
- Scope: Unchanged
- Confidentiality Impact: Low
- Integrity Impact: None
- Availability Impact: None
The threat level is medium due to the low complexity and network attack vector, but the impact is limited to low confidentiality. The vulnerability does not allow for data modification or denial of service.
Deep Dive & Mechanics
The vulnerability stems from a missing authorization check in Zitadel's application lifecycle management. When an organization is deactivated, the system correctly marks the organization as inactive but fails to propagate this state to its associated projects and applications. The application's OAuth2/OIDC client remains active, allowing users from other organizations to authenticate and access resources.
The root cause is in the authorization logic that checks the organization's state. The code likely checks the organization's State field but does not cascade this check to the application level. For example, when a user attempts to authenticate via an application, the system verifies the application's client ID and secret but does not verify if the parent organization is active.
The attack flow:
- An attacker has valid credentials for a user in Organization A.
- Organization B is deactivated, but its application remains active.
- The attacker uses their credentials to authenticate via Organization B's application.
- The system grants access because the application is still active, and the attacker gains unauthorized access to resources.
Vulnerable vs. Patched Code
The vulnerable code likely resides in the authorization middleware or the application service. The patch adds a check for the organization's state before allowing authentication.
Vulnerable Code (pseudo):
func (s *ApplicationService) Authenticate(clientID, clientSecret string) (*Session, error) {
app, err := s.GetApplicationByClientID(clientID)
if err != nil {
return nil, err
}
// Missing check for organization state
if app.ClientSecret != clientSecret {
return nil, ErrInvalidCredentials
}
return s.CreateSession(app)
}
Patched Code (pseudo):
func (s *ApplicationService) Authenticate(clientID, clientSecret string) (*Session, error) {
app, err := s.GetApplicationByClientID(clientID)
if err != nil {
return nil, err
}
org, err := s.GetOrganizationByID(app.OrgID)
if err != nil {
return nil, err
}
if org.State != OrganizationStateActive {
return nil, ErrOrganizationInactive
}
if app.ClientSecret != clientSecret {
return nil, ErrInvalidCredentials
}
return s.CreateSession(app)
}
Exploitation & Lab Setup
Red Team
Prerequisites:
- Access to a Zitadel instance (version < 2.54.10)
- Valid credentials for a user in any organization
- Knowledge of an application client ID from a deactivated organization
Steps:
- Identify a deactivated organization (e.g., via API or admin panel).
- Obtain the client ID of an application in that organization (e.g., from public documentation or leaked config).
- Use the following curl command to authenticate:
curl -X POST "https://zitadel.example.com/oauth/v2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET"
- If successful, the response will contain an access token. Use this token to access protected resources.
Note: The client secret may be unknown. In that case, the attacker would need to find a way to obtain it (e.g., via social engineering or leaked data).
Blue Team
Detection:
- Monitor for authentication requests from deactivated organizations.
- Alert on successful token generation for applications whose parent organization is inactive.
- Review logs for unusual access patterns, such as users from Organization A accessing resources via Organization B's application.
Mitigation:
- Immediately upgrade to a patched version.
- If upgrade is not possible, manually disable all applications in deactivated organizations.
- Implement additional monitoring for cross-organization access.
Indicators of Compromise (IOCs)
- Log Entries: Successful authentication events for applications belonging to deactivated organizations.
- API Calls: POST requests to
/oauth/v2/tokenwith client IDs of deactivated organizations. - Network Traffic: Unusual cross-organization access patterns in Zitadel audit logs.
- File Hashes: No specific file hashes; the vulnerability is in the application logic.
Detection Engineering
YARA Rule
rule CVE_2024_47060_Zitadel_Unauthorized_Access {
meta:
description = "Detects Zitadel versions vulnerable to CVE-2024-47060"
author = "Valters IT"
date = "2024-10-01"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2024-47060"
strings:
$v1 = "2.54.9" ascii wide nocase
$v2 = "2.55.7" ascii wide nocase
$v3 = "2.56.5" ascii wide nocase
$v4 = "2.57.4" ascii wide nocase
$v5 = "2.58.4" ascii wide nocase
$v6 = "2.59.2" ascii wide nocase
$v7 = "2.60.1" ascii wide nocase
$v8 = "2.61.0" ascii wide nocase
$v9 = "2.62.0" ascii wide nocase
condition:
any of ($v*)
}
Sigma Rule
title: Zitadel Unauthorized Access via Deactivated Organization
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects successful authentication for applications belonging to deactivated organizations in Zitadel.
author: Valters IT
date: 2024-10-01
logsource:
product: zitadel
service: audit
detection:
selection:
event_type: "authentication.success"
application_org_state: "inactive"
condition: selection
falsepositives:
- Legitimate access if organization was reactivated
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2024-47060 Zitadel Unauthorized Access"; flow:to_server; content:"POST"; http_method; content:"/oauth/v2/token"; http_uri; content:"client_id"; http_client_body; pcre:"/client_id=[a-zA-Z0-9_-]+/"; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Upgrade Zitadel to one of the patched versions: 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.
- If upgrade is not possible, manually disable all applications in deactivated organizations via the admin console or API.
- Review all deactivated organizations and ensure their applications are disabled.
Long-term Measures:
- Implement automated lifecycle management to cascade organization state to applications.
- Add monitoring for cross-organization access attempts.
- Conduct regular security audits of identity management configurations.
Workaround:
- Use the Zitadel API to disable applications:
curl -X PUT "https://zitadel.example.com/v2/apps/APP_ID" \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"state": "APP_STATE_INACTIVE"}'
Valters IT