mricon/totp-cgi

Always mark tokencode as used even if it was the pincode that failed

Closed this issue · 8 comments

There's a potential for token reuse in the current configuration -- due to the fact that pincodes are a later addon to the functionality.

When pincodes are used in the pincode+tokencode configuration, making a typo in the pincode will not mark the tokencode as used, so an attacker can reuse the same tokencode with the correct pincode.

If you're configured to have the pincode encrypt the token seed are you even going to be able to determine if the tokencode was correct/failed to mark it appropriately?

Well, the scenario is when your workstation is compromised. If the attacker can sniff your keystrokes, they know your pincode by observing you type it in, but can't use that knowledge to get elevated access since it requires a one-time totp code. The attacker can just wait until you mistype your pincode and then quickly use the correct pincode with the still-valid tokencode they watched you type.

Ok, I was just trying to figure out the specific attack you're trying to guard against since pincode is checked before token.

Yes, the solution is to always check the tokencode regardless of the success or failure of the pincode check. This will record the token as used.

Which brings me back to how you're going to check the token if you're configured to encrypt the seed with the pincode. Without a successful pincode check you won't be able to get the seed to check the token.

Luckily, we record timestamps, not tokens. :) So, if pincode check fails for any reason and I'm not able to obtain a token, I just record the timestamp in "failed timestamps" and reusing any token that falls within the same timestamp will return an "already used" error.

This issue is mostly handled in 0.5.2, but I will need to enhance it a bit to close another loophole. Currently, there is still a condition that I can think of that would let an attacker replay a token in case the pincode was mistyped -- if the user uses a token not for the current timestamp, but from within the window. E.g. if current acceptable tokencodes are 111111 for timestamp Tpast, 222222 for timestamp Tcurrent, and 333333 for the timestamp Tfuture -- then if the user had typed "badpincode111111" at the timestamp Tcurrent (which we consider good within window), we only invalidate tokencode "222222", therefore letting the attacker authenticate with "goodpincode111111".

There is a simple solution, but it comes with a caveat. We can easily invalidate the timestamps within the back-window, but if we do the same to the timestamps in the forward-window, then users would have to wait until their window expires before they can authenticate again, and that's not acceptable in terms of usability. Nobody is going to wait for a whole minute before they can authenticate again, and that's with the minimal window setting of 1. If the window setting is 3, that means the user has to wait up to 2 whole minutes before their authentication will succeed!

So, we leave this potential attack vector, but it should only occur rarely and will require an unlikely combination of events to succeed -- 1. user must mistype a pincode known to the attacker, 2. the attacker must be able to observe user interaction in real time and have enough time to respond in order to take advantage of this, and 3. there has to be a large enough time-drift between the user's TOTP app and the totpcgi server that would result in the user's tokencode to be in the forward-window.

I can live with that.

Fixed as well as we can in 0.5.3.