-
-
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) |
@Rickorn622
You should use Python 3 and give an username as the argument of this script.
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.
Hi Everyone,
I was hoping someone could give me some directions on how to use this script. I am relatively new or let's say first experience with Python. Until now I figured out how to run this script from the command line on macOS 10.15.7.
The usage example, I am not sure I get it:
When I run this script on the command line, first I was getting syntax error with lines 28 and 48, is the f supposed to be there? If I removed them, then I get error message with lines 54 and 62. I am at lost at this point. I am just trying to figure out the user(s).plist hash(s) for a group of users I manage, and for which we currently migrated to Catalina.
Previous versions of macOS, was easy to retrieve the user.plist hash, but with 10.15.x doesnt seem to be as easy.
Any assistance will be greatly appreciated. Thanks