During Positive Hack Days V, I made a fast track presentation about eCryptfs and password cracking. The idea came to me after using one feature of Ubuntu which consists in encrypting the home folder directory. This option can be selected during installation or activated later.
If you select this option, nothing changes for the user except that data in his home folder is encrypted. I was interested to know how this process works since the passphrase for decryption is never requested. I discovered that eCryptfs is included in the GNU/Linux kernel and tools called ecryptfs-utils are used to setup the home folder encryption by the Ubuntu distribution.
After reading the code, I discovered how the encryption is performed. First a 16-byte random passphrase is generated. This passphrase will be used with AES-128 to encrypt and decrypt the data in the folder. This passphrase is stored encrypted in the file
The process of encrypting the passphrase is called key wrapping. To generate a wrapping key used to wrap the passphrase, an 8-byte salt and a password are concatenated and given as input to SHA-512. The result is hashed again 65535 times. As shown by the following figure:
The 16 first bytes of the result are the wrapping key. The intermediate result is hashed one more time and the 8 first bytes of this operation represent the signature of the wrapping key. The passphrase is encrypted with the wrapping key using AES-128 and stored with the signature in the wrapped-passphrase file as shown here:
To unwrap the key the process is similar, the salt and the password are hashed 65536 times.
The result is hashed one more time. If the 8-byte signature obtained matches the one stored in the file then eCryptfs detects that the correct wrapping key has been generated. Thus it can unwrap the passphrase for file decryption.
From an adversary’s point of view, to recover the passphrase, the naive approach would be to brute-force the passphrase with encrypted data. However since it is randomly generated over 16-bytes the brute-force approach is not practical. The adversary can also try to brute-force the password used during the key wrapping and thus he would be able to generate the wrapping key and recover the passphrase. He could use precomputed dictionaries or rainbow tables over the signature to recover the password but as a salt is used in the wrapping process this makes such attacks much more difficult.
At this point, I noticed that, for Ubuntu systems, the password used in the wrapping process is directly the login password. This explains why no further information is asked when performing home folder decryption. It means that an adversary who is able to crack the wrapping password will not only obtain the passphrase but also the user password. Next I looked how the salt is generated since it is not stored anywhere in the
wrapped-passphrase file. I finally found in the code that ecryptfs-utils is looking for a salt in the configuration file
If the file does not exist, a default value of
0x0011223344556677 is used. This behavior had already been noted previously.
For a system using eCryptfs, as the configuration file is stored in the (now encrypted) home folder it cannot be found and so the default salt value is used to decrpyt the home folder.
In practical terms this means that, for a system to use this version of eCryptfs the salt value used must be the default value. If not, the initial encryption, using a salt from ~/.ecryptfsrc would never be decrypted correctly as, now unable to find the config file, eCryptfs would apply the default salt at the moment of decryption.
Cleary a precomputed dictionary attack or a rainbow table attack can be mounted against such a system in order to crack the user passwords. To set-up such an attack, I looked at my favorite cracking tool: John the ripper (JTR) and I discovered that the algorithm was already implemented in the 1.8.0-jumbo version. The format is
where the pink value is obviously the salt and the green value is the signature corresponding to the password you want to crack.
I also noticed a Python script
ecryptfs2john.py in JTR which directly read the
wrapped-passphrase file and convert it to the correct format. The algorithm is also implemented in hashcat. As a proof of concept, I computed a dictionary of 14 million signatures based on the famous rock you dictionary. It took me about one month on my personal computer. But with this dictionary it is now possible to reverse a signature with a single look up assuming the password was in the rock you list of password. This dictionnary is available at https://github.com/kudelskisecurity/ecryptfs-dictionary-v1.
Of course we notified the ecryptfs-utils developers about the problem with Ubuntu distributions, a CVE was opened and quickly corrected. They issued a new file format for the wrapped-passphrase file. It now starts with
0x3a02. The salt is generated from
/dev/urandom/ and stored in the same file by default. The old version files are automatically converted after the update and a logoff. To ensure the correction was successfully applied to your system you should check the wrapped-passphrase file. The new file contents should look like this:
Even with the correction the user password is still exposed to bruteforce attack with a randomly generated salt. However the standard Linux password hashing is 5000 iterations of SHA-512 which is easier to crack compared to the 65536 iterations of eCryptfs. As such, and due to the dual use of this password the eCryptfs implementation is a less interesting target for a password cracker. In the future it could be interesting if Argon2, the winner of the Password Hashing Competition, could be used eCryptfs.
My PHDays slides are available here.
Thanks for the paper.
The signature you are selecting is 16 bytes long and the Wrapped passphrase is 32.
You’re right SuperPoney that’s something also I noticed, the key and signature are hexlified that’s why the take twice the size in the file. The wrapped passphrase is the AES ECB encryption of the hexlified version of the passphrase.
Passphrase can be upgraded to v2 with random salt by running: