6 digit OTP for Two Factor Auth (2FA) is brute-forceable in 3 days
lukeplant.me.ukThis article is pointless clickbait - what percent of systems don't have some sort of throttling or lockout after X number of bad guesses - damn few I would say. Even the most basic, low budget systems I have developed or worked on have throttling rules in place - many with exponentially increasing timeouts that would prevent this sort of attack.
If a website/system does not implement even the most basic security practices, then there are probably a lot of easier ways to hack into in than trying 100,000+ different passwords in a row.
It reminds me of the 4-digit PINs on payment cards --- yes, the "keyspace" is tiny, but you're going to be locked out long before you get close to exhausting it.
Given the number of services that turned out to use plaintext or trivial password hashing (e.g. MD5), I would bet there are a bunch of services out there that do not effectively limit OTP attempts.
It’s been a long time since I did any work on a real authentication system — since before TOTP was common, anyway. I appreciated the post and found it interesting.
MS had a 7 digit code, and some rate limiting, but even that was insufficient: https://thezerohack.com/how-i-might-have-hacked-any-microsof...
(7 digit reset code for forgot password flows)
If there's a leak of valid usernames or email addresses, for a system that has a few million users, that has a lockout after 10 wrong guesses, then you could gain access to one account for every 10,000 lockouts.
> leak of valid usernames or email addresses
...and passwords, because OTP is the second factor.
> Say you are thinking of a number between 1 and 10,000 and give me one thousand attempts to guess it. To make it harder, you change the number every 100 guesses. To make it harder still, you are thinking of changing it every 50 guesses. Would that work? Well, in the first case I get 100 guesses at 10 different numbers, in the second I get 50 guesses at 200 different numbers, but that makes no difference — I get the same number of guesses, and I only have to guess correctly once to succeed. Mathematically, it boils down to the fact that (xa)b=xab.
That's blatantly false, of course it makes a difference, because it makes the events stochastically independent.
If I choose a number between 1 and 10, and I give you 10 chances to guess it, you have 100% chances to guess it. If on the other hand I change it at every guess, you have only ~65% of chances to guess it.
Edit: fixed the % of chances, it is a binomial distribution, and I totally miscalculated it
I feel like the service has larger problems if they're allowing you 10 requests a second for 3 days...
Not to mention not warning the user that their account is being brute forced.
My recent experience was to get locked out of an account for a few incorrect password attempts. I was the source, had forgotten I changed it or something, the incorrect attempts were a while ago and I don't remember what I was thinking at the time. Required hours on hold waiting for an operator and dancing through stupid hoops (that make more sense to me as audit ass covering than actual security).
Huge pain in the ass.
Do you use a password manager? You should.
Sure. Doesn't mean I never fuck up.
> For a high value account, a motivated attacker can and will continue at this point. (And if you don't consider your accounts high value, why are you bothering with 2FA?).
Because credential stuffing is highly lucrative, even when no individual account is particularly high value, and is the most common way accounts are compromised on most services. There are other things a _user_ might do to prevent credential stuffing, like using unique passwords, but 2FA has the benefit of actually being visible/verifiable for you as a service provider.
>An alternative is account locking after a number of failures, which is much better. However it also brings problems. It means that your 2FA must only be accessible for people who already have passed one level of security, otherwise you have a denial of service vulnerability.
Isn't that how most 2fa flows work? You enter username + password, if it gets accepted you move onto a second page where you enter your 2fa code.
> Plus you need all the account unlocking procedures etc, and need to make sure they are secure, and not actually effectively another attack route.
But realistically speaking don't you need a password reset flow? If so you can just reuse the password reset flow for lockouts as well.
One problem with the two-step approach of only prompting for 2FA after successful auth is it leaks a lot of information - an attacker can confirm that they got the right password, or even just that the account exists and has 2FA enabled.
For TOTP-based 2FA, it would ideally prompt for all three (username/password/TOTP code) immediately, not indicating which part of auth failed.
This may not always be desired, for example when TOTP is just one among several 2FA options.
In those cases, at the very least always prompt for 2FA for accounts that require it, regardless of if password auth succeeded or not. Don’t tell the user if it was the password or TOTP code that failed.
> In those cases, at the very least always prompt for 2FA for accounts that require it, regardless of if password auth succeeded or not. Don’t tell the user if it was the password or TOTP code that failed.
This still leaks that the account exists and has TOTP enabled tho.
You have to choose if leaking the correctness of the password, or allowing DoS of a login, unless all the accounts have the same MFA.
> This still leaks that the account exists and has TOTP enabled tho.
You could mitigate that by prompting for an OTP code on a random but stable subset of nonexistent accounts -- for example, by hashing the provided username with a server-side secret and requesting an OTP if the hash starts with a zero.
First time I see this idea - I like it!
Yes, maybe I should have made that compromise more clear. The alternative I see would be to enforce some form of 2FA for all accounts and always prompt for 2FA. Depending on the system it may make sense.
It’s still way better than leaking a successful password auth.
(I'm the cofounder at https://clerk.dev)
We - and I'm sure every other vendor - limit OTP tries.
The guidelines for authentication are very well-defined by NIST 800-63B. For example:
> In all cases, the authentication SHALL be considered invalid if not completed within 10 minutes.
https://pages.nist.gov/800-63-3/sp800-63b.html#5132-out-of-b...
Depending on the exact type of OTP, NIST may have different guidelines.
The thing I don't understand is why some OTP systems have started issuing 7-digit codes via text/email (e.g. Sendgrid). I have to imagine Sendgird is competent enough to lockout the account after a few failed OTPs and/or limit the duration a code is valid, so I don't understand the need for the extra digit. It makes mental chunking harder, and is nothing but security theater if the above two checks are in place.
I think what’s important here is when there can’t or won’t be a “lock out” after a certain number of tries. I’ve seen a few door locks based on TOTPish. They won’t lock you out because who knows why. You can just stick a device outside the door, slamming the BLE endpoint with codes until it unlocks, sorta like a proxmark used to do for keyless entries (maybe still does, haven’t messed with them since ‘08ish)
Its a shame the TOTP standard does nor support variable code length, 8 symbols seems to be better for high value accounts but still usable
> Its a shame the TOTP standard does nor support variable code length, 8 symbols seems to be better for high value accounts but still usable
It does. TOTP (RFC 6238) builds off of HOTP (RFC 4226), which states (§5.3):
> Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code. Depending on security requirements, Digit = 7 or more SHOULD be considered in order to extract a longer HOTP value.
* https://datatracker.ietf.org/doc/html/rfc4226#section-5.3
There is no inherent limitation in the protocol on how many digits to use (AFAICT): the pseudo-code examples have the number of digitals desired as a parameter in each function.
Further, HOTP even discusses (§E.2) is to use (six+) alphanumeric characters ([A-Za-z0-9]) instead of just digits ([0-9]), though digits are "desirable" for easier entry.
True, but the problem is I do not believe any apps implementing TOTP allow for codes over six numerical digits. So the real world standard (Google Authenticator) does not support it.
Google supports 6 or 8, [Aegis](https://getaegis.app/) supports the full standard, Authy supports 6 or 8, Sophos supports 6 or 8. Lots of support.
I wish TOTP supported a PIN. That omission makes it pretty useless for high security applications.
You want your secret to have a secret? Several apps that provide time based one time password storage/management can also be secured with bio-metrics, pins or passwords (Aegis, MS Authenticator). Are you specifically thinking physical tokens only?
If you need to conform with higher NIST assurance levels, a one time password generator needs to have a secret as well.
Commercial solutions support this with challenge/response tokens or PINs. I’d love to see an OSS solution.
Perhaps you could link to these NIST standards?
What do you mean by that? The TOTP standard doesn't specify how (if at all) the client is secured. Besides, the one-time code is used in addition to a password, not as a substitute for one.
Any site that allows you more than a few attempts at a time has already failed.
irrelevant, as OTP time out in minutes?
Doesn't matter. If you keep trying enough random codes for long enough, eventually you'll guess right.
Does it assume that each code is new and not repeated? Because they can def be duplicates.
Still doesn't matter. If you're guessing at random, you have a roughly one in a million chance of getting it right each time.
(Actually, the odds are a little better than that, because most TOTP implementations mitigate clock skew by allowing clients to enter codes from a "window" of a few minutes surrounding the current time.)
There is a window of validity (often 30s), and a window of forgiveness (often +- 30s, so the same code will work for 90s), but the standards require only one attempt per window which renders TFA's claim pointless. Except for poor implementations, of course. And once in a million windows it may be 000000 (with 30s windows that will take 347 days assuming even distribution)
What I'm curious about is... If I collect 10 of your OTP keys, can I brute force the secret the generates them?
Not practically. The standard requires that the secret contain at least 128 bits of randomness, and recommends more.
no