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 decrypt_hash(self, rid, hashobj, constant): key1, key2 = SAM.rid_to_key(rid) des1 = DES(key1) des2 = DES(key2) if isinstance(hashobj, SAM_HASH): rc4key = hashlib.md5( self.hashed_bootkey[:0x10] + int(rid, 16).to_bytes(4, 'little', signed=False) + constant).digest() key = RC4(rc4key).encrypt(hashobj.hash) else: key = b'' cipher = AES(self.hashed_bootkey[:0x10], MODE_CBC, IV=hashobj.salt) n = 16 for block in [ hashobj.data[i:i + n] for i in range(0, len(hashobj.data), n) ]: #terrible, terrible workaround key += cipher.decrypt(block) key = key[:16] dec_hash = des1.decrypt(key[:8]) + des2.decrypt(key[8:]) return dec_hash
def decrypt_attr(attr, key): if attr.data is not None: if attr.iv is not None: cipher = AES(key, MODE_CBC, attr.iv) else: cipher = AES(key, MODE_CBC, b'\x00' * 16) cleartext = cipher.decrypt(attr.data) return cleartext
def basic_encrypt(cls, key, plaintext): assert len(plaintext) >= 16 aes = AES(key.contents, MODE_CBC, b'\x00' * 16) ctext = aes.encrypt(_zeropad(plaintext, 16)) if len(plaintext) > 16: # Swap the last two ciphertext blocks and truncate the # final block to match the plaintext length. lastlen = len(plaintext) % 16 or 16 ctext = ctext[:-32] + ctext[-16:] + ctext[-32:-16][:lastlen] return ctext
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 decrypt(self, encrypted): # TODO: NT version specific, move from here in subclasses. cleartext = b'' size = len(encrypted) if size: if size % 8: if not self.aes_key or not self.iv: return cleartext cipher = AES(self.aes_key, MODE_CFB, self.iv) cleartext = cipher.decrypt(encrypted) else: if not self.des_key or not self.iv: return cleartext cipher = TDES(self.des_key, MODE_CBC, self.iv[:8]) cleartext = cipher.decrypt(encrypted) return cleartext
def gppassword(pw_enc_b64): AES_KEY = b'\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b' AES_IV = b'\x00' * 16 pad = len(pw_enc_b64) % 4 if pad == 1: pw_enc_b64 = pw_enc_b64[:-1] elif pad == 2 or pad == 3: pw_enc_b64 += '=' * (4 - pad) pw_enc = base64.b64decode(pw_enc_b64) ctx = AES(AES_KEY, MODE_CBC, IV=AES_IV) pw_dec = pkcs7_unpad(ctx.decrypt(pw_enc)) return pw_dec.decode('utf-16-le')
def basic_decrypt(cls, key, ciphertext): assert len(ciphertext) >= 16 aes = AES(key.contents, MODE_ECB) if len(ciphertext) == 16: return aes.decrypt(ciphertext) # Split the ciphertext into blocks. The last block may be partial. cblocks = [ciphertext[p:p + 16] for p in range(0, len(ciphertext), 16)] lastlen = len(cblocks[-1]) # CBC-decrypt all but the last two blocks. prev_cblock = b'\x00' * 16 plaintext = b'' for b in cblocks[:-2]: plaintext += _xorbytes(aes.decrypt(b), prev_cblock) prev_cblock = b # Decrypt the second-to-last cipher block. The left side of # the decrypted block will be the final block of plaintext # xor'd with the final partial cipher block; the right side # will be the omitted bytes of ciphertext from the final # block. b = aes.decrypt(cblocks[-2]) lastplaintext = _xorbytes(b[:lastlen], cblocks[-1]) omitted = b[lastlen:] # Decrypt the final cipher block plus the omitted bytes to get # the second-to-last plaintext block. plaintext += _xorbytes(aes.decrypt(cblocks[-1] + omitted), prev_cblock) return plaintext + lastplaintext
async def get_HBoot_key(self): logger.debug('SAM parsing hashed bootkey') QWERTY = b"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0" DIGITS = b"0123456789012345678901234567890123456789\0" F = await self.hive.get_value(r'SAM\Domains\Account\F') F = 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 = AES(self.bootkey, MODE_CBC, 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 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
def decrypt_all_chrome(self, dbpaths, throw=False): from unicrypto import use_library, get_cipher_by_name AES = get_cipher_by_name('AES', 'cryptography') results = {} results['logins'] = [] results['cookies'] = [] results['fmtcookies'] = [] localstate_dec = None for username in dbpaths: if 'localstate' in dbpaths[username]: with open(dbpaths[username]['localstate'], 'r') as f: encrypted_key = json.load(f)['os_crypt']['encrypted_key'] encrypted_key = base64.b64decode(encrypted_key) try: localstate_dec = self.decrypt_blob_bytes(encrypted_key[5:]) except: if throw is True: raise Exception('LocalState decryption failed!') # this localstate was encrypted for another user... continue if 'cookies' in dbpaths[username]: secrets = DPAPI.get_chrome_encrypted_secret( dbpaths[username]['cookies']) for host_key, name, path, encrypted_value in secrets[ 'cookies']: if encrypted_value.startswith(b'v10'): nonce = encrypted_value[3:3 + 12] ciphertext = encrypted_value[3 + 12:-16] tag = encrypted_value[-16:] cipher = AES(localstate_dec, MODE_GCM, IV=nonce, segment_size=16) dec_val = cipher.decrypt(ciphertext, b'', tag) results['cookies'].append( (dbpaths[username]['cookies'], host_key, name, path, dec_val)) results['fmtcookies'].append( DPAPI.cookieformatter('https://' + host_key, name, path, dec_val)) else: dec_val = self.decrypt_blob_bytes(encrypted_value) results['cookies'].append( (dbpaths[username]['cookies'], host_key, name, path, dec_val)) results['fmtcookies'].append( DPAPI.cookieformatter('https://' + host_key, name, path, dec_val)) if 'logindata' in dbpaths[username]: secrets = DPAPI.get_chrome_encrypted_secret( dbpaths[username]['logindata']) for url, user, enc_password in secrets['logins']: if enc_password.startswith(b'v10'): nonce = enc_password[3:3 + 12] ciphertext = enc_password[3 + 12:-16] tag = enc_password[-16:] cipher = AES(localstate_dec, MODE_GCM, IV=nonce, segment_size=16) password = cipher.decrypt(ciphertext, b'', tag) results['logins'].append( (dbpaths[username]['logindata'], url, user, password)) else: password = self.decrypt_blob_bytes(enc_password) results['logins'].append( (dbpaths[username]['logindata'], url, user, password)) return results
def decrypt_all_chrome(self, dbpaths): results = {} results['logins'] = [] results['cookies'] = [] results['fmtcookies'] = [] localstate_dec = None for username in dbpaths: if 'localstate' in dbpaths[username]: with open(dbpaths[username]['localstate'], 'r') as f: encrypted_key = json.load(f)['os_crypt']['encrypted_key'] encrypted_key = base64.b64decode(encrypted_key) try: localstate_dec = self.decrypt_blob_bytes(encrypted_key[5:]) except: # this localstate was encrypted for another user... continue if 'cookies' in dbpaths[username]: secrets = DPAPI.get_chrome_encrypted_secret( dbpaths[username]['cookies']) for host_key, name, path, encrypted_value in secrets[ 'cookies']: if encrypted_value.startswith(b'v10'): nonce = encrypted_value[3:3 + 12] ciphertext = encrypted_value[3 + 12:-16] tag = encrypted_value[-16:] cipher = AES(localstate_dec, MODE_GCM) dec_val = cipher.decrypt(nonce, ciphertext, tag, auth_data=b'') results['cookies'].append( (dbpaths[username]['cookies'], host_key, name, path, dec_val)) results['fmtcookies'].append( DPAPI.cookieformatter('https://' + host_key, name, path, dec_val)) else: dec_val = self.decrypt_blob_bytes(encrypted_value) results['cookies'].append( (dbpaths[username]['cookies'], host_key, name, path, dec_val)) results['fmtcookies'].append( DPAPI.cookieformatter('https://' + host_key, name, path, dec_val)) if 'logindata' in dbpaths[username]: secrets = DPAPI.get_chrome_encrypted_secret( dbpaths[username]['logindata']) for url, user, enc_password in secrets['logins']: if enc_password.startswith(b'v10'): nonce = enc_password[3:3 + 12] ciphertext = enc_password[3 + 12:-16] tag = enc_password[-16:] cipher = AES(localstate_dec, MODE_GCM) password = cipher.decrypt(nonce, ciphertext, tag, auth_data=b'') results['logins'].append( (dbpaths[username]['logindata'], url, user, password)) else: password = self.decrypt_blob_bytes(enc_password) results['logins'].append( (dbpaths[username]['logindata'], url, user, password)) return results