def key_iterator( cls, hive: RegistryHive, node_path: Sequence[objects.StructType] = None, recurse: bool = False ) -> Iterable[Tuple[int, bool, datetime.datetime, str, bool, interfaces.objects.ObjectInterface]]: """Walks through a set of nodes from a given node (last one in node_path). Avoids loops by not traversing into nodes already present in the node_path. Args: hive: The registry hive to walk node_path: The list of nodes that make up the recurse: Traverse down the node tree or stay only on the same level Yields: A tuple of results (depth, is_key, last write time, path, volatile, and the node). """ if not node_path: node_path = [hive.get_node(hive.root_cell_offset)] if not isinstance(node_path, list) or len(node_path) < 1: vollog.warning( "Hive walker was not passed a valid node_path (or None)") return node = node_path[-1] key_path_items = [hive] + node_path[1:] key_path = '\\'.join([k.get_name() for k in key_path_items]) if node.vol.type_name.endswith(constants.BANG + '_CELL_DATA'): raise RegistryFormatException( hive.name, "Encountered _CELL_DATA instead of _CM_KEY_NODE") last_write_time = conversion.wintime_to_datetime( node.LastWriteTime.QuadPart) for key_node in node.get_subkeys(): result = (len(node_path), True, last_write_time, key_path, key_node.get_volatile(), key_node) yield result if recurse: if key_node.vol.offset not in [ x.vol.offset for x in node_path ]: try: key_node.get_name() except exceptions.InvalidAddressException as excp: vollog.debug(excp) continue yield from cls.key_iterator(hive, node_path + [key_node], recurse=recurse) for value_node in node.get_values(): result = (len(node_path), False, last_write_time, key_path, node.get_volatile(), value_node) yield result
def _generator(self, syshive: registry.RegistryHive, sechive: registry.RegistryHive): vista_or_later = versions.is_vista_or_later( context=self.context, symbol_table=self.config['nt_symbols']) bootkey = hashdump.Hashdump.get_bootkey(syshive) lsakey = self.get_lsa_key(sechive, bootkey, vista_or_later) if not bootkey: raise ValueError('Unable to find bootkey') if not lsakey: raise ValueError('Unable to find lsa key') secrets_key = sechive.get_key('Policy\\Secrets') if not secrets_key: raise ValueError('Unable to find secrets key') for key in secrets_key.get_subkeys(): sec_val_key = sechive.get_key('Policy\\Secrets\\' + key.get_key_path().split('\\')[3] + '\\CurrVal') if not sec_val_key: continue enc_secret_value = next(sec_val_key.get_values()) if not enc_secret_value: continue enc_secret = sechive.read(enc_secret_value.Data + 4, enc_secret_value.DataLength) if not enc_secret: continue if not vista_or_later: secret = self.decrypt_secret(enc_secret[0xC:], lsakey) else: secret = self.decrypt_aes(enc_secret, lsakey) yield (0, (key.get_name(), secret.decode('latin1'), secret))
def get_secret_by_name(cls, sechive: registry.RegistryHive, name: str, lsakey: bytes, is_vista_or_later: bool): try: enc_secret_key = sechive.get_key("Policy\\Secrets\\" + name + "\\CurrVal") except KeyError: raise ValueError("Unable to read cache from memory") enc_secret_value = next(enc_secret_key.get_values()) if not enc_secret_value: return None enc_secret = sechive.read(enc_secret_value.Data + 4, enc_secret_value.DataLength) if not enc_secret: return None if not is_vista_or_later: secret = cls.decrypt_secret(enc_secret[0xC:], lsakey) else: secret = cls.decrypt_aes(enc_secret, lsakey) return secret
def get_lsa_key(cls, sechive: registry.RegistryHive, bootkey: bytes, vista_or_later: bool) -> Optional[bytes]: if not bootkey: return None if vista_or_later: policy_key = 'PolEKList' else: policy_key = 'PolSecretEncryptionKey' enc_reg_key = sechive.get_key("Policy\\" + policy_key) if not enc_reg_key: return None enc_reg_value = next(enc_reg_key.get_values()) if not enc_reg_value: return None obf_lsa_key = sechive.read(enc_reg_value.Data + 4, enc_reg_value.DataLength) if not obf_lsa_key: return None if not vista_or_later: md5 = MD5.new() md5.update(bootkey) for _i in range(1000): md5.update(obf_lsa_key[60:76]) rc4key = md5.digest() rc4 = ARC4.new(rc4key) lsa_key = rc4.decrypt(obf_lsa_key[12:60]) lsa_key = lsa_key[0x10:0x20] else: lsa_key = cls.decrypt_aes(obf_lsa_key, bootkey) lsa_key = lsa_key[68:100] return lsa_key
def _generator(self, syshive: registry.RegistryHive, sechive: registry.RegistryHive): kernel = self.context.modules[self.config['kernel']] vista_or_later = versions.is_vista_or_later( context=self.context, symbol_table=kernel.symbol_table_name) bootkey = hashdump.Hashdump.get_bootkey(syshive) lsakey = self.get_lsa_key(sechive, bootkey, vista_or_later) if not bootkey: vollog.warning("Unable to find bootkey") return if not lsakey: vollog.warning("Unable to find lsa key") return secrets_key = hashdump.Hashdump.get_hive_key(sechive, 'Policy\\Secrets') if not secrets_key: vollog.warning("Unable to find secrets key") return for key in secrets_key.get_subkeys(): sec_val_key = hashdump.Hashdump.get_hive_key( sechive, 'Policy\\Secrets\\' + key.get_key_path().split('\\')[3] + '\\CurrVal') if not sec_val_key: continue enc_secret_value = next(sec_val_key.get_values()) if not enc_secret_value: continue enc_secret = sechive.read(enc_secret_value.Data + 4, enc_secret_value.DataLength) if not enc_secret: continue if not vista_or_later: secret = self.decrypt_secret(enc_secret[0xC:], lsakey) else: secret = self.decrypt_aes(enc_secret, lsakey) yield (0, (key.get_name(), secret.decode('latin1'), secret))
def _get_subkeys_recursive( self, hive: RegistryHive, node: interfaces.objects.ObjectInterface ) -> Iterable[interfaces.objects.ObjectInterface]: """Recursively descend a node returning subkeys.""" # The keylist appears to include 4 bytes of key name after each value # We can either double the list and only use the even items, or # We could change the array type to a struct with both parts try: signature = node.cast('string', max_length=2, encoding='latin-1') except (exceptions.InvalidAddressException, RegistryFormatException): return listjump = None if signature == 'ri': listjump = 1 elif signature == 'lh' or signature == 'lf': listjump = 2 elif node.vol.type_name.endswith(constants.BANG + "_CM_KEY_NODE"): yield node else: vollog.debug( "Unexpected node type encountered when traversing subkeys: {}, signature: {}" .format(node.vol.type_name, signature)) if listjump: node.List.count = node.Count * listjump for subnode_offset in node.List[::listjump]: if (subnode_offset & 0x7fffffff) > hive.maximum_address: vollog.log( constants.LOGLEVEL_VVV, "Node found with address outside the valid Hive size: {}" .format(hex(subnode_offset))) else: try: subnode = hive.get_node(subnode_offset) except (exceptions.InvalidAddressException, RegistryFormatException): vollog.log( constants.LOGLEVEL_VVV, "Failed to get node at {}, skipping".format( hex(subnode_offset))) continue yield from self._get_subkeys_recursive(hive, subnode)
def get_secret_by_name(cls, sechive: registry.RegistryHive, name: str, lsakey: bytes, is_vista_or_later: bool): enc_secret_key = hashdump.Hashdump.get_hive_key( sechive, "Policy\\Secrets\\" + name + "\\CurrVal") secret = None if enc_secret_key: enc_secret_value = next(enc_secret_key.get_values()) if enc_secret_value: enc_secret = sechive.read(enc_secret_value.Data + 4, enc_secret_value.DataLength) if enc_secret: if not is_vista_or_later: secret = cls.decrypt_secret(enc_secret[0xC:], lsakey) else: secret = cls.decrypt_aes(enc_secret, lsakey) return secret
def list_userassist( self, hive: RegistryHive) -> Generator[Tuple[int, Tuple], None, None]: """Generate userassist data for a registry hive.""" hive_name = hive.hive.cast(self.config["nt_symbols"] + constants.BANG + "_CMHIVE").get_name() if self._win7 is None: try: self._win7 = self._win7_or_later() except exceptions.SymbolError: # self._win7 will be None and only registry value rawdata will be output pass self._determine_userassist_type() userassist_node_path = hive.get_key( "software\\microsoft\\windows\\currentversion\\explorer\\userassist", return_list=True) if not userassist_node_path: vollog.warning( "list_userassist did not find a valid node_path (or None)") return if not isinstance(userassist_node_path, list): vollog.warning( "userassist_node_path did not return a list as expected") return userassist_node = userassist_node_path[-1] # iterate through the GUIDs under the userassist key for guidkey in userassist_node.get_subkeys(): # each guid key should have a Count key in it for countkey in guidkey.get_subkeys(): countkey_path = countkey.get_key_path() countkey_last_write_time = conversion.wintime_to_datetime( countkey.LastWriteTime.QuadPart) # output the parent Count key result = ( 0, (renderers.format_hints.Hex(hive.hive_offset), hive_name, countkey_path, countkey_last_write_time, "Key", renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue()) ) # type: Tuple[int, Tuple[format_hints.Hex, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any]] yield result # output any subkeys under Count for subkey in countkey.get_subkeys(): subkey_name = subkey.get_name() result = (1, ( renderers.format_hints.Hex(hive.hive_offset), hive_name, countkey_path, countkey_last_write_time, "Subkey", subkey_name, renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), )) yield result # output any values under Count for value in countkey.get_values(): value_name = value.get_name() try: value_name = codecs.encode(value_name, "rot_13") except UnicodeDecodeError: pass if self._win7: guid = value_name.split("\\")[0] if guid in self._folder_guids: value_name = value_name.replace( guid, self._folder_guids[guid]) userassist_data_dict = self.parse_userassist_data(value) result = (1, ( renderers.format_hints.Hex(hive.hive_offset), hive_name, countkey_path, countkey_last_write_time, "Value", value_name, userassist_data_dict["id"], userassist_data_dict["count"], userassist_data_dict["focus"], userassist_data_dict["time"], userassist_data_dict["lastupdated"], format_hints.HexBytes(userassist_data_dict["rawdata"]), )) yield result