def load_interfaces(reg_file): try: reg_handle = Registry(reg_file) int_keys = reg_handle.open('Microsoft\\WlanSvc\\Interfaces') except Exception as e: print( "I could not open the specified SOFTWARE registry key. It is usually located in \Windows\system32\config but is locked by the OS." ) print("Error : ", str(e)) return {} profile_lookup = {} for eachinterface in int_keys.subkeys(): if len(eachinterface.subkeys()) == 0: continue for eachprofile in eachinterface.subkey("Profiles").subkeys(): profileid = [ x.value() for x in eachprofile.values() if x.name() == "ProfileIndex" ][0] metadata = eachprofile.subkey("MetaData").values() for eachvalue in metadata: if eachvalue.name() == "Channel Hints": channelhintraw = eachvalue.value() hintlength = struct.unpack("I", channelhintraw[0:4])[0] name = channelhintraw[4:hintlength + 4] profile_lookup[str(profileid)] = name return profile_lookup
def load_reg_history(self, path_to_reg): reg_handle = Registry(path_to_reg) for eachsubkey in reg_handle.open( r"Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\Unmanaged" ).subkeys(): reg_mac = eachsubkey.value("DefaultGatewayMac").value() if not reg_mac: continue BSSID = b':'.join( codecs.encode(reg_mac[i:i + 1], "hex") for i in range(0, 6)).decode().upper() SSID = eachsubkey.value("FirstNetwork").value() ProfileGuid = eachsubkey.value("ProfileGuid").value() nettype, first, last = resolver.get_profile_info( reg_handle, ProfileGuid) location = None if nettype == "Wireless": if SSID in self.Locations.ap_ssids: location = self.Locations.best_ssid_location(SSID) elif BSSID in self.Locations.ap_bssids: location = self.Locations.best_bssid_location(BSSID) assert not isinstance(location, list) if location: self.data.append( Event(first, location, f"{SSID} {BSSID}-First-Connect")) self.data.append( Event(last, location, f"{SSID} {BSSID}-Last-Connect")) return
def main(): args: Type[DumpWindowsSecretsFromRegDumpsArgumentParser. Namespace] = DumpWindowsSecretsFromRegDumpsArgumentParser( ).parse_args() boot_key: bytes = get_boot_key(lsa_registry=Registry( BytesIO(args.system_dump_path.read_bytes())), from_root=True) domain_cached_credentials, policy_secrets = ( None, None) if not args.security_dump_path else dump_lsa_secrets( security_registry=Registry( BytesIO(args.security_dump_path.read_bytes())), boot_key=boot_key) sam_entries: Optional[list[SAMEntry]] = dump_sam_secrets( sam_dump=args.sam_dump_path.read_bytes(), boot_key=boot_key) if args.sam_dump_path else None print( get_secrets_output_string( sam_entries=sam_entries, domain_cached_credentials=domain_cached_credentials, policy_secrets=policy_secrets))
def registry_wifi_to_BSSID(wifi_name, reg_path = r"c:\Windows\system32\config\SOFTWARE"): reg_handle = Registry(reg_path) subkeys = reg_handle.open(r"Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\Unmanaged").subkeys() for eachkey in subkeys: if eachkey.value("FirstNetwork").value().lower() == wifi_name.lower(): reg_mac = eachkey.value("DefaultGatewayMac").value() BSSID = b':'.join(codecs.encode(reg_mac[i:i+1],"hex") for i in range(0,6)).decode().upper() return BSSID
def getCryptUsername(sSoftware, sSID): with open(sSoftware, 'rb') as oFile: oReg = Registry(oFile) oRegKey = oReg.open( 'Microsoft\\Windows\\CurrentVersion\\Authentication\\Credential Providers\\{D6886603-9D2F-4EB2-B667-1971041FA96B}' ) for oKey in oRegKey.subkeys(): if oKey.name() in sSID: return oKey.subkey('UserNames').subkeys()[0].name() return '<Unknown>'
def registry_all_wireless(reg_path = r"c:\Windows\system32\config\SOFTWARE"): reg_handle = Registry(reg_path) subkeys = reg_handle.open(r"Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\Unmanaged").subkeys() result = [] for eachkey in subkeys: name = eachkey.value("FirstNetwork").value() reg_mac = eachkey.value("DefaultGatewayMac").value() if not int.from_bytes(reg_mac, "big"): continue BSSID = b':'.join(codecs.encode(reg_mac[i:i+1],"hex") for i in range(0,6)).decode().upper() result.append( (name,BSSID) ) return result
def get_security_policy_secrets( security_registry: Registry, policy_secrets_decryption_key: bytes, use_new_style: bool = True ) -> Dict[str, bytes]: security_policy_secret_keys = ( security_policy_key for security_policy_key in security_registry.open(r'Policy\Secrets').subkeys() if security_policy_key.name() != 'NL$Control' ) secret_name_to_secret_data: Dict[str, bytes] = dict() for security_policy_secret_key in security_policy_secret_keys: for value_type in ('CurrVal', 'OldVal'): encrypted_secret_data = security_policy_secret_key.subkey(value_type).value('(default)').raw_data() if not encrypted_secret_data: continue if use_new_style: secret_data = LSASecret.from_bytes( data=encrypted_secret_data ).blob(decryption_key=policy_secrets_decryption_key).secret else: raise NotImplementedError secret_name_to_secret_data[ security_policy_secret_key.name() + ('_history' if value_type == 'OldVal' else '') ] = secret_data return secret_name_to_secret_data
def get_boot_key(lsa_registry: Registry, from_root: bool = False): return transform_untransformed_boot_key( untransformed_boot_key=bytes.fromhex(''.join( lsa_registry.open(boot_key_path.name if not from_root else str( boot_key_path))._nkrecord.classname() for boot_key_path in BOOT_KEY_PARTS_PATHS)))
def load_registry_sids(reg_file): """Given Software hive find SID usernames""" sids = {} profile_key = r"Microsoft\Windows NT\CurrentVersion\ProfileList" tgt_value = "ProfileImagePath" try: reg_handle = Registry(reg_file) key_handle = reg_handle.open(profile_key) for eachsid in key_handle.subkeys(): sids_path = eachsid.value(tgt_value).value() sids[eachsid.name()] = sids_path.split("\\")[-1] except Exception as e: print(str(e)) return {} print("Loaded SIDS", sids) return sids
def get_encrypted_policy_secrets_encryption_key( security_registry: Registry) -> tuple[bytes, bool]: """ :param security_registry: :return: """ try: return security_registry.open(r'Policy\PolEKList').value( '(default)').raw_data(), True except RegistryValueNotFoundException: pass try: return security_registry.open(r'Policy\PolSecretEncryptionKey').value( '(default)').raw_data(), False except RegistryValueNotFoundException: pass # TODO: Add a proper exception. raise Exception
def load_interfaces(reg_file): try: reg_handle = Registry(reg_file) except Exception as e: log.info("I could not open the specified SOFTWARE registry key. It is usually located in \Windows\system32\config. This is an optional value. If you cant find it just dont provide one.") log.info(f"WARNING : {str(e)} ") return {} try: int_keys = reg_handle.open('Microsoft\\WlanSvc\\Interfaces') except Exception as e: log.info("There doesn't appear to be any wireless interfaces in this registry file.") log.info(f"WARNING : {str(e)} ") return {} ssid2bssid = {} for eachsubkey in reg_handle.open(r"Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\Unmanaged").subkeys(): DefaultGatewayMac = eachsubkey.value("DefaultGatewayMac").value() BSSID = b':'.join(codecs.encode(DefaultGatewayMac[i:i+1],"hex") for i in range(0,6)).decode().upper() if BSSID == "00:00:00:00:00:00": continue SSID = eachsubkey.value("FirstNetwork").value() ssid2bssid[SSID]=BSSID profile_lookup = {} for eachinterface in int_keys.subkeys(): if len(eachinterface.subkeys())==0: continue for eachprofile in eachinterface.subkey("Profiles").subkeys(): profileid = eachprofile.value("ProfileIndex").value() try: channelhintraw = eachprofile.subkey("MetaData").value("Channel Hints").value() except: continue hintlength = struct.unpack("I", channelhintraw[0:4])[0] ssid = channelhintraw[4:hintlength+4].decode() bssid = ssid2bssid.get(ssid) if bssid: profile_lookup[str(profileid)] = (bssid,ssid) return profile_lookup
def main(sSOFTWARE, boolOutput = True): with open(sSOFTWARE, 'rb') as oFile: oReg = Registry(oFile) oKey = oReg.open('Microsoft\\Windows\\CurrentVersion\\Authentication\\LogonUI\\NgcPin\\Credentials') arrSIDs = [] for oSubKey in oKey.subkeys(): sSID = oSubKey.name() bData = oSubKey.value('EncryptedPassword').value() arrSIDs.append((sSID, bData)) if boolOutput: print('[+] Found ' + str(len(arrSIDs)) + ' stored NgcPin Password(s) in the registry :\n') oKey = oReg.open('Microsoft\\Windows\\CurrentVersion\\Authentication\\Credential Providers\\{D6886603-9D2F-4EB2-B667-1971041FA96B}') arrUsers = [] for oSubKey in oKey.subkeys(): for oItem in arrSIDs: if oItem[0] == oSubKey.name(): sUsername = oSubKey.subkey('UserNames').subkeys()[0].name() if boolOutput: print('[+] SID : ' + oItem[0]) print('[+] Username : '******'=' * 20) return arrUsers
def dump_sam_secrets(sam_dump: bytes, boot_key: bytes) -> List[SAMEntry]: """ Dump secrets from a SAM registry hive. The hashes are are stored in an double-encrypted format, and must first be decrypted. :param sam_dump: The SAM registry hive as a bytes. :param boot_key: The boot key as bytes. :return: A list of LM and NT hashes for each user in the SAM registry hive of the target host. """ sam_registry = Registry(BytesIO(sam_dump)) hashed_bootkey: bytes = obtain_hashed_bootkey( domain_account_f=DomainAccountF.from_bytes( data=sam_registry.open(r'SAM\Domains\Account').value('F').raw_data() ), bootkey=boot_key ) sam_entries: List[SAMEntry] = [] rid_strings = ( value.name() for value in sam_registry.open(r'SAM\Domains\Account\Users').subkeys() if value.name() != 'Names' ) for rid_string in rid_strings: user_account_v = UserAccountV.from_bytes( data=sam_registry.open(f'SAM\\Domains\\Account\\Users\\{rid_string}').value('V').raw_data() ) lm_hash, nt_hash = obtain_hashes_for_rid( rid=int(rid_string, 16), double_encrypted_lm_hash=user_account_v.encrypted_lm_hash, double_encrypted_nt_hash=user_account_v.encrypted_nt_hash, hashed_bootkey=hashed_bootkey ) password_hint = '' with suppress(RegistryValueNotFoundException): password_hint = sam_registry.open( path=f'SAM\\Domains\\Account\\Users\\{rid_string}' ).value('UserPasswordHint').raw_data().decode(encoding='utf-16le') sam_entries.append( SAMEntry( rid=int(rid_string, 16), account_name=user_account_v.name, password_hint=password_hint, lm_hash=lm_hash, nt_hash=nt_hash ) ) return sam_entries
def get_domain_cached_credentials( security_registry: Registry, cached_credentials_decryption_key: bytes, use_new_style: bool = True ) -> List[DomainCachedCredentials2]: iteration_count = 10240 with suppress(RegistryValueNotFoundException): reg_iteration_count = struct_unpack('<L', security_registry.open(r'Cache').value('NL$IterationCount'))[0] iteration_count = reg_iteration_count & 0xfffffc00 if reg_iteration_count > 10240 else reg_iteration_count * 1024 registry_cache_entries = ( RegistryCacheEntry.from_bytes(cache_key.raw_data()) for cache_key in security_registry.open('Cache').values() if cache_key.name() not in {'NL$Control', 'NL$IterationCount'} ) domain_cached_credentials: List[Union[DomainCachedCredentials, DomainCachedCredentials2]] = list() for registry_cache_entry in registry_cache_entries: if registry_cache_entry.initialization_vector == b'\x00' * 16: continue # TODO: Make nicer? (and parse flags in class)? if registry_cache_entry.flags & 1 == 1: if use_new_style: decrypted_cached_entry_data = decrypt_aes( key=cached_credentials_decryption_key, value=registry_cache_entry.encrypted_data, initialization_vector=registry_cache_entry.initialization_vector ) else: raise NotImplementedError # TODO: For some reason impacket uses `encrypt()`. Confirm that `decrypt()` is correct. # decrypted_cached_entry_data = ARC4.new( # key=HMAC.new( # key=nlkm_key, # msg=registry_cache_entry.initialization_vector, # ).digest() # ).decrypt(ciphertext=registry_cache_entry.encrypted_data) else: # "Plain! Until we figure out what this is, we skip it" raise NotImplementedError def calc_padded_length(length: int) -> int: return length + (length & 0x3) if (length & 0x3) > 0 else length padded_user_length = calc_padded_length(registry_cache_entry.user_length) padded_domain_length = calc_padded_length(registry_cache_entry.domain_name_length) encrypted_hash = decrypted_cached_entry_data[:16] username = decrypted_cached_entry_data[72:72+registry_cache_entry.user_length].decode(encoding='utf-16le') dns_domain_name = decrypted_cached_entry_data[ 72+padded_user_length+padded_domain_length : 72+padded_user_length+padded_domain_length+registry_cache_entry.dns_domain_name_length ].decode(encoding='utf-16-le') if use_new_style: domain_cached_credentials.append( DomainCachedCredentials2( iteration_count=iteration_count, dns_domain_name=dns_domain_name, username=username, encrypted_hash=encrypted_hash ) ) else: domain_cached_credentials.append( DomainCachedCredentials( dns_domain_name=dns_domain_name, username=username, encrypted_hash=encrypted_hash ) ) return domain_cached_credentials
def unpack(self, data): with MemoryFile(data) as stream: root = Registry(stream).root() yield from self._walk(root, root.name())
def process(self, data): with io.BytesIO(data) as stream: yield from self._walk(Registry(stream).root())
async def dump_remote_windows_secrets( smb_session: SMBv2Session, skip_lsa_secrets: bool = False, skip_sam_secrets: bool = False, dump_reg_share_name: str = 'C$', dump_reg_path: Optional[PureWindowsPath] = None ) -> tuple[Optional[list[SAMEntry]], Optional[list[DomainCachedCredentials2]], Optional[dict[str, bytes]]]: """ Dump Windows secrets remotely over SMB. The secrets are obtained by reading and then processing data from the Windows registry. The MS-RRP protocol is used to do read this data remotely. The relevant registry values are saved to disk on the remote system with the `BaseRegSaveKey` RRP operation, and are then retrieved via the SMB READ command, after which the files on disk are deleted. :param smb_session: An SMB session with which to remotely dump the Windows secrets. :param skip_lsa_secrets: Whether to skip the extraction of LSA secrets. :param skip_sam_secrets: Whether to skip the extraction of SAM secrets. :param dump_reg_share_name: A name of an SMB share on the remote system in which the disk-saved registry values can be obtained. :param dump_reg_path: A path on the remote system where the relevant registry values are to be saved on disk. :return: Optionally, a list of SAM entries, domain cached credentials, and policy secrets. """ if skip_lsa_secrets and skip_sam_secrets: return None, None, None async with smb_session.tree_connect(share_name=dump_reg_share_name) as (tree_id, _): async with smb_session.make_smbv2_transport(pipe=MS_RRP_PIPE_NAME) as (r, w): async with RPCConnection(reader=r, writer=w) as rpc_connection: await rpc_connection.bind( presentation_context_list=ContextList([ ContextElement(context_id=0, abstract_syntax=MS_RRP_ABSTRACT_SYNTAX) ]) ) open_local_machine_options = dict( rpc_connection=rpc_connection, request=OpenLocalMachineRequest( sam_desired=Regsam(maximum_allowed=True) ) ) async with open_local_machine(**open_local_machine_options) as local_machine_key_handle: # TODO: `asyncio.gather` isn't working here for some reason... lsa_data: bytes = await dump_reg( rpc_connection=rpc_connection, smb_session=smb_session, root_key_handle=local_machine_key_handle, tree_id=tree_id, sub_key_name=r'SYSTEM\CurrentControlSet\Control\Lsa', save_path=dump_reg_path ) security_data: Optional[bytes] = await dump_reg( rpc_connection=rpc_connection, smb_session=smb_session, root_key_handle=local_machine_key_handle, tree_id=tree_id, sub_key_name='SECURITY', save_path=dump_reg_path ) if not skip_lsa_secrets else None sam_data: Optional[bytes] = await dump_reg( rpc_connection=rpc_connection, smb_session=smb_session, root_key_handle=local_machine_key_handle, tree_id=tree_id, sub_key_name='SAM', save_path=dump_reg_path ) if not skip_sam_secrets else None boot_key = get_boot_key(lsa_registry=Registry(BytesIO(lsa_data)), from_root=False) if not skip_lsa_secrets: domain_cached_credentials, policy_secrets = dump_lsa_secrets( security_registry=Registry(BytesIO(security_data)), boot_key=boot_key ) else: domain_cached_credentials, policy_secrets = None, None return ( dump_sam_secrets(sam_dump=sam_data, boot_key=boot_key) if not skip_sam_secrets else None, domain_cached_credentials, policy_secrets )