RegistryParse
This command uses the Registry Parse automation to extract critical forensics data from a registry file. The essential values are specified by the argument.
python · Windows Forensics
Source
import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 import codecs import configparser CUSTOM_REG_TYPE = "Custom" REGISTRY_TYPE_TO_KEY = { "Users": [r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"], "MachineStartup": [r"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run"], "UserStartup": [r"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run"], "MachineRunOnce": [r"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce"], "UserRunOnce": [r"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce"], "Services": ["HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services"], "DelayedServices": [r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellServiceObjectDelayLoad"], "UserRecentApps": [r"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Search\RecentApps"], "Timezone": [r"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation"], "Networks": [ r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\NetworkList\Signatures\Unmanaged", r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\NetworkList\Signatures\Managed", r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\NetworkList\Nla\Cache", ], "USB": [r"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBSTOR", r"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB"], "LastLoggedOnUser": [r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"], } REGISTRY_SUB_FOLDER = {"Users": "SID"} MAX_HR_RESULTS = 50 def parse_reg_value(value): value = value.strip('"') try: if value.startswith("hex"): value2 = "".join([ch for ch in value[value.find(":") + 1:].split(",") if len(ch) == 2 and ch != "00"]) return bytearray.fromhex(value2).decode() if value.startswith("dword"): return str(int("0x" + value[value.find(":") + 1:], 16)) return value except Exception: return value def get_registry(entry_id): res = demisto.getFilePath(entry_id) path = res["path"] with codecs.open(path, encoding="utf-16") as myfile: data = myfile.read() cfg = configparser.ConfigParser(strict=False, allow_no_value=True) cfg.optionxform = str # type: ignore[assignment, assignment] cfg.read_string( data[data.find("["):], ) reg = {} # type: ignore[var-annotated] for section in cfg.sections(): try: if section not in reg: reg[section] = {} items = cfg.items(section) reg[section].update(dict(items)) except Exception: reg[section] = {} continue return reg def get_sub_keys(reg, key, folder_output_key): all_folders = {k for k in reg if k.lower().startswith(key.lower())} users = [] records = [] for folder in all_folders: new_key = folder[len(key):].strip("\\") if new_key: user = reg[folder] user = {k.strip('"'): parse_reg_value(v) for k, v in user.items()} user[folder_output_key] = new_key for registry_key, registry_value in user.items(): record = { "Type": "Services", "RegistryPath": folder, "RegistryKey": registry_key, "RegistryValue": registry_value, } records.append(record) users.append(user) return records, users def get_reg_users(reg): key = REGISTRY_TYPE_TO_KEY["Users"][0] records, users = get_sub_keys(reg, key, "Sid") return records, {"Users": users} def get_reg_services(reg): key = REGISTRY_TYPE_TO_KEY["Services"][0] records, users = get_sub_keys(reg, key, "Service") return records, {"Services": users} def get_reg_results(reg, type_to_keys): records = [] # type: ignore[var-annotated] type_records = {} # type: ignore[var-annotated] for _type, keys in type_to_keys.items(): if _type == "Users": users_records, users_type_records = get_reg_users(reg) records += users_records type_records.update(users_type_records) elif _type == "Services": services_records, services_type_records = get_reg_services(reg) records += services_records type_records.update(services_type_records) elif _type == "LastLoggedOnUser": key = REGISTRY_TYPE_TO_KEY["LastLoggedOnUser"][0] logged_on_user_values = [v for (k, v) in reg.items() if k.lower() == key.lower()] values = {} if len(logged_on_user_values) == 0 else logged_on_user_values[0] registry_value = values.get('"LastLoggedOnUser"') if registry_value: registry_value = parse_reg_value(registry_value) records.append( { "Type": "LastLoggedOnUser", "RegistryPath": key, "RegistryKey": "LastLoggedOnUser", "RegistryValue": registry_value, } ) type_records["LastLoggedOnUser"] = registry_value else: all_keys = [] # type: ignore[var-annotated] for key in keys: all_keys += [k for k in reg if k.lower().startswith(key.lower())] for key in all_keys: registry_keys_values = reg.get(key) dict_key = _type if _type != CUSTOM_REG_TYPE else key if dict_key not in type_records: type_records[dict_key] = [] if registry_keys_values: registry_keys_values = {k.strip('"'): parse_reg_value(v) for k, v in registry_keys_values.items()} type_records[dict_key].append(registry_keys_values) for registry_key, registry_value in registry_keys_values.items(): record = { "Type": _type, "RegistryPath": key, "RegistryKey": registry_key, "RegistryValue": registry_value, } records.append(record) return records, type_records def main(): args = demisto.args() reg = get_registry(args.get("entryID")) registry_data = args.get("registryData") if registry_data == "All": registry_types = REGISTRY_TYPE_TO_KEY.keys() elif registry_data == "None": registry_types = [] # type: ignore[assignment] else: registry_types = argToList(registry_data) registry_types = [x for x in registry_types if x in REGISTRY_TYPE_TO_KEY] # type: ignore[assignment] registry_types_to_keys = {k: REGISTRY_TYPE_TO_KEY[k] for k in registry_types} custom_reg_paths = args.get("customRegistryPaths") if custom_reg_paths: for reg_path in argToList(custom_reg_paths): reg_path = reg_path.strip() if reg_path: if CUSTOM_REG_TYPE not in registry_types_to_keys: registry_types_to_keys[CUSTOM_REG_TYPE] = [] registry_types_to_keys[CUSTOM_REG_TYPE].append(reg_path) records, type_records = get_reg_results(reg, registry_types_to_keys) hr_max_results = arg_to_number(args.get("hrMaxResults")) or MAX_HR_RESULTS hr = tableToMarkdown("Registry Results", records[:hr_max_results]) return_outputs(hr, {"RegistryForensicDataRaw": records, "RegistryForensicData": type_records}, records) if __name__ in ["__main__", "__builtin__", "builtins"]: main()
README
This command uses the Registry Parse automation to extract critical forensics data from a registry file. The essential values are specified by the argument.
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
| Tags | |
| Cortex XSOAR Version | 6.0.0 |
Used In
This script is used in the following playbooks and scripts.
- Registry Parse Data Analysis
Inputs
| Argument Name | Description |
|---|---|
| entryID | This entry ID for the reg file. |
| registryData | This argument allows the user to specify which of the following objects in the registry to parse. Default is “All”. |
| customRegistryPaths | A comma-separated list of registry paths to parse. Try to keep your searches as exact as possible, for example registry_path=`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\AutoExclusionList`. |
Outputs
| Path | Description | Type |
|---|---|---|
| RegistryForensicDataRaw.Type | The registry data type. “Custom” for custom registry path. | Unknown |
| RegistryForensicDataRaw.RegistryPath | The registry key path. | Unknown |
| RegistryForensicDataRaw.RegistryKey | The registry key. | Unknown |
| RegistryForensicDataRaw.RegistryValue | The registry value. | Unknown |
| RegistryForensicData.Users.Sid | User SID. | Unknown |
| RegistryForensicData.Users.Guid | User GUID. | Unknown |
| RegistryForensicData.LastLoggedOnUser | Last user to be logged in. | Unknown |
| RegistryForensicData.TimeZone | Registry ime zone. | Unknown |
| RegistryForensicData.Services.DisplayName | Registry service name. | Unknown |