# -*- coding: utf-8 -*-
"""Domain cached credentials collector."""
import codecs
import struct
import pyfcrypto
import pyhmac
from winregrc import hexdump
from winregrc import interface
[docs]
class CachedCredentialsKeyCollector(interface.WindowsRegistryKeyCollector):
"""Domain cached credentials key collector."""
_CREDENTIALS_CACHE_KEY_PATH = 'HKEY_LOCAL_MACHINE\\Security\\Cache'
_NL_KEY_MATERIAL_KEY_PATH = (
'HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\NL$KM\\CurrVal')
_LSA_KEY_PATH = (
'HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Lsa')
_POLICY_ENCRYPTION_KEY_PATH = (
'HKEY_LOCAL_MACHINE\\Security\\Policy\\PolSecretEncryptionKey')
_ODD_PARITY_TABLE = [
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 16, 16, 19, 19, 21,
21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 32, 32, 35, 35, 37, 37, 38,
38, 41, 41, 42, 42, 44, 44, 47, 47, 49, 49, 50, 50, 52, 52, 55, 55, 56,
56, 59, 59, 61, 61, 62, 62, 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74,
74, 76, 76, 79, 79, 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93,
93, 94, 94, 97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107, 107, 109,
109, 110, 110, 112, 112, 115, 115, 117, 117, 118, 118, 121, 121, 122, 122,
124, 124, 127, 127, 128, 128, 131, 131, 133, 133, 134, 134, 137, 137, 138,
138, 140, 140, 143, 143, 145, 145, 146, 146, 148, 148, 151, 151, 152, 152,
155, 155, 157, 157, 158, 158, 161, 161, 162, 162, 164, 164, 167, 167, 168,
168, 171, 171, 173, 173, 174, 174, 176, 176, 179, 179, 181, 181, 182, 182,
185, 185, 186, 186, 188, 188, 191, 191, 193, 193, 194, 194, 196, 196, 199,
199, 200, 200, 203, 203, 205, 205, 206, 206, 208, 208, 211, 211, 213, 213,
214, 214, 217, 217, 218, 218, 220, 220, 223, 223, 224, 224, 227, 227, 229,
229, 230, 230, 233, 233, 234, 234, 236, 236, 239, 239, 241, 241, 242, 242,
244, 244, 247, 247, 248, 248, 251, 251, 253, 253, 254, 254]
[docs]
def __init__(self, debug=False, output_writer=None):
"""Initializes a system key collector.
Args:
debug (Optional[bool]): True if debug information should be printed.
output_writer (Optional[OutputWriter]): output writer.
"""
super(CachedCredentialsKeyCollector, self).__init__(debug=debug)
self._output_writer = output_writer
def _DecryptARC4(self, key, data):
"""Decrypts ARC4 encrypted data.
Args:
key (str): key used to decrypt the data.
data (bytes): data to decrypt.
Returns:
bytes: decrypted data.
"""
rc4_context = pyfcrypto.rc4_context()
rc4_context.set_key(key)
return pyfcrypto.crypt_rc4(rc4_context, data)
def _DecryptTripleDES(self, key, data):
"""Decrypts Triple DES-ECB encrypted data.
Args:
key (str): key used to decrypt the data.
data (bytes): data to decrypt.
Returns:
bytes: decrypted data.
"""
des3_context = pyfcrypto.des3_context()
des3_context.set_key(key)
return pyfcrypto.crypt_des3(
des3_context, pyfcrypto.crypt_modes.DECRYPT, data)
def _GetBootKey(self, registry):
"""Retrieves the boot key.
Args:
registry (dfwinreg.WinRegistry): Windows Registry.
Returns:
bytes: boot key or None if not found.
"""
try:
lsa_key = registry.GetKeyByPath(self._LSA_KEY_PATH)
except RuntimeError:
lsa_key = None
if not lsa_key:
return None
lsa_jd_key = lsa_key.GetSubkeyByName('JD')
lsa_skew1_key = lsa_key.GetSubkeyByName('Skew1')
lsa_gbg_key = lsa_key.GetSubkeyByName('GBG')
lsa_data_key = lsa_key.GetSubkeyByName('Data')
if None in (lsa_jd_key, lsa_skew1_key, lsa_gbg_key, lsa_data_key):
return None
lsa_jd_class_name = lsa_jd_key.class_name
lsa_skew1_class_name = lsa_skew1_key.class_name
lsa_gbg_class_name = lsa_gbg_key.class_name
lsa_data_class_name = lsa_data_key.class_name
if None in (
lsa_jd_class_name, lsa_skew1_class_name, lsa_gbg_class_name,
lsa_data_class_name):
return None
class_name_string = ''.join([
lsa_jd_class_name, lsa_skew1_class_name, lsa_gbg_class_name,
lsa_data_class_name])
scrambled_key = codecs.decode(class_name_string, 'hex')
key = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
for index, scrambled_index in enumerate([
8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7]):
key[index] = scrambled_key[scrambled_index]
return bytes(key)
def _GetLSAKey(self, registry, boot_key):
"""Retrieves the LSA key.
Args:
registry (dfwinreg.WinRegistry): Windows Registry.
boot_key (bytes): boot key.
Returns:
bytes: LSA key or None if not found.
"""
policy_encryption_key = registry.GetKeyByPath(
self._POLICY_ENCRYPTION_KEY_PATH)
if not policy_encryption_key:
return None
policy_encryption_value = policy_encryption_key.GetValueByName('')
if not policy_encryption_value:
return None
value_data = policy_encryption_value.data
digest_context = pyhmac.md5_context()
digest_context.update(boot_key)
iteration = 0
while iteration < 1000:
digest_context.update(value_data[60:76])
iteration += 1
rc4_key = digest_context.finalize()
decrypted_data = self._DecryptARC4(rc4_key, value_data[12:60])
return decrypted_data[16:32]
def _GetNLKey(self, registry, lsa_key):
"""Retrieves the NL key.
Args:
registry (dfwinreg.WinRegistry): Windows Registry.
lsa_key (bytes): LSA key.
Returns:
bytes: NL key or None if not found.
"""
nl_key_material_key = registry.GetKeyByPath(
self._NL_KEY_MATERIAL_KEY_PATH)
if not nl_key_material_key:
return None
nl_key_material_value = nl_key_material_key.GetValueByName('')
if not nl_key_material_value:
return None
key_size = len(lsa_key)
value_data = nl_key_material_value.data
value_data_size = len(value_data)
decrypted_value_data = []
key_offset = 0
for value_data_offset in range(12, value_data_size, 8):
key_end_offset = key_offset + 7
value_data_end_offset = value_data_offset + 8
des_key = self._UnpackLSAKey(lsa_key[key_offset:key_end_offset])
decrypted_data = self._DecryptTripleDES(
des_key, value_data[value_data_offset:value_data_end_offset])
decrypted_value_data.append(decrypted_data)
key_offset = key_end_offset
available_key_size = key_size - key_offset
if available_key_size < 7:
key_offset = available_key_size
decrypted_value_data = b''.join(decrypted_value_data)
print(hexdump.Hexdump(decrypted_value_data))
(data_size, ) = struct.unpack('<L', decrypted_value_data[:4])
data_size += 8
return decrypted_value_data[8:data_size]
def _UnpackLSAKey(self, lsa_key):
"""Unpacks 7 bytes of the LSA key as a 8-byte Triple DES decryption key.
Args:
lsa_key (bytes): LSA key.
Returns:
bytes: Triple DES decryption key.
"""
lsa_key = bytearray(lsa_key)
des_key = [
lsa_key[0] >> 1,
(lsa_key[0] & 0x01) << 6 | lsa_key[1] >> 2,
(lsa_key[1] & 0x03) << 5 | lsa_key[2] >> 3,
(lsa_key[2] & 0x07) << 4 | lsa_key[3] >> 4,
(lsa_key[3] & 0x0f) << 3 | lsa_key[4] >> 5,
(lsa_key[4] & 0x1f) << 2 | lsa_key[5] >> 6,
(lsa_key[5] & 0x3f) << 1 | lsa_key[6] >> 7,
lsa_key[6] & 0x7f]
des_key = bytearray([
self._ODD_PARITY_TABLE[value << 1] for value in des_key])
return bytes(des_key)
[docs]
def Collect(self, registry): # pylint: disable=arguments-differ
"""Collects system information.
Args:
registry (dfwinreg.WinRegistry): Windows Registry.
Returns:
bool: True if the system key was found, False if not.
"""
credentials_cache_key = registry.GetKeyByPath(
self._CREDENTIALS_CACHE_KEY_PATH)
if not credentials_cache_key:
return False
boot_key = self._GetBootKey(registry)
if not boot_key:
return False
lsa_key = self._GetLSAKey(registry, boot_key)
if not lsa_key:
return False
nl_key = self._GetNLKey(registry, lsa_key)
if not nl_key:
return False
for value in credentials_cache_key.GetValues():
if value.name == 'NL$Control':
continue
rc4_key = pyhmac.md5_calculate_hmac(nl_key, value.data[64:80])
decrypted_data = self._DecryptARC4(rc4_key, value.data[96:])
print(value.name)
print(hexdump.Hexdump(value.data))
print(hexdump.Hexdump(decrypted_data))
return True