Breaking into my MacBookPro10,2

I recently found my old 2012 Macbook Pro, but I forgot the password. I decided to try to get in without it. Here's what happened…

Accessing the filesystem

You can boot in single user mode to get into the filesystem (just hold ⌘-S at boot1). I thought it would be more fun to actually log in, so I decided to keep trying…

Resetting the password

To reset a user's password, you can create a new administrator account from which any password can be overwritten2. An easy way is to boot in single-user mode and run:

$ rm /var/db/.AppleSetupDone

which triggers the setup process at the next boot, allowing you to create a new admin account. But at this point, I decided it would be more fun to recover my actual password…

Cracking the password

First, we need to find the hash stored by the OS. I found a nice guide3 on cracking mac passwords: apparently, a user's password hash resides at /Volumes/<hard drive name>/var/db/dslocal/nodes/Default/users/<username>.plist. I grabbed the relevant plist and attempted to extract the hash:

$ sudo defaults read tanner.plist ShadowHashData | tr -dc 0-9a-f | xxd -r -p | plutil -convert xml1 - -o - 2> /dev/null
Property List error: Unexpected character è at line 1 / JSON error: JSON text did not start with array or object and option to allow fragments not set.

No luck. I'm not sure why. I tried another method4 which happened to work:

$ dscl . read tanner.plist dsAttrTypeNative:ShadowHashData | xxd -r -p | plutil -convert xml1 -
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">

Great! Now that we have the hash, we'll use hashcat to crack it. Hashcat expects input in base 16, so we'll need to convert the above hash/salt values which are in base 64. You can perform this conversion any way you want, so I just rewrote a popular script 5:

import base64
import sys
entropy64 = '96kq3nobm2G/jFtdoIsfgzP8m3SCYbGhnI1OdO0rv/ag+8UJOUDUZtTIaH4GMsBLMM8HFtqpH5jAKpJiQhDwmYNWZ64ZAYpV1hUt7UlswDK8HtnTOeZ1x5hICHFkd0ZbGUziMPRGRuNUv64aOC2ihxFODsAkbuZnrvjsfWTCBZ0='
iterations = '36900'
salt64 = 'BQn1fkxpi15tLrI6sVCfZs8rVxxE+0q8W94cwIxj3wA='
entropyRaw = base64.b64decode(entropy64)
entropyHex = entropyRaw.encode("hex")
saltRaw = base64.b64decode(salt64)
saltHex = saltRaw.encode("hex")
print("$ml$%s$%s$%s" %(iterations, saltHex, entropyHex))

I ran the script, and:


Now we're ready for hashcat. I decided to use a dictionary attack with 14 million of the most common passwords:

$ hashcat -a 0 -m 7100 hash.txt rockyou.txt -w 4 --potfile-path hash.pot
hashcat (v5.1.0) starting...

* Device #1: WARNING! Kernel exec timeout is not disabled.
             This may cause "CL_OUT_OF_RESOURCES" or related errors.
             To disable the timeout, see:
OpenCL Platform #1: NVIDIA Corporation
* Device #1: NVIDIA GeForce GTX 960, 499/1998 MB allocatable, 8MCU

Hashfile 'hash.txt' on line 1 ($ml$36...114e0ec0246ee667aef8ec7d64c2059d): Token length exception
No hashes loaded.

Started: Wed Dec 15 05:03:01 2021
Stopped: Wed Dec 15 05:03:01 2021

We got a "token length exception." It turns out that hashcat expects a 64-byte hash, but we gave it a 128-byte one6. We'll just remove 64 bytes from the hash and try again. Since each byte of the hash is represented in hash.txt in hexadecimal as two ASCII characters, each of which occupies one byte, we need to remove 64*2 bytes from hash.txt:

$ truncate -s=-64 hash.txt > hash-truncated.txt
$ hashcat -a 0 -m 7100 hash-truncated.txt rockyou.txt --encoding-from=utf8 --encoding-to=ascii -w 4 --potfile-path ~/hash-truncated.pot
hashcat (v5.1.0) starting...

* Device #1: WARNING! Kernel exec timeout is not disabled.
             This may cause "CL_OUT_OF_RESOURCES" or related errors.
             To disable the timeout, see:
OpenCL Platform #1: NVIDIA Corporation
* Device #1: NVIDIA GeForce GTX 960, 499/1998 MB allocatable, 8MCU

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Applicable optimizers:
* Zero-Byte
* Single-Hash
* Single-Salt
* Slow-Hash-SIMD-LOOP
* Uses-64-Bit

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Watchdog: Temperature abort trigger set to 90c

* Device #1: build_opts '-cl-std=CL1.2 -I OpenCL -I /usr/share/hashcat/OpenCL -D LOCAL_MEM_TYPE=1 -D VENDOR_ID=32 -D CUDA_ARCH=502 -D AMD_ROCM=0 -D VECT_SIZE=1 -D DEVICE_TYPE=4 -D DGST_R0=0 -D DGST_R1=1 -D DGST_R2=2 -D DGST_R3=3 -D DGST_ELEM=32 -D KERN_TYPE=7100 -D _unroll'
Dictionary cache built:
* Filename..: ./rockyou.txt
* Passwords.: 14329857
* Bytes.....: 139921497
* Keyspace..: 14329850
* Runtime...: 7 secs

Session..........: hashcat
Status...........: Cracked
Hash.Type........: macOS v10.8+ (PBKDF2-SHA512)
Hash.Target......: $ml$36900$0509f57dbc698b5e6d2eb23ab150b066cf2b571c4...10f099
Time.Started.....: Wed Dec 15 23:37:35 2021 (1 min, 18 secs)
Time.Estimated...: Wed Dec 15 23:38:53 2021 (0 secs)
Guess.Base.......: File (./rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:     3385 H/s (268.08ms) @ Accel:512 Loops:128 Thr:64 Vec:1
Recovered........: 1/1 (100.00%) Digests, 1/1 (100.00%) Salts
Progress.........: 262144/14329850 (1.83%)
Rejected.........: 0/262144 (0.00%)
Restore.Point....: 0/14329850 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:36864-36899
Candidates.#1....: 123456 -> rayburn1
Hardware.Mon.#1..: Temp: 69c Fan: 17% Util:100% Core:1430MHz Mem:3004MHz Bus:16

Started: Wed Dec 15 23:37:20 2021
Stopped: Wed Dec 15 23:38:55 2021

We recovered the password in just over a minute!


To prevent leaking the recovered password, I flipped bits in both the salt and the hash. However, I used a well-known password dictionary and provided full output from hashcat, which means that the recovered password lies somewhere in the dictionary between the shown candidates (unless I'm lying). If I'm not, then my password could be recovered by hashing the candidates until a hash is found with low edit distance from the above hash. Happy hunting!


BTC Address: bc1qlpu2c7ltxrz5fd2a977ywedlp9u4lvukvluc3d