From 4395d1467812918a8b0ec0019a7b154d9a47dbfa Mon Sep 17 00:00:00 2001 From: Romke van Dijk Date: Sun, 22 Dec 2024 19:27:19 +0100 Subject: [PATCH] Adding keybag2hashcat --- tools/keybag2hashcat.py | 171 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 tools/keybag2hashcat.py diff --git a/tools/keybag2hashcat.py b/tools/keybag2hashcat.py new file mode 100644 index 000000000..83da25c5e --- /dev/null +++ b/tools/keybag2hashcat.py @@ -0,0 +1,171 @@ +import argparse +import logging +import sys + +__VERSION__ = '1.0.0' + +# Set up logging +logger = logging.getLogger("keybag_logger") +handler = logging.StreamHandler() +formatter = logging.Formatter('%(message)s') +handler.setFormatter(formatter) +logger.addHandler(handler) + +class Keybag: + def __init__(self, file_obj): + self.size = 0 + self.uuid = '' + self.version = 0 + self.type = 0 + self.hmackey = '' + self.wrap = 0 + self.salt = '' + self.iterations = 0 + + + self._read_header(file_obj) + self.class_keys = self._read_class_keys(file_obj) + + def _read_header(self, file_obj): + while True: + tag = file_obj.read(4).decode('ascii') + if tag == 'DATA': # DATA + self.size = int.from_bytes(file_obj.read(4), byteorder='big') + else: + length = int.from_bytes(file_obj.read(4), byteorder='big') + data = file_obj.read(length) + + if tag == 'VERS': # VERS + self.version = int.from_bytes(data, byteorder='big') + elif tag == 'TYPE': + self.type = int.from_bytes(data, byteorder='big') + elif tag == 'UUID': + if not self.uuid: + self.uuid = data.hex() + else: + file_obj.seek(-length - 8, 1) + break + elif tag == 'HMCK': + self.hmackey = data.hex() + elif tag == 'WRAP': + self.wrap = int.from_bytes(data, byteorder='big') + elif tag == 'SALT': + self.salt = data.hex() + elif tag == 'ITER': + self.iterations = int.from_bytes(data, byteorder='big') + + def _read_class_keys(self, file_obj): + class_keys = {} + + for x in range(0, 10): + stop = False + while stop != True: + tag = file_obj.read(4).decode('ascii') + length = int.from_bytes(file_obj.read(4), byteorder='big') + data = file_obj.read(length) + # new class key + if tag == 'UUID': + if class_keys.get(x): + if class_keys[x].get('UUID'): + file_obj.seek(-length - 8, 1) + stop = True + else: + class_keys[x] = {} + else: + class_keys[x] = {} + if tag == 'WRAP' or tag == 'CLAS' or tag == 'KTYP': + class_keys[x][tag] = int.from_bytes(data, byteorder='big') + else: + class_keys[x][tag] = data.hex() + if file_obj.tell() > self.size: + stop = True + return class_keys + + + def print_keybag(self): + logger.debug(f'SIZE: {self.size}') + logger.debug(f'VERSION: {self.version}') + logger.debug(f'TYPE: {self.type}') + logger.debug(f'UUID: {self.uuid}') + logger.debug(f'HMACKEY: {self.hmackey}') + logger.debug(f'SALT: {self.salt}') + logger.debug(f'ITERATIONS: {self.iterations}') + for x, class_key in self.class_keys.items(): + logger.debug(f'{x}:') + for key, value in class_key.items(): + logger.debug(f' {key}: {value}') + +def main(): + # Create the argument parser + parser = argparse.ArgumentParser(description="Process a keybag file with a specified UID.") + + # Add the UID argument + parser.add_argument( + '--uid', + type=str, + required=True, + help="Specify the device UID." + ) + + # Add the keybag file argument + parser.add_argument( + 'keybag', + type=str, + help="Path to the keybag file." + ) + + # Add the debug flag + parser.add_argument( + '--debug', + action='store_true', + help="Enable debug logging." + ) + + # Parse the arguments + args = parser.parse_args() + + if args.debug: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.WARNING) + + # Access the arguments + uid = args.uid[0:32] + keybag_path = args.keybag + + logger.debug(f'keybag2hashcat - version {__VERSION__}') + + with open(keybag_path, 'br') as keybag_file: + kb = Keybag(keybag_file) + kb.print_keybag() + if not kb.version: + logger.error('Unable to detect version of keybag, exiting.') + sys.exit(1) + if not kb.salt: + logger.error('Unable to detect salt, exiting.') + sys.exit(1) + if not kb.iterations: + logger.error('Unable to detect iterations, exiting.') + sys.exit(1) + if not kb.version in [3, 4]: + logger.error(f'This script has not been tested with version {kb.version}.') + sys.exit(1) + if not kb.class_keys: + logger.error(f'Unable to parse class keys, exiting.') + sys.exit(1) + classkey1 = 0 + for x, class_key in kb.class_keys.items(): + if class_key.get('WRAP') == 3: + class_type = class_key.get('CLAS') + if class_type == 1 or class_type == 33: + classkey1 = class_key.get('WPKY') + + if not classkey1: + logger.error(f'Unable to find a classkey of class NSFileProtectionComplete.') + logger.error(f'You could try to get another class key, make sure it is ktyp 0 and wrap 3.') + exit(1) + print(f'$uido${uid}${kb.salt}${kb.iterations}${classkey1}') + + +if __name__ == "__main__": + main()