Skip to content

users.2fa.sendEmailCode: unauthenticated email flooding + attacker-driven 2FA code invalidation #42483

Description

@Victor725

Description:

POST /api/v1/users.2fa.sendEmailCode is reachable without authentication and (unlike its sibling users.sendConfirmationEmail, which has rateLimiterOptions: 1/60s) carries no per-target rate limit. EmailCheck.sendEmailCode() generates a fresh 6-digit code on every call and overwrites the single stored services.emailCode.code — so each anonymous call both sends an email to the victim and invalidates any code the victim is currently using. (User enumeration via response differences was already fixed — thanks! — but the flood/overwrite abuse remains.)

Steps to reproduce:

  1. On a fresh workspace, register an initial user (becomes admin), then a victim user with a verified email and email 2FA enabled (account settings, or provisionally via DB).
  2. Anonymous caller (no credentials at all) fires 8 rapid requests:
for i in $(seq 1 8); do
  curl -s -X POST http://localhost:3000/api/v1/users.2fa.sendEmailCode \
    -H 'Content-Type: application/json' \
    -d '{"emailOrUsername":"alice@example.com"}' -o /dev/null -w '%{http_code} ';
done
  1. Inspect the victim's mailbox (any SMTP sink works) and the users collection (services.emailCode).

Expected behavior:

Per-target throttling (e.g., 1 mail/60 s, 5/hour) comparable to users.sendConfirmationEmail, and no attacker-driven invalidation of an unexpired code (resend same code or keep remaining TTL).

Actual behavior:

  • 8/8 requests return 200 within 0.44 s; 8 "Authentication code" emails arrive at the victim's address.
  • Consecutive mails carry different codes (observed 285206 → 917527); services.emailCode.code is a single bcrypt field rewritten on every call — the victim's in-flight code dies each time the attacker calls.
  • A limit does kick in around the 9th–10th request within 60 s (429), which blunts but does not remove the flood (~8–9 mails/minute/attacker-IP, anonymously).

Server Setup Information:

  • Version of Rocket.Chat Server: 8.10.0-develop (branch develop, commit 151b7df, 2026-10-01)
  • License Type: Community (no license)
  • Number of Users: 2 (1 admin for setup, 1 victim with email 2FA) — attack is unauthenticated
  • Operating System: Linux (Docker)
  • Deployment Method: docker (built from source following the official CI pipeline: yarn install → turbo run build → meteor build → apps/meteor/.docker/Dockerfile.alpine)
  • Number of Running Instances: 1
  • DB Replicaset Oplog: enabled (single-node replica set rs0)
  • NodeJS Version: 24.15.0
  • MongoDB Version: 8.0
  • SMTP: local sink on the instance's SMTP host/port

Client Setup Information

  • Desktop App or Browser Version: none — plain REST via curl
  • Operating System: any

Activity

  1. julio-rocketchat commented on Oct 2, 2026

    @julio-rocketchat
    Member

    The right way to report security issues are via advisories, HackerOne, or email (we saw that you already sent an email with all the possible findings and we will track it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions