Query Details

Suspicious Windows Policy And Security Registry Tampering

Query

// Derived from Sigma ccc946a3-f65a-432a-bb6f-efc6acc1fd27
let Lookback = 1h;
let CorrelationWindow = 10m;
let MinimumDistinctRestrictions = 3;
let PolicyRules = datatable(
    PolicyPath:string, ValueName:string, DangerousMode:string, Critical:bool
)
[
    @"software\microsoft\windows\currentversion\policies\system", "consentpromptbehavioradmin", "zero-machine", true,
    @"software\microsoft\windows\currentversion\policies\system", "disableregistrytools", "one-or-two", true,
    @"software\microsoft\windows\currentversion\policies\system", "disabletaskmgr", "one", true,
    @"software\policies\microsoft\windows\system", "disablecmd", "one-or-two", true,
    @"software\microsoft\windows\currentversion\policies\system", "disablechangepassword", "one", false,
    @"software\microsoft\windows\currentversion\policies\system", "disablelockworkstation", "one", false,
    @"software\policies\microsoft\windows\explorer", "disablenotificationcenter", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "hidescahealth", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "hidescanetwork", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "hidescavolume", "one", false,
    @"software\microsoft\windows\currentversion\policies\system", "inactivitytimeoutsecs", "zero-machine", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "noclose", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "nocontrolpanel", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "nodesktop", "one", false,
    @"software\microsoft\windows\currentversion\policies\system", "nodispbackgroundpage", "one", false,
    @"software\microsoft\windows\currentversion\policies\system", "nodispcpl", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "nodrives", "nonzero", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "nofilemenu", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "nofind", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "nologoff", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "nopropertiesmydocuments", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "norun", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "nosettaskbar", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "notraycontextmenu", "one", false,
    @"software\microsoft\windows\currentversion\policies\explorer", "startmenulogoff", "one", false,
    @"software\microsoft\windows\currentversion\policies\system", "shutdownwithoutlogon", "one-machine", false
];
let PolicyValueNames = toscalar(PolicyRules | summarize make_set(ValueName));
let SuspiciousParentNames = dynamic([
    "winword.exe", "excel.exe", "powerpnt.exe", "outlook.exe",
    "mshta.exe", "wscript.exe", "cscript.exe"
]);
let PowerShellNames = dynamic(["powershell.exe", "pwsh.exe", "pwsh.dll"]);
let RiskyWriterPathRegex =
    @"(?i)^[a-z]:\\(?:users\\[^\\]+\\(?:downloads|desktop|appdata\\(?:local|locallow|roaming))|users\\public|windows\\temp|programdata|perflogs)(?:\\|$)";
// Matches -e, -en ... -encodedcommand and -ec with "-", "/", en dash, em dash or horizontal bar as prefix.
let EncodedCommandRegex =
    @"(?i)(?:^|\s)[-/\x{2013}\x{2014}\x{2015}](?:ec|e(?:n(?:c(?:o(?:d(?:e(?:d(?:c(?:o(?:m(?:m(?:a(?:n(?:d)?)?)?)?)?)?)?)?)?)?)?)?)?)\s+\S+";
DeviceRegistryEvents
// Overlap by one correlation window so sessions across run boundaries are complete.
| where Timestamp >= ago(Lookback + CorrelationWindow)
| where ActionType == "RegistryValueSet"
// Cheap prefilters before any string transformation or regex.
| where RegistryValueName in~ (PolicyValueNames)
| where RegistryKey has "Policies"
| where RegistryValueType in~ ("Dword", "REG_DWORD")
| extend NormalizedKey = tolower(RegistryKey),
         ValueName = tolower(RegistryValueName)
// HKCU may be reported with the user SID (HKEY_CURRENT_USER\S-1-5-21-...\Software).
| extend TargetHive = extract(
    @"^(hkey_local_machine|hkey_current_user(?:\\s-1-[0-9-]+)?|hkey_users\\s-1-[0-9-]+)\\software\\",
    1, NormalizedKey)
| where isnotempty(TargetHive)
| extend PolicyPath = substring(NormalizedKey, strlen(TargetHive) + 1)
| lookup kind=inner PolicyRules on PolicyPath, ValueName
| extend NumericData = tolong(trim(@"\s+", RegistryValueData)),
         PreviousNumericData = tolong(trim(@"\s+", PreviousRegistryValueData))
| where isnotnull(NumericData)
| where
    (DangerousMode == "one" and NumericData == 1)
    or (DangerousMode == "one-or-two" and NumericData in (1, 2))
    or (DangerousMode == "nonzero" and NumericData != 0)
    or (DangerousMode == "zero-machine" and NumericData == 0
        and TargetHive == "hkey_local_machine")
    or (DangerousMode == "one-machine" and NumericData == 1
        and TargetHive == "hkey_local_machine")
// Unknown previous data is retained; known numerical no-op writes are removed.
| where isnull(PreviousNumericData) or PreviousNumericData != NumericData
| extend OriginalFileName = column_ifexists("InitiatingProcessVersionInfoOriginalFileName", "")
| extend IsPowerShell = InitiatingProcessFileName in~ (PowerShellNames)
             or OriginalFileName in~ (PowerShellNames)
| extend WriterInRiskyPath = InitiatingProcessFolderPath matches regex RiskyWriterPathRegex,
         SuspiciousDirectParent = InitiatingProcessParentFileName in~ (SuspiciousParentNames),
         EncodedPowerShell = IsPowerShell
             and InitiatingProcessCommandLine matches regex EncodedCommandRegex
| where WriterInRiskyPath or SuspiciousDirectParent or EncodedPowerShell
| extend ContextReason = case(
    WriterInRiskyPath, "Registry writer executing from a risky path",
    SuspiciousDirectParent, "Office or script-host direct parent",
    "PowerShell with an encoded-command argument")
// Sibling reg.exe processes can share a cmd/PowerShell/script-host parent.
| extend UseParentActor = InitiatingProcessFileName =~ "reg.exe"
    and InitiatingProcessParentFileName in~ (
        "cmd.exe", "powershell.exe", "pwsh.exe", "wscript.exe", "cscript.exe", "mshta.exe")
    and InitiatingProcessParentId > 0
    and isnotnull(InitiatingProcessParentCreationTime)
| extend ActorId = case(
    UseParentActor,
        strcat("parent:", InitiatingProcessParentId, ":", InitiatingProcessParentCreationTime),
    isnotempty(InitiatingProcessUniqueId),
        strcat("process:", InitiatingProcessUniqueId),
    isnotnull(InitiatingProcessCreationTime),
        strcat("process:", InitiatingProcessId, ":", InitiatingProcessCreationTime),
    strcat("event:", DeviceName, ":", Timestamp, ":", ReportId))
| sort by DeviceId asc, InitiatingProcessAccountSid asc, TargetHive asc, ActorId asc, Timestamp asc
| extend SessionStart = row_window_session(
    Timestamp, CorrelationWindow, CorrelationWindow,
    DeviceId != prev(DeviceId)
        or InitiatingProcessAccountSid != prev(InitiatingProcessAccountSid)
        or TargetHive != prev(TargetHive)
        or ActorId != prev(ActorId))
| summarize
    FirstSeen = min(Timestamp),
    LastSeen = max(Timestamp),
    CriticalWrites = countif(Critical),
    ChangedSettings = make_set(ValueName, 64),
    RegistryEvidence = make_set(
        pack("Key", RegistryKey, "Name", RegistryValueName,
             "Data", RegistryValueData, "PreviousData", PreviousRegistryValueData), 64),
    ContextReasons = make_set(ContextReason, 8),
    arg_max(Timestamp, ReportId, DeviceName, RegistryKey, RegistryValueName,
        RegistryValueData, PreviousRegistryValueData,
        InitiatingProcessFileName, InitiatingProcessFolderPath,
        InitiatingProcessCommandLine, InitiatingProcessSHA1, InitiatingProcessSHA256,
        InitiatingProcessAccountName, InitiatingProcessAccountDomain,
        InitiatingProcessId, InitiatingProcessCreationTime,
        InitiatingProcessParentFileName, InitiatingProcessParentId,
        InitiatingProcessParentCreationTime)
    by DeviceId, InitiatingProcessAccountSid, TargetHive, ActorId, SessionStart
// Only report sessions that ended inside the current run window;
// sessions that ended in the overlap were evaluated by the previous run.
| where LastSeen >= ago(Lookback)
| extend DistinctSettings = array_length(ChangedSettings)
| where CriticalWrites > 0 or DistinctSettings >= MinimumDistinctRestrictions
| extend DetectionReason = iff(
    CriticalWrites > 0,
    "High-impact registry tampering with suspicious execution context",
    "Multiple restrictive registry settings with suspicious execution context")
| project
    Timestamp, DeviceId, DeviceName, ReportId,
    DetectionReason, FirstSeen, LastSeen, DistinctSettings, CriticalWrites,
    ChangedSettings, RegistryEvidence, ContextReasons, TargetHive, ActorId,
    RegistryKey, RegistryValueName, RegistryValueData, PreviousRegistryValueData,
    InitiatingProcessAccountSid, InitiatingProcessAccountDomain, InitiatingProcessAccountName,
    InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine,
    InitiatingProcessSHA1, InitiatingProcessSHA256, InitiatingProcessId,
    InitiatingProcessCreationTime, InitiatingProcessParentFileName,
    InitiatingProcessParentId, InitiatingProcessParentCreationTime

About this query

Explanation

This query is designed to detect suspicious changes to Windows registry settings that could indicate malicious activity, such as ransomware or malware attempting to weaken system defenses or lock down the desktop. Here's a simplified breakdown of what the query does:

  1. Purpose: The query looks for suspicious modifications to Windows registry settings that control system policies and security features. These changes could be indicative of malicious behavior.

  2. Techniques Monitored: It focuses on registry modifications related to:

    • Disabling or modifying security tools.
    • Bypassing user account control (UAC).
    • Modifying registry settings.
  3. Registry Settings: The query checks for changes to specific registry settings, such as disabling the task manager, registry tools, command prompt, and various Explorer restrictions.

  4. Execution Context: It filters registry changes based on the context in which they occur. Suspicious contexts include:

    • Processes running from user-writable or temporary paths.
    • Processes with direct parent processes that are Office applications or script hosts.
    • PowerShell processes started with encoded command arguments.
  5. Session Grouping: Changes are grouped into sessions based on device, user account, registry hive, and the process making the change. Sessions are reported if they contain critical changes or multiple different settings are altered.

  6. Detection Criteria: A session is flagged if it includes at least one critical registry change or multiple distinct settings are altered in a suspicious context.

  7. Output: The query outputs details about the detected sessions, including the device, user account, registry keys affected, and the context of the changes.

  8. Customization: Parameters like the lookback period, correlation window, and minimum distinct restrictions can be adjusted to fine-tune the detection.

Overall, this query helps identify potential security threats by monitoring for unusual and potentially harmful changes to critical Windows registry settings.