Created
May 3, 2018 20:17
-
-
Save joncardasis/ca7b267feffa06f7e277f1851ca54650 to your computer and use it in GitHub Desktop.
A python script to check if a FileVault password has ever been used on macOS before.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os, sys | |
import re | |
import base64 | |
import hashlib | |
import plistlib | |
from binascii import unhexlify | |
class FVPasswordManager(object): | |
PL_SALTED_HASH_ARRAY_KEY = 'SALTED-SHA512-PBKDF2' | |
PL_ENTROPY_KEY = 'entropy' | |
PL_SALT_KEY = 'salt' | |
PL_ITERATION_KEY = 'iterations' | |
DK_LEN = 128 # Length of derived key for sha512 | |
@staticmethod | |
def _get_user_plist_location(user_shortname): | |
return '/var/db/dslocal/nodes/Default/users/{}.plist'.format(user_shortname.lower()) | |
@staticmethod | |
def pwd_was_used_previously(user_shortname, password): | |
''' | |
Checks if the provided password has ever been used before by checking local filevault | |
dscl records. (Records use SALTED-SHA512-PBKDF2 algorithm in macOS 10.8 and higher.) | |
See /var/db/dslocal/nodes/Default/users/<username>.plist files. | |
@param user_shortname: A string of the user account | |
@param password: A plain-text string password to check | |
@return: True if the filevault password has been set as a filevault password before | |
@raise Exception: Exception raised if failed to parse an existing dictionary | |
''' | |
user_plist = FVPasswordManager._get_user_plist_location(user_shortname) | |
raw_data = os.popen('sudo defaults read {plist} ShadowHashData 2> /dev/null'.format(plist=user_plist)).read() | |
plist_datum = re.findall(r'\<([0-9a-f ]+)\>', raw_data) | |
for plist_data in plist_datum: | |
cleaned_plist_data = plist_data.replace(' ', '') | |
plist_str = os.popen('echo "{}" | xxd -r -p | plutil -convert xml1 - -o - 2> /dev/null'.format(cleaned_plist_data)).read() | |
try: | |
hash_dict = plistlib.readPlistFromString(plist_str)[FVPasswordManager.PL_SALTED_HASH_ARRAY_KEY] | |
# Obtain salt and entropy in their base64 encoded formats | |
entropy_64encoded = hash_dict[FVPasswordManager.PL_ENTROPY_KEY].asBase64(maxlinelength=1000000).strip() | |
salt_64encoded = hash_dict[FVPasswordManager.PL_SALT_KEY].asBase64(maxlinelength=1000000).strip() | |
iterations = int(hash_dict[FVPasswordManager.PL_ITERATION_KEY]) | |
# Decode values and represent as hex | |
entropy_hex = base64.b64decode(entropy_64encoded).encode('hex') | |
salt_hex = base64.b64decode(salt_64encoded).encode('hex') | |
# Get shadow hash and compare with the entropy | |
salt_hex_str = unhexlify(salt_hex) # hashlib needs hex string to be in safe hex representation | |
shadow_hash = hashlib.pbkdf2_hmac('sha512', password, salt_hex_str, iterations, FVPasswordManager.DK_LEN).encode('hex') | |
if shadow_hash == entropy_hex: | |
return True | |
except KeyError as e: | |
raise KeyError("No key '{}' found for {}".format(e.args[0], FVPasswordManager)) | |
except Exception as e: | |
raise e | |
return False |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment