Background
In the article How to Hack KeePass, the author used keepass2john on a KeePass database to extract a hash of its master password.
For the database CrackThis.kdb
the extracted hash was (line breaks added by me)
$keepass$*
1*
6000*
0*
dfb86938fedd22b8235c4de4b02e5bb3*
c292fa502cbb0e912f30c1e1b6829a2c04ebe30477cb269d462fbbbeeb9360a6*
b85fb0e07f3edbd4123db2c829b79355*
6d4857a728900d4da4fad66999233e5c647e5354330d31be3a47bbdd102a5ca7*
0*
CrackThis
Which Hash is Actually Stored?
To understand the risks of storing such a hash, we have to know more about the hash. How is it computed and what was the input? What are the different values separated by *
?
From the KeePass website I learned that KeePass executes something like the following routine.
var password = readPasswordFromUser();
var hash1 = SHA256(password);
var salt = getSaltFromDatabase();
var iterations = getIterationsFromDatabase(); // default 6'000
var hash2 = AES_KDF(hash1, salt, iterations); // key derivation function
The database is then encrypted/decrypted using AES with the key hash2
.
Back to our extracted hash from above. There are three possibilities.
The stored/extracted hash is
hash2
. Sincehash2
is used as the decryption key, this would be a disaster. I think we can exclude this one.The stored/extracted hash is
hash1
. Storinghash1
renders the key derivation function (KDF) useless. The KDF is there to slow down bruteforce attacks. Withhash1
an attacker doesn't have to deal with the slow KDF, but only with SHA256.I misunderstood something and the extracted hash is neither
hash1
norhash2
.
Main Question
Why does the KeePass database contain an unencrypted hash of its master password at all? To decrypt the database we don't need a hash, we just decrypt using a given password. If the password was wrong then the encrypted data will be nonsense, no problem.
If the stored hash is only there to prevent dealing with nonsense data when the user entered a wrong password by accident, then I don't get why they didn't use a less risky way. For instance, wouldn't it be safer to compute a checksum (not necessarily cryptographic, for instance CRC) of the database and encrypt checksum+database. On decryption, the checksum can be verified against the database.