Recovering Lost and Forgotten Keychain Passwords
It is possible to recover Keychain passwords but it is not trivial. Together with other tools, Keysafe can help recover your lost and forgotten Keychain passwords.
I am not an expert at password recovery. What follows are my notes and a guide about how to approach the problem.
How Difficult?
Keychain files are protected by a 24 byte key. This is also known as having a 192 bit key length (24 bytes * 8 bits per byte = 192 bits).
If you try every combination of the 24 bytes, then every keychain file can be unlocked without knowing the original password. So how possible is this approach?
Each byte has 255 possible values and there are 24 bytes. The total number of permutations allowing repetition is 255 to the power of 24, which equals:
- 5.7143170177×10⁵⁷ (scientific notation)
- 5714317017700000000000000000000000000000000000000000000000 (standard form)
This 58 digit number is a clearly a large number but is it a large number in terms of computing? Yes, yes it is.
Trying every combination is called brute force. Trying every combination1 is possible but extremely time consuming. Thankfully the task can be divided across numerous computers. Adding more computers reduces the time required. The real question then becomes how much are you willing to spend on compute power to recover the password?
Unfortunately (thankfully?) the cost of brute force will be prohibitively expensive.
Human Frailty
Brute force trying every combination of the key is beyond the reach of individuals and all but a few organisations.
So how can anyone hope to recover a Keychain password?
The weakness is the where the key comes from. The Keychain’s 24 bytes of seemingly random values are derived from something2, and it is that something that may help us.
The 24 bytes are derived from a password entered by a user.
For Keychain files, the algorithm PDKDF2 is used to transform the variable length password into a constant length 24 bytes. Constant length values are needed for most cryptographic algorithms.
Passwords pose a potential problem, their variable length. A Keychain password can be anywhere between 1 and 255 bytes long. The maximum of 255 bytes is far longer than the limited 24 bytes we struggled with before. So why are passwords easier to guess?
- Passwords are unlikely to be random and are prone to being guessable;
- Passwords are limited to visible characters and typically do not use the full 255 values available per byte;
- Passwords tend to be short because they need to be entered frequently.
So despite the possibility of passwords being as secure as the derived 24 byte key, they rarely are. There are exceptions but for most users these weaknesses apply.
Guessing passwords is well covered by password recovery tools like hashcat and John the Ripper. These are two open source tools that are designed to iterate over millions of possible passwords that humans are more likely to have used.
Practical Example
Let’s work through a practical example of recovering a Keychain password.
This example assumes keysafe
and hashcat
are both available. A list of potential passwords will also be needed. For French speakers, download and expand the latest clem9669_wordlist_small.7z
file from clem9669/wordlists.
On macOS, brew can be used to install these tools:
brew tap miln-eu/miln-eu
brew install miln-keysafe hashcat
Keysafe can help recover lost and forgotten passwords using the -recover
flag.
Use Keysafe’s
-recover
flag to display the master password hash3 of a keychain file:keysafe -recover -path sample.keychain > keychain-hash.txt
A new file called
keychain-hash.txt
will be created. The format of the file isfilename:$keychain$*salt*iv*ciphertext
and it will look something like:sample.keychain:$keychain$*80d0f67a73305c877cb5118402022f5ebe393829*1fd22c876ddd03aa*f278ff91a37bf80406a89ad543aa5ad9ca221d0c3a752506489e6472ae150ed21d39fa8e206b93453ecf45bfc6734209
For
hashcat
the filename in the hash needs to be removed. On Linux and macOS, this edit can be done with the followingsed
command:sed 's/^[^:]*://' keychain-hash.txt > for-hashcat.txt
The new
for-hashcat.txt
file should look like:$keychain$*80d0f67a73305c877cb5118402022f5ebe393829*1fd22c876ddd03aa*f278ff91a37bf80406a89ad543aa5ad9ca221d0c3a752506489e6472ae150ed21d39fa8e206b93453ecf45bfc6734209
Use
hashcat
to try a word list against the Keychain hash:hashcat -m 23100 --keep-guessing for-hashcat.txt ~/Downloads/clem9669_wordlist_small
-m 23100
indicates the hashes are Keychain encrypted;--keep-guessing
flag causes the full set of words to be attempted, even after a seemingly successful password match is found;for-hashcat.txt
path to a file containing the Keychain hashes to attempt;~/Downloads/clem9669_wordlist_small
path to a file containing potential passwords.
Running hashcat
will take time. Every entry in the word list is being tried. Expect the wait to be an hour or more. hashcat
will regularly update the status and provide an estimated completion time:
Session..........: hashcat
Status...........: Running
Hash.Mode........: 23100 (Apple Keychain)
Hash.Target......: $keychain$*80d0f67a73305c877cb5118402022f5ebe393829...734209
Time.Started.....: Mon Feb 19 12:09:48 2024 (13 mins, 25 secs)
Time.Estimated...: Mon Feb 19 13:05:54 2024 (42 mins, 41 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (~/Downloads/clem9669_wordlist_small)
Guess.Queue......: 1/1 (100.00%)
Speed.#2.........: 36670 H/s (8.87ms) @ Accel:256 Loops:124 Thr:1 Vec:4
Recovered........: 0/1 (0.00%) Digests (total), 0/1 (0.00%) Digests (new)
Progress.........: 29921280/123844354 (24.16%)
Rejected.........: 0/29921280 (0.00%)
Restore.Point....: 29921280/123844354 (24.16%)
Restore.Sub.#2...: Salt:0 Amplifier:0-1 Iteration:992-999
Candidate.Engine.: Device Generator
Candidates.#2....: RégionOpérer -> RégionalApparent
Hardware.Mon.SMC.: Fan0: 99%
Hardware.Mon.#2..: Temp: 51c
[s]tatus [p]ause [b]ypass [c]heckpoint [f]inish [q]uit =>
If you are lucky, the word list will contain the Keychain’s password. Successful matches are printed to the screen with the hash first, then the proposed password at the end4:
$keychain$*80d0f67a73305c877cb5118402022f5ebe393829*1fd22c876ddd03aa*f278ff91a37bf80406a89ad543aa5ad9ca221d0c3a752506489e6472ae150ed21d39fa8e206b93453ecf45bfc6734209:<password>
Keychain’s design does not allow tools to be certain that a matching password is correct. You must try the proposed passwords manually.
Beyond Word Lists
If the password is not found, do not worry. The command above runs hashcat
using the default dictionary attack
mode. This is a simple approach which tries each line in the word list file as the password. More sophisticated modes are available.
To use the combinator attack, which mixes and matches combinations of the word list, issue the command:
hashcat -m 23100 -a 1 --keep-guessing for-hashcat.txt ~/Downloads/clem9669_wordlist_small
If this fails, move onto even broader approaches such as the mask and rule attacks.
Weak Passwords
Using the example hashcat
command above, my sample Keychain password was discovered in just twenty minutes. It is a weak password and this has been a good demonstration of why it is considered weak.
Good luck recovering your Keychain password. I hope this helps.
If this helped you, please help ensure this work continues and remains viable.
Theorists will argue that, on average, only half the combinations will need trying. The odds of the right value being the last value tried is unlikely. For us, this makes no practical difference. It is still too expensive. ↩︎
For a System.keychain the key really is just 24 random5 bytes. In this case, try to find a copy of the
SystemKey
file from a back-up. ↩︎The master password hash is not the password but the means by which the password is checked. ↩︎
The match here is replaced with
<password>
but this is a real Keychain hash. Finding the password is left as an exercise. ↩︎Hopefully the random source is cryptographically safe. Even if not it is not truly random, we are unlikely to have weakened the source in time for it to matter now. ↩︎