Jul 29, 2026·7 min read·54 visits
ZITADEL allowed self-managing users to request plaintext verification OTPs in API responses by setting the returnCode parameter to true. Standard users could exploit this logic flaw to verify arbitrary email addresses and phone numbers without possessing actual access to the targeted out-of-band communication channel.
A critical authorization bypass vulnerability was identified in the ZITADEL identity management platform, tracked as CVE-2026-54693 (GHSA-jq8w-8q2f-ffm9). Due to incorrect privilege validation in self-service profile modification endpoints, standard authenticated users could obtain plaintext verification codes during email or phone number updates. This allows attackers to claim and verify arbitrary email addresses or phone numbers without controlling the underlying contact methods.
ZITADEL, an open-source identity and access management platform, contains a logical authorization vulnerability tracked under CVE-2026-54693. The flaw is located within the platform's self-management APIs handling email and phone updates. These endpoints are designed to allow users to update their contact information independently.
During a standard update sequence, the application generates a verification code or one-time password and delivers it out-of-band to confirm ownership of the new destination. The vulnerability allows self-managing users to request that the plaintext verification code be returned directly in the API response. This behavior bypasses the requirement for out-of-band delivery entirely.
Attackers can exploit this design flaw to associate arbitrary email addresses or phone numbers with their accounts. This process does not require access to the target inbox or mobile device. The resulting security bypass undermines trust in email and phone verification states throughout the identity provider, compromising downstream applications.
The vulnerability is classified under CWE-863 (Incorrect Authorization). It represents a failure to restrict administrative-level query parameters from standard users. The impact extends to downstream applications that rely on ZITADEL for federated identity and access decisions.
The root cause of the vulnerability resides in ZITADEL's backend authorization logic implemented in Go within the internal/command/ package. The platform provides a returnCode parameter to support administrative workflows, such as automated provisioning or programmatic migrations. When this parameter is set to true, the server serializes and returns the plaintext verification OTP directly in the JSON response payload.
To check user permissions during profile modifications, the command handlers call the checkPermissionUpdateUser method. This function accepts a boolean parameter called allowSelfManagement. In the vulnerable implementation, this parameter was hardcoded to true across multiple update endpoints, including email, phone, and human profile update paths.
Because the system hardcoded allowSelfManagement to true, the authorization layer did not evaluate whether the caller possessed administrative privileges. The authorization layer only validated that the user was updating their own profile. This logic failed to recognize that requesting the verification code in the response is an administrative action.
Consequently, any authenticated user could issue a self-service profile update request with the returnCode parameter set to true. The system processed the request, generated the verification code, and returned the plaintext OTP to the user. This logical oversight allowed standard users to abuse administrative functions designed solely for backend integrations.
The vulnerable code paths are distributed across three primary self-service command files in the backend: internal/command/user_v2_email.go, internal/command/user_v2_phone.go, and internal/command/user_v2_human.go. The flaw is characterized by the static passing of true to the checkPermissionUpdateUser function.
The architectural flow of the vulnerability can be modeled to illustrate how the request bypasses the out-of-band validation channel.
The official fix resolves this issue by dynamically calculating the value of the allowSelfManagement parameter. The patch sets allowSelfManagement to the negation of returnCode (allowSelfManagement := !returnCode). When a client requests the verification code in the API response, allowSelfManagement becomes false, forcing the server to evaluate the request against administrative permissions.
The following code comparison highlights the specific structural changes introduced in the patch for email updates within internal/command/user_v2_email.go:
// VULNERABLE CODE
if err = c.checkPermissionUpdateUser(ctx, cmd.aggregate.ResourceOwner, userID, true); err != nil {
return nil, err
}
// PATCHED CODE
// The allowSelfManagement variable is now dynamically set to false if returnCode is true.
allowSelfManagement := !returnCode
if err = c.checkPermissionUpdateUser(ctx, cmd.aggregate.ResourceOwner, userID, allowSelfManagement); err != nil {
return nil, err
}This pattern is consistently implemented across all affected command files to ensure uniform authorization checks and robust access control enforcement.
Exploitation of this vulnerability requires the attacker to possess a valid, authenticated account on the target ZITADEL instance. No elevated administrative privileges are needed to initiate the attack sequence. The attacker must have network access to the ZITADEL user management API endpoints.
The attack begins when the user submits an HTTP POST request to update their email or phone number. In this request, the attacker specifies the target email address (for example, victim@company.com) and adds the parameter "returnCode": true. The server processes the update and returns a JSON payload containing the plaintext verification code.
Upon receiving the plaintext verification code in the HTTP response, the attacker extracts the code. The attacker then sends a subsequent verification request to the ZITADEL API using the extracted code. Because the code is valid, the server marks the email address or phone number as verified on the attacker's account.
The attack succeeds without generating any out-of-band delivery notifications to the actual owner of the email address or phone number. The attacker completes the validation sequence entirely within the in-band HTTP channel. This allows the attacker to verify arbitrary identities without ownership verification.
The security impact of this vulnerability is critical for environments that rely on email or phone verification for security boundaries. Attackers can claim and verify arbitrary email domains or phone numbers on their accounts. This verified status can bypass security controls or downstream application policies.
In single sign-on (SSO) environments, downstream applications often trust the verification claim provided by ZITADEL. An attacker who verifies an email address belonging to a target domain can log in to connected applications as a member of that domain. This leads to unauthorized data access, privilege escalation, and domain hijacking.
The vulnerability also compromises multi-factor authentication (MFA) and account recovery mechanisms. An attacker can associate and verify their phone number on a victim's profile if they have temporary session access. This modification allows the attacker to establish persistence and bypass subsequent MFA challenges.
The CVSS v4.0 score is calculated as 8.2 (High). The primary drivers of this score are the high impact on integrity and subsequent systems. While confidentiality of ZITADEL itself is not directly compromised, the integrity of the identity claims is completely undermined.
The primary remediation strategy is to upgrade ZITADEL deployments to the official patched releases. For deployments on the v3 branch, administrators must upgrade to version v3.4.11 or higher. For deployments on the v4 branch, administrators must upgrade to version v4.15.1 or higher.
If immediate patching is not possible, organizations should implement monitoring to detect exploit attempts. Administrators should monitor HTTP API logs for requests targeting /v2/users/.../email or /v2/users/.../phone that contain the parameter returnCode: true or ReturnCode: true. Any such request initiated by a non-administrative user indicates active exploitation.
Security teams can also audit the ZITADEL event store for indicators of compromise. Specifically, look for instances where a user.HumanEmailChangedEvent is immediately followed by a user.HumanEmailVerifiedEvent within a very short timeframe. This pattern suggests automated verification bypassing the typical out-of-band latency.
Long-term architectural security requires evaluating how downstream systems trust ZITADEL identity claims. Organizations should implement strict validation of federated claims and avoid relying solely on the email verification flag for sensitive authorization decisions. Applying the principle of least privilege to API client permissions will further limit the attack surface.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:H/VA:N/SC:N/SI:H/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
ZITADEL ZITADEL | >= 2.43.0, < 2.71.19 | 2.71.19 |
ZITADEL ZITADEL | >= 3.0.0-rc.1, < 3.4.11 | 3.4.11 |
ZITADEL ZITADEL | >= 4.0.0-rc.1, < 4.15.1 | 4.15.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-863 |
| Attack Vector | Network |
| CVSS Score | 8.2 (High) |
| Exploit Status | PoC |
| CISA KEV Status | Not Listed |
The software does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.
A critical Server-Side Request Forgery (SSRF) vulnerability in Trigger.dev prior to version 4.5.2 allows authenticated organization members to configure webhook alert channels with unvalidated target URLs. This can lead to internal network scanning and cloud metadata extraction.
A Server-Side Request Forgery (SSRF) vulnerability exists in the rmcp OAuth client, which is part of the Model Context Protocol (MCP) Rust SDK. The vulnerability arises from insecure processing of the resource_metadata parameter in WWW-Authenticate headers returned by a malicious or compromised MCP server. The client parses and fetches absolute URLs from this header without validation of scheme, origin, or network routing, allowing remote attackers to initiate HTTP GET requests to local network interfaces, RFC 1918 private subnets, or cloud metadata endpoints.
A module allowlist bypass vulnerability (CVE-2026-92945 / GHSA-7q3f-wx44-378m) was identified in the vm2 sandboxing library prior to version 3.11.7. This flaw permits unauthenticated or untrusted code running within the sandbox environment to bypass explicit module restrictions. When the transitive resolution option is disabled, the system fails to validate file system path boundaries, allowing prefix-sharing sibling directories to be resolved and loaded, thereby escaping intended sandbox restrictions.
CVE-2026-92941 is a critical sandbox-escape and trust-manipulation vulnerability in the vm2 library (versions 3.11.3 to 3.11.6). This security flaw allows untrusted code executing within a NodeVM sandbox environment to compromise the global TLS trust store of the host Node.js process. By leveraging a design flaw where the host's native 'tls.setDefaultCACertificates' can be executed via a proxy wrapper, combined with a bridge unwrapping bypass in the 'url' module, an attacker can modify the process-wide default root Certificate Authorities. Consequently, all subsequent outbound TLS/HTTPS clients running on the host thread are forced to trust attacker-signed certificates, facilitating transparent Man-in-the-Middle (MitM) attacks. The vulnerability was resolved in version 3.11.7 of vm2 by introducing built-in member-level sanitization before applying read-only proxy wrappers.
A critical engine-level reachability failure in Node.js 26 running V8 14.6 allows attackers to escape the vm2 sandbox environment. When consecutive prototype properties are modified using sequential assignments, a V8 optimization bug fails to invalidate the PromiseThenLookupChain protector. By calling Promise.prototype.finally, the attacker bypasses the vm2 wrappers, hijacks the promise reaction using a custom constructor, triggers a calibrated stack overflow to capture a host-realm RangeError, and executes arbitrary shell commands on the host.
A critical sandbox escape vulnerability in the vm2 library allows sandboxed JavaScript code to bypass containment and execute arbitrary native code on the host process. This occurs when the host's builtin crypto module is exposed to the NodeVM environment. Although vm2 implements a read-only proxy layer to restrict direct modifications to host properties, it does not prevent invocation of host-level functions. By calling the crypto.setEngine API with a path to a malicious native library on disk, an attacker can trigger OpenSSL's dynamic module loader. The host's operating system loader immediately runs the dynamic library's initializers/constructors before verifying engine compatibility, leading to remote code execution in the context of the host process.