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.
- Type
- python
- Pack
- WindowsForensics
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 |