diff --git a/volatility/framework/plugins/windows/cachedump.py b/volatility/framework/plugins/windows/cachedump.py new file mode 100644 index 000000000..797cab018 --- /dev/null +++ b/volatility/framework/plugins/windows/cachedump.py @@ -0,0 +1,138 @@ +# 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)) \ No newline at end of file diff --git a/volatility/framework/plugins/windows/lsadump.py b/volatility/framework/plugins/windows/lsadump.py new file mode 100644 index 000000000..b486860a8 --- /dev/null +++ b/volatility/framework/plugins/windows/lsadump.py @@ -0,0 +1,202 @@ +# 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 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, poolscanner +from Crypto.Hash import MD5, SHA256 +from Crypto.Cipher import ARC4, DES, AES +from struct import unpack, pack +import collections + +vollog = logging.getLogger(__name__) + +class Lsadump(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)) + ] + + @classmethod + def decrypt_aes(cls, secret, key): + """ + Based on code from http://lab.mediaservice.net/code/cachedump.rb + """ + sha = SHA256.new() + sha.update(key) + for _i in range(1, 1000 + 1): + sha.update(secret[28:60]) + aeskey = sha.digest() + + data = b"" + for i in range(60, len(secret), 16): + aes = AES.new(aeskey, AES.MODE_CBC, b'\x00' * 16) + buf = secret[i : i + 16] + if len(buf) < 16: + buf += (16 - len(buf)) * "\00" + data += aes.decrypt(buf) + + return data + + @classmethod + def get_lsa_key(cls, sechive, bootkey, vista_or_later): + if not bootkey: + return None + + if vista_or_later: + policy_key = 'PolEKList' + else: + policy_key = 'PolSecretEncryptionKey' + + enc_reg_key = sechive.get_key("Policy\\"+policy_key) + if not enc_reg_key: + return None + enc_reg_value = next(enc_reg_key.get_values()) + + + if not enc_reg_value: + return None + + obf_lsa_key = sechive.read(enc_reg_value.Data+4, enc_reg_value.DataLength) + + if not obf_lsa_key: + return None + if not vista_or_later: + md5 = MD5.new() + md5.update(bootkey) + for _i in range(1000): + md5.update(obf_lsa_key[60:76]) + rc4key = md5.digest() + + rc4 = ARC4.new(rc4key) + lsa_key = rc4.decrypt(obf_lsa_key[12:60]) + lsa_key = lsa_key[0x10:0x20] + else: + lsa_key = cls.decrypt_aes(obf_lsa_key, bootkey) + lsa_key = lsa_key[68:100] + return lsa_key + + @classmethod + def get_secret_by_name(cls, sechive, name, lsakey, is_vista_or_later): + try: + enc_secret_key = sechive.get_key("Policy\\Secrets\\" + name + "\\CurrVal") + except KeyError: + raise Exception("Unable to read cache from memory") + + + enc_secret_value = next(enc_secret_key.get_values()) + if not enc_secret_value: + return None + + enc_secret = sechive.read(enc_secret_value.Data+4, + enc_secret_value.DataLength) + if not enc_secret: + return None + + 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 + def decrypt_secret(cls, secret, key): + """Python implementation of SystemFunction005. + + Decrypts a block of data with DES using given key. + Note that key can be longer than 7 bytes.""" + decrypted_data = '' + j = 0 # key index + + for i in range(0, len(secret), 8): + enc_block = secret[i:i + 8] + block_key = key[j:j + 7] + des_key = hashdump.Hashdump.sidbytes_to_key(block_key) + des = DES.new(des_key, DES.MODE_ECB) + enc_block = enc_block + b"\x00" * int(abs(8 - len(enc_block)) % 8) + decrypted_data += des.decrypt(enc_block) + j += 7 + if len(key[j:j + 7]) < 7: + j = len(key[j:j + 7]) + + (dec_data_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']) + + bootkey = hashdump.Hashdump.get_bootkey(syshive) + lsakey = self.get_lsa_key(sechive, bootkey, vista_or_later) + if not bootkey: + raise Exception('Unable to find bootkey') + + if not lsakey: + raise Exception('Unable to find lsa key') + + secrets_key = sechive.get_key('Policy\\Secrets') + if not secrets_key: + raise Exception('Unable to find secrets key') + + for key in secrets_key.get_subkeys(): + + sec_val_key=sechive.get_key('Policy\\Secrets\\' + key.get_key_path().split('\\')[3] + '\\CurrVal') + if not sec_val_key: + continue + + enc_secret_value = next(sec_val_key.get_values()) + if not enc_secret_value: + continue + + enc_secret = sechive.read(enc_secret_value.Data + 4, + enc_secret_value.DataLength) + if not enc_secret: + continue + if not vista_or_later: + secret = self.decrypt_secret(enc_secret[0xC:], lsakey) + else: + secret = self.decrypt_aes(enc_secret, lsakey).decode('latin1') + + + yield (0,(key.get_name()+'\n', secret+'\n', secret.encode('latin1'))) + + + + 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([("Key", str), ("Secret", str), ('Hex', bytes)], + self._generator(syshive, sechive)) + + + + diff --git a/volatility/framework/symbols/linux/__init__.py b/volatility/framework/symbols/linux/__init__.py index e79feb0e6..de06f985c 100644 --- a/volatility/framework/symbols/linux/__init__.py +++ b/volatility/framework/symbols/linux/__init__.py @@ -7,6 +7,7 @@ from volatility.framework import exceptions, constants, interfaces, objects, con from volatility.framework.objects import utility from volatility.framework.symbols import intermed from volatility.framework.symbols.linux import extensions +from volatility.framework.objects import utility class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): diff --git a/volatility/framework/symbols/linux/extensions/elf.py b/volatility/framework/symbols/linux/extensions/elf.py index 41fa55ce2..f09cf087b 100644 --- a/volatility/framework/symbols/linux/extensions/elf.py +++ b/volatility/framework/symbols/linux/extensions/elf.py @@ -2,15 +2,27 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # +<<<<<<< HEAD from typing import Dict, Tuple from volatility.framework import constants from volatility.framework import objects, interfaces +======= +from volatility.framework import exceptions, constants +from volatility.framework import objects, interfaces +from volatility.framework.objects import utility +from volatility.framework.renderers import conversion +from typing import Any, Dict, List, Optional, Tuple, Union, Type +>>>>>>> added hashdump class elf(objects.StructType): ''' +<<<<<<< HEAD Class used to create elf objects. It overrides the typename to `Elf32_` or `Elf64_`, +======= + Class used to create elf objects. It overrides the typename to Elf32_ or Elf64_, +>>>>>>> added hashdump depending on the corresponding value on e_ident ''' @@ -45,7 +57,11 @@ class elf(objects.StructType): elif ei_class == 2: self._type_prefix = "Elf64_" else: +<<<<<<< HEAD raise ValueError("Unsupported ei_class value {}".format(ei_class)) +======= + self._type_prefix = None +>>>>>>> added hashdump # Construct the full header self._hdr = self._context.object(symbol_table_name + constants.BANG + self._type_prefix + "Ehdr",