OTP bypass through email case variants
The application treated each case variant of the email address as a separate identity. Each one received its own OTP code and its own attempt counter.
Summary
RFC 5321 allows the local part of an address to be case sensitive and, at the same time, discourages relying on that distinction. Mainstream mail providers deliver every variant to the same mailbox. The application did not normalise the address before issuing the code.
An attacker could keep dozens of valid codes active on the same account at once. The number of attempts available against the second factor rose in the same proportion, and the limit of three attempts no longer protected the account.
Technical details
Parallel code generation
Each case variant produced a code of its own:
POST /api/auth/request-otp { "email": "victim@company.com" } -> code A
POST /api/auth/request-otp { "email": "Victim@company.com" } -> code B
POST /api/auth/request-otp { "email": "VICTIM@company.com" } -> code C
POST /api/auth/request-otp { "email": "vIctim@company.com" } -> code D
...
All of them reach the same mailbox: victim@company.com
Each variant has its own limit of 3 attemptsEffect on brute forcing
The local part in the example is six letters long, so 64 variants are possible, one for each combination of upper and lower case. Only alphabetic characters count. Each variant enables a valid code with three attempts:
OTP code: 6 digits -> 1,000,000 combinations
Normal limit: 3 attempts per code -> 0.0003% success
With the bypass:
- 64 case variants x 1 code each = 64 valid codes
- Any one of them works for the same account
- 64 codes x 3 attempts = 192 attempts per round
P(success per round) = 1 - (1 - 1/1,000,000)^192 ~ 0.019%
Repeating every 60 s -> ~1.1% in one hour, against ~0.018% without the bypassParallel issuance also cancels the warning to the user: the victim receives dozens of legitimate code emails. They can no longer tell someone else’s login attempt from their own.
Impact
- Second factor bypass by multiplying the number of valid codes
- Rate limiting evasion by generating distinct identities for the same mailbox
- Unauthorised access to accounts protected by email OTP
- Flooding of the victim’s mailbox with one code email per variant
Remediation
- Normalise the email address to lower case before any operation.
- Apply rate limiting to the normalised email address, not to the string exactly as received.
- Limit active codes. Invalidate the previous code when a new one is generated for the same mailbox.
- Apply a progressive lockout once failed attempts accumulate against the same mailbox, whichever variant they came from.
- Move to TOTP. Time-based codes depend on neither the mailbox nor its delivery.
References
More write-ups
This vector appears in any application that does not normalise the email address
Tell us which assets you want tested. We reply within 24 hours.