Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2025-64101

CVE-2025-64101: Zitadel Auth Bypass Vulnerability

CVE-2025-64101 is an authentication bypass vulnerability in Zitadel that allows attackers to manipulate password reset links and capture secret codes. This article covers the technical details, affected versions, and mitigation.

Published:

CVE-2025-64101 Overview

CVE-2025-64101 is a host header injection vulnerability in Zitadel, an open-source identity infrastructure platform. The flaw resides in the password reset workflow, where Zitadel constructs the reset confirmation URL from the Forwarded or X-Forwarded-Host HTTP request headers. An attacker who manipulates these headers can cause Zitadel to deliver a password reset email containing a link pointing to an attacker-controlled domain. If the victim clicks the link, the embedded secret reset code is captured by the attacker and used to take over the account. Accounts protected by Multi-Factor Authentication (MFA) or passwordless authentication are not affected by this attack path.

Critical Impact

Account takeover via captured password reset codes for users without MFA enabled, classified under [CWE-601] Open Redirect.

Affected Products

  • Zitadel versions prior to 2.71.18
  • Zitadel versions prior to 3.4.3
  • Zitadel versions prior to 4.6.0

Discovery Timeline

  • 2025-10-29 - CVE-2025-64101 published to NVD
  • 2025-11-04 - Last updated in NVD database

Technical Details for CVE-2025-64101

Vulnerability Analysis

The vulnerability stems from Zitadel trusting client-controlled HTTP headers when generating password reset confirmation URLs. When a user requests a password reset, Zitadel reads the Forwarded or X-Forwarded-Host header from the inbound HTTP request to determine the host portion of the reset link. The resulting URL embeds a single-use secret reset code and is sent to the user's verified email address.

An attacker submits a password reset request for a target account while supplying a forged X-Forwarded-Host header pointing to a domain they control. Zitadel renders the email using the malicious host. When the victim clicks the link, their browser sends the secret reset code to the attacker's server. The attacker then replays the code against the legitimate Zitadel instance to set a new password and seize the account.

Root Cause

The root cause is the use of the request Host value, sourced from forwarded headers, as both the instance lookup key and the basis for outbound URL construction without validation against a configured allowlist. The fix in commit 72a5c33e6ac302b978d564bd049f9364f5a989b1 decouples instance resolution from the raw host string by introducing an InstanceDomain() accessor that returns the hostname only, ignoring port and untrusted forwarded values.

Attack Vector

Exploitation requires network access to the Zitadel password reset endpoint and user interaction (clicking the email link). No authentication is required to initiate the attack. MFA-enabled and passwordless accounts are not exploitable via this path because possession of the reset code alone is insufficient to authenticate.

go
// Patch excerpt: internal/api/grpc/server/connect_middleware/instance_interceptor.go
 func addInstanceByRequestedHost(ctx context.Context, req connect.AnyRequest, handler connect.UnaryFunc, verifier authz.InstanceVerifier, translator *i18n.Translator, externalDomain string) (connect.AnyResponse, error) {
 	requestContext := zitadel_http.DomainContext(ctx)
-	if requestContext.InstanceHost == "" {
+	if requestContext.InstanceDomain() == "" {
 		logging.WithFields("origin", requestContext.Origin(), "externalDomain", externalDomain).Error("unable to set instance")
 		return nil, connect.NewError(connect.CodeNotFound, errors.New("no instanceHost specified"))
 	}
-	instance, err := verifier.InstanceByHost(ctx, requestContext.InstanceHost, requestContext.PublicHost)
+	instance, err := verifier.InstanceByHost(ctx, requestContext.InstanceDomain(), requestContext.RequestedDomain())

Source: Zitadel GitHub Commit 72a5c33

Detection Methods for CVE-2025-64101

Indicators of Compromise

  • Password reset emails delivered to users containing links with hostnames different from the configured Zitadel external domain.
  • Inbound HTTP requests to /ui/login/password/init or password reset gRPC endpoints with X-Forwarded-Host or Forwarded headers referencing unknown domains.
  • Multiple password reset requests for the same account originating from a single IP within a short interval.

Detection Strategies

  • Inspect reverse proxy and Zitadel access logs for mismatches between the configured ExternalDomain and the Host, X-Forwarded-Host, or Forwarded request headers.
  • Alert on HTTP requests to password reset endpoints carrying forwarded host headers that resolve outside an approved domain allowlist.
  • Correlate password reset initiation events with subsequent successful password change events from unfamiliar geolocations or user agents.

Monitoring Recommendations

  • Forward Zitadel application logs and upstream proxy logs to a centralized SIEM with normalized HTTP header fields.
  • Monitor outbound email metadata or templating logs to verify generated reset URLs match the canonical Zitadel domain.
  • Track audit events for human.password.changed and human.password.code.added and flag abnormal sequences across tenants.

How to Mitigate CVE-2025-64101

Immediate Actions Required

  • Upgrade Zitadel to version 4.6.0, 3.4.3, or 2.71.18 depending on the deployed major release branch.
  • Enforce MFA or passwordless authentication for all user accounts to neutralize this and similar reset-token interception attacks.
  • Audit recent password reset events and invalidate active reset codes for non-MFA users.

Patch Information

The vulnerability is fixed in Zitadel 4.6.0, 3.4.3, and 2.71.18. The fix replaces direct use of the request host with a validated InstanceDomain() value that ignores untrusted forwarded headers when building reset URLs. See the Zitadel Security Advisory GHSA-mwmh-7px9-4c23 for full upgrade guidance.

Workarounds

  • Configure the upstream reverse proxy or load balancer to strip or overwrite X-Forwarded-Host and Forwarded headers before they reach Zitadel.
  • Restrict the proxy to only forward requests where the Host header matches the configured Zitadel external domain.
  • Mandate MFA enrollment policies organization-wide until patches are deployed.
bash
# NGINX example: sanitize forwarded host headers before proxying to Zitadel
location / {
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header Forwarded "host=$host;proto=$scheme";
    proxy_pass http://zitadel_upstream;
}

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.