Windows: Improve hashdumping plugin errors

This commit is contained in:
Mike Auty
2021-05-16 17:01:52 +01:00
parent 2705614306
commit f873ced04e
3 changed files with 68 additions and 42 deletions
@@ -1,20 +1,22 @@
# This file is Copyright 2020 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
import logging
from struct import unpack
from typing import Tuple
from Crypto.Cipher import ARC4, AES
from Crypto.Hash import HMAC
from volatility3.framework import interfaces, renderers, exceptions
from volatility3.framework import interfaces, renderers
from volatility3.framework.configuration import requirements
from volatility3.framework.layers import registry
from volatility3.framework.symbols.windows import versions
from volatility3.plugins.windows import hashdump, lsadump
from volatility3.plugins.windows.registry import hivelist
vollog = logging.getLogger(__name__)
class Cachedump(interfaces.plugins.PluginInterface):
"""Dumps lsa secrets from memory"""
@@ -30,7 +32,8 @@ class Cachedump(interfaces.plugins.PluginInterface):
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"),
requirements.PluginRequirement(name = 'hivelist', plugin = hivelist.HiveList, version = (1, 0, 0)),
requirements.PluginRequirement(name = 'lsadump', plugin = lsadump.Lsadump, version = (1, 0, 0))
requirements.PluginRequirement(name = 'lsadump', plugin = lsadump.Lsadump, version = (1, 0, 0)),
requirements.PluginRequirement(name = 'hashdump', plugin = hashdump.Hashdump, version = (1, 1, 0))
]
@staticmethod
@@ -60,7 +63,7 @@ class Cachedump(interfaces.plugins.PluginInterface):
(uname_len, domain_len) = unpack("<HH", cache_data[:4])
if len(cache_data[60:62]) == 0:
return (uname_len, domain_len, 0, b'', b'')
(domain_name_len, ) = unpack("<H", cache_data[60:62])
(domain_name_len,) = unpack("<H", cache_data[60:62])
ch = cache_data[64:80]
enc_data = cache_data[96:]
return (uname_len, domain_len, domain_name_len, enc_data, ch)
@@ -84,21 +87,25 @@ class Cachedump(interfaces.plugins.PluginInterface):
def _generator(self, syshive, sechive):
bootkey = hashdump.Hashdump.get_bootkey(syshive)
if not bootkey:
raise ValueError('Unable to find bootkey')
vollog.warning('Unable to find bootkey')
return
vista_or_later = versions.is_vista_or_later(context = self.context, symbol_table = self.config['nt_symbols'])
lsakey = lsadump.Lsadump.get_lsa_key(sechive, bootkey, vista_or_later)
if not lsakey:
raise ValueError('Unable to find lsa key')
vollog.warning('Unable to find lsa key')
return
nlkm = self.get_nlkm(sechive, lsakey, vista_or_later)
if not nlkm:
raise ValueError('Unable to find nlkma key')
vollog.warning('Unable to find nlkma key')
return
cache = sechive.get_key("Cache")
cache = hashdump.Hashdump.get_hive_key(sechive, "Cache")
if not cache:
raise ValueError('Unable to find cache key')
vollog.warning('Unable to find cache key')
return
for cache_item in cache.get_values():
if cache_item.Name == "NL$Control":
@@ -133,10 +140,12 @@ class Cachedump(interfaces.plugins.PluginInterface):
if hive.get_name().split('\\')[-1].upper() == 'SECURITY':
sechive = hive
if syshive is None:
raise exceptions.VolatilityException('Unable to locate SYSTEM hive')
if sechive is None:
raise exceptions.VolatilityException('Unable to locate SECURITY hive')
if syshive is None or sechive is None:
if syshive is None:
vollog.warning('Unable to locate SYSTEM hive')
if sechive is None:
vollog.warning('Unable to locate SECURITY hive')
return
return renderers.TreeGrid([("Username", str), ("Domain", str), ("Domain name", str), ('Hashh', bytes)],
self._generator(syshive, sechive))
@@ -22,6 +22,7 @@ class Hashdump(interfaces.plugins.PluginInterface):
"""Dumps user hashes from memory"""
_required_framework_version = (1, 0, 0)
_version = (1, 1, 0)
@classmethod
def get_requirements(cls):
@@ -60,11 +61,22 @@ class Hashdump(interfaces.plugins.PluginInterface):
empty_lm = b"\xaa\xd3\xb4\x35\xb5\x14\x04\xee\xaa\xd3\xb4\x35\xb5\x14\x04\xee"
empty_nt = b"\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31\xb7\x3c\x59\xd7\xe0\xc0\x89\xc0"
@classmethod
def get_hive_key(cls, hive: registry.RegistryHive, key: str):
result = None
try:
result = hive.get_key(key)
except KeyError:
vollog.info(
"Unable to load the required registry key {}\\{} from this memory image".format(hive.get_name(), key))
return result
@classmethod
def get_user_keys(cls, samhive: registry.RegistryHive) -> List[interfaces.objects.ObjectInterface]:
user_key_path = "SAM\\Domains\\Account\\Users"
user_key = samhive.get_key(user_key_path)
user_key = cls.get_hive_key(samhive, user_key_path)
if not user_key:
return []
return [k for k in user_key.get_subkeys() if k.Name != "Names"]
@@ -75,7 +87,7 @@ class Hashdump(interfaces.plugins.PluginInterface):
lsa_base = "ControlSet{0:03}".format(cs) + "\\Control\\Lsa"
lsa_keys = ["JD", "Skew1", "GBG", "Data"]
lsa = syshive.get_key(lsa_base)
lsa = cls.get_hive_key(syshive, lsa_base)
if not lsa:
return None
@@ -83,9 +95,10 @@ class Hashdump(interfaces.plugins.PluginInterface):
bootkey = ''
for lk in lsa_keys:
key = syshive.get_key(lsa_base + '\\' + lk)
class_data = syshive.read(key.Class + 4, key.ClassLength)
key = cls.get_hive_key(syshive, lsa_base + '\\' + lk)
class_data = None
if key:
class_data = syshive.read(key.Class + 4, key.ClassLength)
if class_data is None:
return None
@@ -102,7 +115,7 @@ class Hashdump(interfaces.plugins.PluginInterface):
if not bootkey:
return None
sam_account_key = samhive.get_key(sam_account_path)
sam_account_key = cls.get_hive_key(samhive, sam_account_path)
if not sam_account_key:
return None
@@ -270,7 +283,7 @@ class Hashdump(interfaces.plugins.PluginInterface):
rid = int(str(user.get_name()), 16)
yield (0, (name, rid, lmout, ntout))
else:
raise ValueError("Hbootkey is not valid")
vollog.warning("Hbootkey is not valid")
def run(self):
offset = self.config.get('offset', None)
@@ -31,7 +31,8 @@ class Lsadump(interfaces.plugins.PluginInterface):
description = 'Memory layer for the kernel',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"),
requirements.PluginRequirement(name = 'hivelist', plugin = hivelist.HiveList, version = (1, 0, 0))
requirements.VersionRequirement(name = 'hashdump', component = hashdump.Hashdump, version = (1, 1, 0)),
requirements.VersionRequirement(name = 'hivelist', component = hivelist.HiveList, version = (1, 0, 0))
]
@classmethod
@@ -65,7 +66,7 @@ class Lsadump(interfaces.plugins.PluginInterface):
else:
policy_key = 'PolSecretEncryptionKey'
enc_reg_key = sechive.get_key("Policy\\" + policy_key)
enc_reg_key = hashdump.Hashdump.get_hive_key(sechive, "Policy\\" + policy_key)
if not enc_reg_key:
return None
enc_reg_value = next(enc_reg_key.get_values())
@@ -94,23 +95,21 @@ class Lsadump(interfaces.plugins.PluginInterface):
@classmethod
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_key = hashdump.Hashdump.get_hive_key(sechive, "Policy\\Secrets\\" + name + "\\CurrVal")
enc_secret_value = next(enc_secret_key.get_values())
if not enc_secret_value:
return None
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 not enc_secret:
return None
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)
if not is_vista_or_later:
secret = cls.decrypt_secret(enc_secret[0xC:], lsakey)
else:
secret = cls.decrypt_aes(enc_secret, lsakey)
return secret
@classmethod
@@ -133,7 +132,7 @@ class Lsadump(interfaces.plugins.PluginInterface):
if len(key[j:j + 7]) < 7:
j = len(key[j:j + 7])
(dec_data_len, ) = unpack("<L", decrypted_data[:4])
(dec_data_len,) = unpack("<L", decrypted_data[:4])
return decrypted_data[8:8 + dec_data_len]
@@ -144,18 +143,23 @@ class Lsadump(interfaces.plugins.PluginInterface):
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')
vollog.warning("Unable to find bootkey")
return
if not lsakey:
raise ValueError('Unable to find lsa key')
vollog.warning("Unable to find lsa key")
return
secrets_key = sechive.get_key('Policy\\Secrets')
secrets_key = hashdump.Hashdump.get_hive_key(sechive, 'Policy\\Secrets')
if not secrets_key:
raise ValueError('Unable to find secrets key')
vollog.warning("Unable to find secrets key")
return
for key in secrets_key.get_subkeys():
sec_val_key = sechive.get_key('Policy\\Secrets\\' + key.get_key_path().split('\\')[3] + '\\CurrVal')
sec_val_key = hashdump.Hashdump.get_hive_key(sechive,
'Policy\\Secrets\\' + key.get_key_path().split('\\')[
3] + '\\CurrVal')
if not sec_val_key:
continue