Skip to content

Case study of an attack on SCRAM

Nelson Vides edited this page Feb 4, 2021 · 3 revisions

Suppose an attacker Mallory attempts to impersonate Alice. So upon establishing a connection with the server, Mallory sends:

clientFirst() -> <<"n,,n=Alice,r=fyko+d2lbbFgONRv9qkxdawL">>.

The server recognises Alice, and hence answers with its own nonce, and Alice's salt and iteration count (first issue, server has revealed the salt).

serverFirst() -> <<"r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096">>.

Mallory is now able to prepare dictionaries for a Rainbow attack, knowing already the salt and iteration count.

Case 1: MitM attack

Was Mallory to be actually a MitM, and the previous two messages be legit messages exchanged between Alice and the Server, then when Alice sends

clientFinal() -> <<"c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=">>.

Mallory is now able to mount offline attacks, as it has now the proof and all the transient data to try password guesses.

AuthMessage := username,client-nonce,salt,ic,server-nonce
do
    password'        := NextPasswordGuess()
    SaltedPassword'  := Hi(password', salt, ic)
    ClientKey'       := HMAC(SaltedPassword', "Client Key"))
    StoredKey'       := H(ClientKey')
    ClientSignature' := HMAC(StoredKey', AuthMessage)
    ClientProof'     := ClientKey' XOR ClientSignature'
while ClientProof != ClientProof'

This is now only made more difficult by the challenge, that might make the testing way slower, but more often than not, the passwords are human-made low-entropy passwords, so there won't be a lot of testing here.

Channel binding

If channel binding was used, then the best Mallory can do is not to relay the message Alice just sent with the proof back to the server. If Mallory was to do so, the server would notice that a MitM was in course and can —should!— then block authentication to this user until the account is legitimately recovered some other trusted way.

Was Mallory to then cut the connection, the server wouldn't be sure why the connection was dropped and Mallory would be left with enough data to mount an offline attack.

Case 2: Active impersonation attack

If Mallory was not a MitM, then she has to compose the client's final message. Of it, only the proof is unknown, so let's suppose Mallory only tries to guess:

clientFinal() -> <<"c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=base64encodedguess">>.

Here, as Mallory is actively trying to impersonate Alice, channel binding wouldn't make any difference. So we're then left with the attempted proof. What can Mallory learn from this?

Timing attacks

An important question to ask is whether is is sensitive to timing attacks. The answer is that not really, because the next time Mallory tries to guess a different proof, as the nonces are all different, the valid proof will be all different and hence matching on any first bytes tells Mallory nothing useful for future attempts. This is good news.

If the server was to compare instead StoredKeys from the given proof, then the server would exor the given proof with the signature the server alone knows from the secret StoredKey and the variable AuthMessage. So timing attacks on the StoredKeys comparisons wouldn't also work, as the StoredKey calculated from the given client's proof is also randomly variable.

serverFinal() -> <<"v=rmF9pqV8S7suAoZWja4dJRkFsKQ=">>.
Clone this wiki locally