Dubious security vulnerability: Discovering the salt

A security vulnerability report came in that went something like this:

The XYZ component has a feature where it can cache the password used to access a network resource. It encrypts this password with the Crypt­Protect­Data function, using the default options so that only the user who encrypted the data can decrypt it. It then saves this encrypted password in a file that is accessible only to the user.

The security vulnerability is that when the component encrypts the data, it uses a predictable entropy, namely the name of the network resource. This means that any program running as the user can open the file that has the encrypted password, pass the name of the network resource as the entropy, and then call the Crypt­Unprotect­Data function to decrypt the password.

The XYZ component needs to use an entropy that cannot be predicted. Otherwise, any program running as the user can extract the password.

When you protect data with the Crypt­Protect­Data function, you can optionally pass some additional data that will be used as part of the encryption key. The Crypt­Protect­Data function calls this extra data entropy, but a more common name for it is salt.

It is not important that the salt be unpredictable. What's important is that the salt is different for each network resource.

The salt serves multiple purposes.

One use for the salt is to prevent someone from taking an encrypted password from one network resource and applying it to another network resource. The idea here is that somebody might get their hands on the file, and even though they cannot decrypt the password, they can copy the encrypted password from the entry for one network resource and paste it into the entry for another network resource, trying to take advantage of the fact that people often reuse passwords.¹ Alternatively, the attacker can expend effort to decrypt one password, and once they do, they can look for the same encrypted data elsewhere and know that they have the password for that other resource as well.

Salting the encryption with the name of the network resource foils this trick. Since each password is encrypted with a different salt, if you take an encrypted password from one network resource and try to decrypt it for another network resource, the decryption will fail because the salt (and therefore the decryption key) does not match.

Another purpose for the salt is to make it more computationally expensive to precalculate passwords. A common way of attacking a stolen password database is to take a dictionary of possible passwords, and encrypt each individual password. You then invert the table so that the encrypted password is the lookup key and the decrypted password is the value.

Building this giant table takes a long time, but once you have it, you can attack a large number of passwords at once because you can just take every stolen encrypted password and look it up in the table. If it's in the table, then you have the decrypted password almost immediately.

The salt foils this attack: Since each password is encrypted with a different salt, somebody who wants to mount a reverse-lookup attack would have to have multiple reverse lookup tables, one for each salt. And if you give each password a different salt, then the reverse-lookup attack completely dissolves, because your reverse-lookup table is good for attacking only one password.

Another clue that the salt is not sensitive information is the fact that on classic Unix systems, the master password database /etc/passwd is readable by everyone. It encrypts each password with a different salt, and the salt is readable by anyone.

So if you're concerned that some Windows component doesn't do a good job of protecting its salt, then you should be even more concerned that classic Unix systems don't even try to protect the salt at all!

Bonus chatter: This report is even more bogus even if you get past the fact that the salt is easily guessed. Even if the salt were hard to guess, you can still figure out what it is because you can reverse-engineer the code to the XYZ component and see how it calculates the salt. And then you can replicate this calculation in your own rogue program.

The algorithm for generating the salt needs to be deterministic, because you have to be able to generate the same salt that was used to encrypt the data in order to decrypt it. And that algorithm must operate on publically-known data, because it is generating the inputs to the decryption function! If the salt were itself encrypted, then you've begged the question: How do you decrypt the salt? Is it salt all the way down?

Bonus bonus chatter: An attacker wouldn't even need to extract the salt in order to get the password. The weak salt is a complete red herring. Since the XYZ component runs in-process, the decrypted password is already in memory in the same process. So the attacker just needs to load the XYZ component and ask it to connect to the network resource using the cached password. The XYZ component will decrypt the password, and now the decrypted password is in the attacker's process. It just needs to go copy it.

Now, the attacker could spend a lot of effort studying the XYZ component to see where it stores the decrypted password in memory. Or it can just detour the Crypt­Unprotect­Data function! The XYZ component will call Crypt­Unprotect­Data, and that will call the detour. The detour passes the call through to the original function, and then inspects the result. Bingo, instant password.

¹ Or create a new entry for a network resource that the attacker controls, and copy the encrypted password to it. If you can convince the user to connect to your rogue server, then you will receive the decrypted password.

Comments (22)
  1. kantos says:

    This reminds me of the sort of things you get a lot from Security Theater Auditors that don’t know what they are doing and are just hired to shift liability.

      1. CarlD says:

        Just. Wow. Thanks for that link!

      2. Wayne says:

        That is the scariest thing I Read in a long time.  I wish I could unread it.

        1. cheong00 says:

          Agreed. I wonder how that auditor could stay in the industry that long (assuming he didn’t lie about this), and not be fired when the mail is CC-ed to the CTO.

  2. Mason Wheeler says:

    Why are these passwords being encrypted and decrypted in the first place, rather than hashed?

    1. Because the network resource doesn’t let you sign in with a userid and a hash.

  3. bigger question… with the performance abilities to brute force password attack (hashcat), which can probably be given a specific salt / entropy… does the encryption use effort based algorithms or linear time algorithms? I suspect the latter (I generally tend to assume the worst when it comes to security), which then becomes the actual weakness

    1. Klimax says:

      Pretty sure you have mixed up encryption and hashing in your question. (only hashing has difficulty algorithms) Second, I don’t think it is applicable. Without getting sufficiently high level access there is no access to encrypted password (and once there, you don’t really need it anymore) or you get code running in correct process under correct account and then see this article.

  4. Colin says:

    “And if you give each password a different salt, then the reverse-lookup attack completely _dissolves_”

    Pun intended? :)

  5. Stephen Touset says:

    Raymond, you are confusing a salt with an initialization vector or nonce. Salts apply to password hashing, and are not relevant to symmetric encryption; CryptProtectData is a symmetric encryption function and not a password hash. The purpose of an IV in this scenario is vastly different, and has nothing to do with rainbow tables.

    It’s hard to tell exactly what that API actually does under the hood because its cryptographic guarantees don’t seem to be documented (which is an extraordinarily poor idea for an encryption function), but if it’s using *only* the optional entropy parameter as the value of its IV, it’s entirely plausible then it’s highly plausible there *is* a security vulnerability here, if not the one originally reported. On the other hand, if the optional entropy is just AAD passed to an AEAD cipher, then using the name of the network resource here is completely acceptable.

    Either way, the lack of documentation about what security guarantees that function actually provides makes it a cryptographic landmine.

  6. alexi says:

    I apoliogise if I am trumpeting my ignorance to all and sundry, but doesn’t this type of “vulnerability” stem from a confusion between “salting” (protection against precomputed dictionaries) versus “key stretching” (protection against brute force attacks)?

  7. Yukkuri says:

    I love these dubious vulnerability posts :D

  8. Antonio Rodríguez says:

    The problem has a difficult solution if you need to provide the clear password to the remote server. You could (and, in fact, you *should*) generate a different, pseudo-random salt for every password (I use the hash of the concatenation of the current timestamp with a short pseudo-random string). But you have to store it along with the encrypted password, and if you use a two-way function, attackers would have enough information to decrypt the password. Encrypting a password with a two-way function only dissuade occasional lookers, and is never a serious security measure. No hash, no security: even the weakest hash is (marginally) more secure than the most complex reversible algorithm.

    Alternatively, you could ask the user for a “master password” and use it as the salt (without storing it on disk, of course). But you would need to ask the user for the master password at least once for every session a network connection is made, and that would defeat the purpose of storing the password. It would only make a difference if the software made several connections to different servers (the user would have to enter the password only once instead of several times), but that is an unlikely scenario.

    See that I’m considering that the process’ private memory is safe, and thus I’m free to store plain text passwords there (but not on disk). If you need to make sure you don’t store plain passwords even in private memory, the rules change completely, and one-way hashes are mandatory.

    1. Medinoc says:

      If you have a master password, why use it as salt rather than as base input for deriving the key to decrypting the password? (of course it still needs to be accompanied by salt; personnally I’d store this salt in plaintext alongside the encrypted password)

  9. Harry Johnston says:

    I may be mistaken, but I think that CryptProtectData already uses a master password. (Specifically, the user’s logon password.)

  10. Ray Koopa says:

    “Bonus bonus chatter”? It really is christmas :)

  11. Petr Kadlec says:

    OK, I am confused about the “classic Unix” remark. OK, there was a time when Unix systems stored password _hashes and salts_ in world-readable /etc/passwd. Nowadays, publicizing _hashes_ is considered a bad idea (especially when used with an outdated hashing algorithm, among others limiting the password to eight characters), so Unix systems moved _hashes and salts_ to non-readable /etc/shadow. Was there a time when hashes were private, while salts public? Or, why say “the salt is readable by anyone”?? It feels like the whole two paragraphs are just a red herring. Also, I’d say the terminology here is a bit mixed. “Salt” is generally used for (password) _hashing_, not password _encryption_. For encryption, we typically have initialization vectors, nonces, and tweaks.

    (Even though when discussing “dubious security vulnerabilities”, it is difficult to distinguish “this is wrong because the original idea is wrong” from “this explanation of the original idea is wrong”, it may be just a matter of taste/opinion.)

    1. Yes, I meant super-classic Unix, where the /etc/passwd file was world-readable. (Because that’s the Unix I had when I grew up.)

      And yes, I’m confusing salts with nonces. Encryption is not my field of expertise.

      1. Steve says:

        These days /etc/passwd is still world-readable, and has to be such that numeric user-IDs can be converted to usernames, and vice-versa.

        But (salted) hashes are now stored in a non-readable file. I think the parent was asking whether there was ever a time when the hashes were private but the salts were public, and I suspect there never was such a time. But it doesn’t spoil your illustration :)

      2. Jack says:

        Very old Unix (including Version 7 and the like) did encrypt passwords rather than hash them, as far as I’m aware.

        1. Harry Johnston says:

          If I remember rightly, they used an encryption algorithm to generate a hash. The password wasn’t encrypted, it was used as the encryption key.

Comments are closed.

Skip to main content