# 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 # from volatility.framework import interfaces, renderers from volatility.framework.configuration import requirements from volatility.framework.renderers import format_hints from volatility.framework.layers import intel from volatility.plugins.windows.registry import hivelist from volatility.plugins.windows import hashdump, lsadump, poolscanner from Crypto.Hash import HMAC from Crypto.Cipher import ARC4, AES from struct import unpack class Cachedump(interfaces.plugins.PluginInterface): """Dumps lsa secrets from memory""" _version = (1, 0, 0) @classmethod def get_requirements(cls): return [requirements.TranslationLayerRequirement(name = 'primary', 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.PluginRequirement(name = 'lsadump', plugin = lsadump.Lsadump, version = (1, 0, 0)) ] def get_nlkm(self, sechive, lsakey, is_vista_or_later): return lsadump.Lsadump.get_secret_by_name(sechive, 'NL$KM', lsakey, is_vista_or_later) def decrypt_hash(self, edata, nlkm, ch, xp): if xp: hmac_md5 = HMAC.new(nlkm, ch) rc4key = hmac_md5.digest() rc4 = ARC4.new(rc4key) data = rc4.encrypt(edata) else: # based on Based on code from http://lab.mediaservice.net/code/cachedump.rb aes = AES.new(nlkm[16:32], AES.MODE_CBC, ch) data = "" for i in range(0, len(edata), 16): buf = edata[i : i + 16] if len(buf) < 16: buf += (16 - len(buf)) * "\00" data += aes.decrypt(buf) return data def parse_cache_entry(self, cache_data): (uname_len, domain_len) = unpack("= (6, 0), fallback_checks = [("KdCopyDataBlock", None, True)]) vista_or_later = 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 Exception('Unable to find lsa key') nlkm = self.get_nlkm(sechive, lsakey, vista_or_later) if not nlkm: raise Exception('Unable to find nlkma key') cache = sechive.get_key("Cache") if not cache: raise Exception('Unable to find cache key') for cache_item in cache.get_values(): if cache_item.Name == "NL$Control": continue data = sechive.read(cache_item.Data+4, cache_item.DataLength) if data == None: continue (uname_len, domain_len, domain_name_len, enc_data, ch) = self.parse_cache_entry(data) # Skip if nothing in this cache entry if uname_len == 0 or len(ch) == 0: continue dec_data = self.decrypt_hash(enc_data, nlkm, ch, not vista_or_later) (username, domain, domain_name, hashh) = self.parse_decrypted_cache(dec_data, uname_len, domain_len, domain_name_len) yield (0,(username, domain, domain_name, hashh)) def run(self): offset = self.config.get('offset', None) for hive in hivelist.HiveList.list_hives(self.context, self.config_path, self.config['primary'], self.config['nt_symbols'], hive_offsets = None if offset is None else [offset]): if hive.get_name().split('\\')[-1].upper() == 'SYSTEM': syshive=hive if hive.get_name().split('\\')[-1].upper() == 'SECURITY': sechive=hive return renderers.TreeGrid([("Username", str), ("Domain", str), ("Domain name", str), ('Hashh', bytes)], self._generator(syshive, sechive))