Published on 18.05.2021
Don't encrypt user passwords in reversible ways
But when you do, don't get caught

During the audit of a third party application, a look into the user database table revealed a password column containing base64 encoded data with different lengths. Decoding the base64 returned only arbitrary binary data, so it did not contain encoded cleartext passwords. But the different lengths raised the assumption, that the passwords were not hashed in a non reversible way but were encrypted in a reversible way.

How are password stored by state of the art

If you write an application which is an endpoint, there should be only one way to store user passwords: as a salted hash, calculated with an irreversible brute-force search resistant hash function. Such hash functions produce fixed length hashes, independent of the length of the hashed password. The salt, sometimes stored in the same database field concatenated to the hash, usually is also generated randomly with fixed length. Storing passwords in a secure way normally results not in password fields with varying length. Storing passwords reversibly encrypted does.

Stored passwords with different length should be a red flag
Why is reversible password storage a bad idea?

There exist mainly two different attack scenarios from reversibly stored passwords: using it to attack the vulnerable application itself, or exploit possible password reuse in other applications.

Both attacks imply access to the stored passwords. This could be eg the result of an SQL injection vulnerability, or access to a backup or to the database itself. To use the password, the attacker needs them as cleartext. If the passwords are stored as (salted) hashes with a suitable algorithm, the only way to reverse a hash is bruteforcing possible cleartext password. If the password is sufficiently complex, this will not lead to success.

But if stored reversible encrypted, the attack can simply get a cleartext password by decrypting it, independent of the complexity of the password.

Besides these attacks: many people use personal private information as part of their password. This is generally seen as bad practice, but this does not mean that the data privacy of such users should be violated.

Get a proof of concept

Since we had the assumption that the passwords were stored in a reversible way, the goal was to write a proof of concept to reverse them to plaintext. In this concrete example, we had access to the binaries of an application server and of a corresponding fat client. Both contained the code not only to encrypt the passwords but also to decrypt them. Decompiling the binary ended with code similar to this:

public string Decrypt(string encryptedPassword, string secret) 	{
	byte[] data = Convert.FromBase64String(encryptedPassword);
	PasswordDeriveBytes secretDeriveBytes = new PasswordDeriveBytes(
		secret, 
		new byte[13]{113, 41, 23, 41, 12, 34, 32, 23, 14, 12, 21, 58}
		);
	byte[] bytes = this.Decrypt(data, secretDeriveBytes.GetBytes(32), secretDeriveBytes.GetBytes(16));
	return Encoding.Unicode.GetString(bytes);
}

The rest was copy and paste. Security by obscurity is in general no good idea.

As a side note: the call secretDeriveBytesGetBytes(16) returns the initialization vector (IV). The IV should ensure that two equal plaintexts do not result in the same cipher text. Because of the way the IV was derived, it was static (since the secret is static). As a result, two identical user passwords result in the same cipher text.

How to fix

Developing a fix is relatively easy: you just have to store the passwords as hashed salts. Since you can decrypt the plaintext passwords, you can easily migrate from reversible encrypted passwords to irreversible hashes. This may seem obvious, but there are other ways to screw up password storing, where fixing is not trivial, since you have no direct access to the plaintext passwords¹.

Okay, and what was the vulnerable application?

The vulnerability was send to the vendor. By the time of this post, the vendor already communicated the successful development of a (currently not published) fix. If the gods of responsible disclosure are gracious, maybe a CVE can be linked to this article in near future².