{
  "schema_version": "1.0",
  "generated": "2026-06-15T01:48:51Z",
  "count": 221,
  "use_cases": [
    {
      "id": "UC_BEACONING",
      "title": "Beaconing \u2014 periodic outbound to small set of destinations",
      "kill_chain": "c2",
      "confidence": "Medium",
      "description": "C2 channel detection via inter-beacon-time stddev / fan-out to single dest.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1071.001",
          "name": "Web Protocols"
        },
        {
          "id": "T1071.004",
          "name": "DNS"
        }
      ],
      "data_models": [
        "Network_Traffic.All_Traffic",
        "DeviceNetworkEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports\n    from datamodel=Network_Traffic.All_Traffic\n    where All_Traffic.action=\"allowed\" AND All_Traffic.dest_category!=\"internal\"\n    by _time span=10s, All_Traffic.src, All_Traffic.dest\n| `drop_dm_object_name(All_Traffic)`\n| streamstats current=f last(_time) AS prev_time by src, dest\n| eval delta = _time - prev_time\n| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest\n| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600\n| sort - count",
      "defender_kql": "DeviceNetworkEvents\n| where Timestamp > ago(1d)\n| where RemoteIPType == \"Public\" and ActionType == \"ConnectionSuccess\"\n| project DeviceName, RemoteIP, RemotePort, Timestamp\n| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc\n| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),\n         prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)\n| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port\n| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)\n| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)\n    by DeviceName, RemoteIP, RemotePort\n| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0\n| order by conn_count desc"
    },
    {
      "id": "UC_BROWSER_EXT",
      "title": "Suspicious browser extension installation",
      "kill_chain": "install",
      "confidence": "Medium",
      "description": "Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1176",
          "name": "Browser Extensions"
        }
      ],
      "data_models": [
        "Endpoint.Registry",
        "DeviceRegistryEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Registry\n    where (Registry.registry_path=\"*\\Software\\Google\\Chrome\\Extensions\\*\"\n        OR Registry.registry_path=\"*\\Software\\Microsoft\\Edge\\Extensions\\*\"\n        OR Registry.registry_path=\"*\\Software\\Mozilla\\Firefox\\Extensions\\*\")\n    by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user\n| `drop_dm_object_name(Registry)`",
      "defender_kql": "DeviceRegistryEvents\n| where Timestamp > ago(7d)\n| where InitiatingProcessAccountName !endswith \"$\"\n| where RegistryKey has_any (\"\\Software\\Google\\Chrome\\Extensions\\\",\"\\Software\\Microsoft\\Edge\\Extensions\\\",\"\\Software\\Mozilla\\Firefox\\Extensions\\\")\n| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,\n          InitiatingProcessFileName, InitiatingProcessAccountName"
    },
    {
      "id": "UC_BROWSER_STEALER",
      "title": "Infostealer \u2014 non-browser process accessing browser cookie/login DBs",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1539",
          "name": "Steal Web Session Cookie"
        },
        {
          "id": "T1555.003",
          "name": "Credentials from Web Browsers"
        }
      ],
      "data_models": [
        "Endpoint.Filesystem",
        "DeviceFileEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Filesystem\n    where (Filesystem.file_path=\"*\\Google\\Chrome\\User Data\\*\\Login Data*\"\n        OR Filesystem.file_path=\"*\\Google\\Chrome\\User Data\\*\\Cookies*\"\n        OR Filesystem.file_path=\"*\\Microsoft\\Edge\\User Data\\*\\Login Data*\"\n        OR Filesystem.file_path=\"*\\Mozilla\\Firefox\\Profiles\\*\\logins.json*\"\n        OR Filesystem.file_path=\"*\\Mozilla\\Firefox\\Profiles\\*\\cookies.sqlite*\")\n      AND NOT Filesystem.process_name IN (\"chrome.exe\",\"msedge.exe\",\"firefox.exe\",\"brave.exe\",\"opera.exe\")\n    by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user\n| `drop_dm_object_name(Filesystem)`",
      "defender_kql": "DeviceFileEvents\n| where Timestamp > ago(7d)\n| where InitiatingProcessAccountName !endswith \"$\"\n| where FolderPath has_any (@\"\\Google\\Chrome\\User Data\\\", @\"\\Microsoft\\Edge\\User Data\\\", @\"\\Mozilla\\Firefox\\Profiles\\\")\n| where FileName in~ (\"Login Data\",\"Cookies\",\"logins.json\",\"cookies.sqlite\")\n| where InitiatingProcessFileName !in~ (\"chrome.exe\",\"msedge.exe\",\"firefox.exe\",\"brave.exe\",\"opera.exe\")\n| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType\n"
    },
    {
      "id": "UC_CRYPTO_WALLET",
      "title": "Crypto-wallet file/keystore access by non-wallet process",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Stealers / wallet drainers read keystore.json, MetaMask, Exodus, Atomic, Phantom data.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1005",
          "name": "Data from Local System"
        }
      ],
      "data_models": [
        "Endpoint.Filesystem",
        "DeviceFileEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Filesystem\n    where (Filesystem.file_path=\"*\\Ethereum\\keystore\\*\"\n        OR Filesystem.file_path=\"*\\Bitcoin\\wallet.dat\"\n        OR Filesystem.file_path=\"*\\Exodus\\exodus.wallet*\"\n        OR Filesystem.file_path=\"*\\Electrum\\wallets\\*\"\n        OR Filesystem.file_path=\"*\\MetaMask\\*\"\n        OR Filesystem.file_path=\"*\\Phantom\\*\"\n        OR Filesystem.file_path=\"*\\Atomic\\Local Storage\\*\")\n      AND NOT Filesystem.process_name IN (\"MetaMask.exe\",\"Exodus.exe\",\"Atomic.exe\",\"electrum.exe\",\"Bitcoin.exe\",\"Phantom.exe\")\n    by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user\n| `drop_dm_object_name(Filesystem)`",
      "defender_kql": "DeviceFileEvents\n| where Timestamp > ago(7d)\n| where InitiatingProcessAccountName !endswith \"$\"\n| where FolderPath has_any (@\"\\Ethereum\\keystore\\\", @\"\\Bitcoin\\\", @\"\\Exodus\\\", @\"\\Electrum\\wallets\\\", @\"\\MetaMask\\\", @\"\\Phantom\\\", @\"\\Atomic\\Local Storage\\\")\n| where InitiatingProcessFileName !in~ (\"MetaMask.exe\",\"Exodus.exe\",\"Atomic.exe\",\"electrum.exe\",\"Bitcoin.exe\",\"Phantom.exe\")\n| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType\n"
    },
    {
      "id": "UC_DDOG_1PASSWORD_FAILED_SIGNIN_BURST",
      "title": "1Password failed sign-in burst",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Failed 1Password logins \u2014 credential spray indicator. Datadog default 'Anomalous amount of failed sign-in attempts by 1Password user'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110",
          "name": "Brute Force"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_1PASSWORD_IMPOSSIBLE_TRAVEL",
      "title": "1Password impossible-travel sign-in",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Geographically impossible 1Password sign-in. Datadog default 'Impossible travel event observed from 1Password user'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1078",
          "name": "Valid Accounts"
        },
        {
          "id": "T1550",
          "name": "Use Alternate Authentication Material"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_1PASSWORD_ITEM_EXFIL_ATTEMPT",
      "title": "1Password item exfiltration attempt",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Suspicious item-level access \u2014 credential-theft pattern. Datadog default 'Attempt to exfiltrate a 1Password item by user'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1555",
          "name": "Credentials from Password Stores"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_1PASSWORD_TOR_ACTIVITY",
      "title": "1Password activity from Tor exit node",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "1Password access from Tor \u2014 credential-vault foreign-source flag. Datadog default '1Password activity observed from Tor client IP'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1090.003",
          "name": "Multi-hop Proxy"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_1PASSWORD_VAULT_EXPORT",
      "title": "1Password vault export attempted",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Vault export \u2014 bulk credential exfil. Datadog default '1Password vault export attempt by user'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1537",
          "name": "Transfer Data to Cloud Account"
        },
        {
          "id": "T1555",
          "name": "Credentials from Password Stores"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_ABNORMAL_BRUTE_FORCE",
      "title": "Abnormal Security: brute-force attack detected",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Abnormal Security flagged brute-force activity. Datadog default 'Abnormal Security Brute Force Attack'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110",
          "name": "Brute Force"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_ABNORMAL_LOGIN_NEW_LOCATION",
      "title": "Abnormal Security: login from new location",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Login from a previously-unseen geo for the user. Datadog default 'Abnormal Security Login New Location'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1078.004",
          "name": "Cloud Accounts"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_ABNORMAL_MALICIOUS_EMAIL",
      "title": "Abnormal Security: malicious email opened",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "User opened a malicious email Abnormal flagged. Datadog default 'Abnormal Security Malicious Email Opened'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1566.001",
          "name": "Spearphishing Attachment"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_API_ADMIN_NO_AUTH",
      "title": "Authentication not detected on admin API endpoint",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Admin API route serving 2xx responses without an Authorization header or session cookie \u2014 exposed admin surface. Datadog default 'Authentication not detected on admin endpoint'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_API_PII_UNAUTH",
      "title": "Unauthenticated route returns sensitive PII",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "API endpoint without authentication returning PII (email, phone, SSN-shape, card-shape) in its response body \u2014 direct data-exposure path. Datadog default 'Unauthenticated route returns sensitive PII'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1526",
          "name": "Cloud Service Discovery"
        },
        {
          "id": "T1538",
          "name": "Cloud Service Dashboard"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_API_RESOURCE_ABUSE",
      "title": "Excessive resource consumption of third-party API",
      "kill_chain": "actions",
      "confidence": "Medium",
      "description": "Abnormal request volume to a third-party API endpoint \u2014 resource abuse, scraping, or DoS-staging behaviour. Datadog default 'Excessive resource consumption of third-party API'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1496",
          "name": "Resource Hijacking"
        },
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_APP_JWT_BYPASS_ATTEMPT",
      "title": "JWT authentication bypass attempt",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "JWT manipulation / none-alg / kid-abuse signatures. Datadog default 'JWT authentication bypass attempt'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_APP_LFI_EXPLOITED",
      "title": "Local File Inclusion (LFI) exploited",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "LFI WAF rule fired with 2xx response \u2014 successful file read. Datadog default 'Local file inclusion exploited'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_APP_SPRING4SHELL_RCE",
      "title": "Spring4Shell RCE attempts (CVE-2022-22963)",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Spring4Shell exploitation attempts. Datadog default 'Spring4shell RCE attempts - CVE-2022-22963'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_APP_USER_TOR",
      "title": "Application user activity from Tor",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "App-side activity from Tor exit \u2014 anonymisation flag. Datadog default 'User activity from Tor'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1090.003",
          "name": "Multi-hop Proxy"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_ATLASSIAN_ADMIN_GROUP_ADD",
      "title": "Atlassian user added to administrative group",
      "kill_chain": "install",
      "confidence": "High",
      "description": "User added to an Atlassian admin group. Datadog default 'Atlassian user added to administrative group'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_ATLASSIAN_ADMIN_IMPERSONATION",
      "title": "Atlassian administrator impersonating user",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Admin took over a user session \u2014 investigate operational legitimacy. Datadog default 'Atlassian administrator impersonated user'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1078",
          "name": "Valid Accounts"
        },
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AUTH0_ANOMALOUS_PROTECTION_EVENTS",
      "title": "Auth0 anomalous attack-protection event spike",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Spike in Auth0 attack-protection trigger events. Datadog default 'Auth0 Anomalous Attack Protection Events'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110",
          "name": "Brute Force"
        },
        {
          "id": "T1078",
          "name": "Valid Accounts"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AUTH0_BREACHED_PWD_DETECTION_DISABLED",
      "title": "Auth0 breached-password detection disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Breached-password detection turned off \u2014 adversary lowering the auth bar. Datadog default 'Auth0 Breached Password Detection Disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AUTH0_BREACHED_PWD_LOGIN",
      "title": "Auth0 login with known-breached password",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "User authenticated with a credential known from a breach corpus. Datadog default 'Auth0 Breached Password Login'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110.004",
          "name": "Credential Stuffing"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AUTH0_BRUTE_FORCE",
      "title": "Auth0 brute-force attack on user",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Auth0's per-user brute-force lockout fired. Datadog default 'Brute Force Attack on Auth0 User'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110",
          "name": "Brute Force"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AUTH0_BRUTE_FORCE_PROTECTION_DISABLED",
      "title": "Auth0 brute-force protection disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Auth0 brute-force protection disabled. Datadog default 'Auth0 Brute Force Protection Disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        },
        {
          "id": "T1110",
          "name": "Brute Force"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AUTH0_CREDENTIAL_STUFFING",
      "title": "Auth0 credential-stuffing attack",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Auth0's distributed-credential-stuffing detector fired. Datadog default 'Credential Stuffing Attack on Auth0'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110.004",
          "name": "Credential Stuffing"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AUTH0_IMPOSSIBLE_TRAVEL",
      "title": "Auth0 impossible-travel sign-in",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Auth0 successful login from geographically impossible locations. Datadog default 'Impossible Travel Auth0 Login'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1078.004",
          "name": "Cloud Accounts"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AUTH0_SUSPICIOUS_IP_THROTTLING_DISABLED",
      "title": "Auth0 suspicious-IP throttling disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Auth0 suspicious-IP throttling turned off. Datadog default 'Auth0 Suspicious IP Throttling Disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_ACCESS_KEY_CREATED_ANOMALY",
      "title": "AWS access key created (programmatic credential)",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "New IAM access key created \u2014 long-lived credential issued, watch for abuse. Datadog default 'AWS access key creation by unusual principal'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1136.003",
          "name": "Cloud Account"
        },
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_BRUTE_FORCE_CONSOLE_LOGIN",
      "title": "AWS brute-force ConsoleLogin then AssumeRole",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Failed ConsoleLogin attempts against the same identity correlated with a successful AssumeRole call shortly after \u2014 credential-spray landing on a privileged role. Datadog default 'Brute forced ConsoleLogin event correlates with assumed role'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110",
          "name": "Brute Force"
        },
        {
          "id": "T1550.001",
          "name": "Application Access Token"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_CLOUDTRAIL_CONFIG_MODIFIED",
      "title": "AWS CloudTrail logging configuration modified",
      "kill_chain": "install",
      "confidence": "High",
      "description": "CloudTrail logging stopped, trail deleted, or selectors changed \u2014 blinding the audit pipeline. Datadog default 'AWS CloudTrail configuration modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.008",
          "name": "Disable or Modify Cloud Logs"
        },
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_CONFIG_MODIFIED",
      "title": "AWS Config service modified or stopped",
      "kill_chain": "install",
      "confidence": "High",
      "description": "AWS Config recorder stopped or deleted \u2014 compliance trail blinded. Datadog default 'AWS Config modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.008",
          "name": "Disable or Modify Cloud Logs"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_CONSOLELOGIN_NO_MFA",
      "title": "AWS Console login without MFA + impossible travel",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "AWS ConsoleLogin events for an identity that didn't use MFA, where the same principal also signed in from a geographically distant location within a tight window (impossible travel). Mirrors Datadog's default rule 'AWS ConsoleLogin without MFA triggered Impossible Travel scenario'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1078",
          "name": "Valid Accounts"
        },
        {
          "id": "T1550",
          "name": "Use Alternate Authentication Material"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_EC2_SECGROUP_MODIFIED",
      "title": "AWS EC2 security group rules modified",
      "kill_chain": "install",
      "confidence": "High",
      "description": "EC2 security-group rules changed \u2014 adversary opening a path or covering tracks. Datadog default 'AWS EC2 security group modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_ECS_CLUSTER_DELETED",
      "title": "AWS ECS cluster deleted",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "ECS cluster removed \u2014 workload impact. Datadog default 'AWS ECS cluster deleted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1485",
          "name": "Data Destruction"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_GUARDDUTY_DISABLED",
      "title": "AWS GuardDuty detector disabled or deleted",
      "kill_chain": "install",
      "confidence": "High",
      "description": "GuardDuty turned off \u2014 defenders' main AWS-native threat detection silenced. Datadog default 'AWS GuardDuty detector disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.001",
          "name": "Disable or Modify Tools"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_IAM_ADMIN_GRANTED",
      "title": "AWS IAM AdministratorAccess policy applied to a user",
      "kill_chain": "install",
      "confidence": "High",
      "description": "An IAM user / group / role just got AdministratorAccess \u2014 direct admin grant rather than going through the org's standard provisioning path. Datadog default 'AWS IAM AdministratorAccess policy was applied to a user'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        },
        {
          "id": "T1548",
          "name": "Abuse Elevation Control Mechanism"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_IAM_IMPOSSIBLE_TRAVEL",
      "title": "Impossible travel observed for IAM user",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "IAM user activity from geographically distant locations within an impossible-travel window. Datadog default 'Impossible travel observed on IAM user'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1078.004",
          "name": "Cloud Accounts"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_IAM_POLICY_MODIFIED",
      "title": "AWS IAM policy created / updated / version changed",
      "kill_chain": "install",
      "confidence": "High",
      "description": "IAM policy added or version changed \u2014 privilege drift. Datadog default 'AWS IAM policy modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098.003",
          "name": "Additional Cloud Roles"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_KMS_KEY_DELETION",
      "title": "AWS KMS key deleted or scheduled for deletion",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "KMS key scheduled for deletion or deleted outright \u2014 disables decryption of everything the key wrapped (S3, EBS, RDS). Ransomware / insider-impact pattern. Datadog default 'AWS KMS key deleted or scheduled for deletion'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1485",
          "name": "Data Destruction"
        },
        {
          "id": "T1531",
          "name": "Account Access Removal"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_LAMBDA_MODIFIED",
      "title": "AWS Lambda function code or configuration modified",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Lambda function code/config changed \u2014 adversary persistence via serverless backdoor. Datadog default 'AWS Lambda function modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1648",
          "name": "Serverless Execution"
        },
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_RDS_CLUSTER_DELETED",
      "title": "AWS RDS DB cluster deleted",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "RDS DB cluster or instance deleted \u2014 destructive impact. Datadog default 'AWS RDS cluster deleted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1485",
          "name": "Data Destruction"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_ROOT_ACTIVITY",
      "title": "AWS root account activity (any action)",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Any console / API action by the AWS root identity. Steady-state should be ZERO; every hit is an investigation. Datadog default 'AWS root account activity'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1078.004",
          "name": "Cloud Accounts"
        },
        {
          "id": "T1087.004",
          "name": "Cloud Account"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_S3_BUCKET_POLICY_MODIFIED",
      "title": "AWS S3 bucket policy modified",
      "kill_chain": "install",
      "confidence": "High",
      "description": "S3 bucket access policy changed \u2014 pivot to find adversary granting themselves read or write. Datadog default 'AWS S3 bucket policy modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_S3_BUCKET_PUBLIC",
      "title": "AWS S3 bucket ACL / policy made public",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "S3 bucket ACL or policy modified to allow access from AllUsers / AllAuthenticatedUsers, or PublicAccessBlock removed. Either misconfig or intentional data-exposure prep. Datadog default 'AWS S3 Bucket ACL made public'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1537",
          "name": "Transfer Data to Cloud Account"
        },
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_SECURITYHUB_DISABLED",
      "title": "AWS SecurityHub disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "SecurityHub turned off \u2014 aggregated AWS findings silenced. Datadog default 'AWS SecurityHub disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.001",
          "name": "Disable or Modify Tools"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AWS_VPC_FLOW_LOG_DELETED",
      "title": "AWS VPC Flow Log deleted",
      "kill_chain": "install",
      "confidence": "High",
      "description": "VPC Flow Logs deleted \u2014 adversary erasing network-traffic visibility. Datadog default 'AWS VPC Flow Log deleted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.008",
          "name": "Disable or Modify Cloud Logs"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AZURE_AD_BRUTE_FORCE",
      "title": "Azure AD brute-force login",
      "kill_chain": "delivery",
      "confidence": "Medium",
      "description": "Multiple failed Azure AD sign-ins against the same UPN within a short window \u2014 credential spray / brute force. Datadog default 'Azure AD brute force login'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110",
          "name": "Brute Force"
        },
        {
          "id": "T1078.004",
          "name": "Cloud Accounts"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AZURE_AD_GLOBAL_ADMIN_ASSIGNED",
      "title": "Azure AD member assigned Global Administrator role",
      "kill_chain": "install",
      "confidence": "High",
      "description": "A user just got the Azure AD Global Administrator role \u2014 top-of-tenant privilege. Datadog default 'Azure AD member assigned Global Administrator role'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        },
        {
          "id": "T1548",
          "name": "Abuse Elevation Control Mechanism"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AZURE_AD_MFA_DISABLED",
      "title": "Azure AD MFA disabled for a user",
      "kill_chain": "install",
      "confidence": "High",
      "description": "MFA was turned off for a user \u2014 adversary post-compromise persistence step (remove the second factor before re-using the credential). Datadog default 'Azure AD MFA disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        },
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AZURE_DIAGNOSTIC_SETTING_DELETED",
      "title": "Azure diagnostic setting deleted",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Azure diagnostic-setting deletion blinds activity-log forwarding to SIEM. Datadog default 'Azure diagnostic setting deleted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.008",
          "name": "Disable or Modify Cloud Logs"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AZURE_KEYVAULT_KEYS_ACCESSED",
      "title": "Azure Key Vault keys / secrets read",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Key Vault secret/key reads \u2014 pivot for credential-theft tradecraft. Datadog default 'Azure Key Vault access keys viewed'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1555.005",
          "name": "Password Managers"
        },
        {
          "id": "T1552.001",
          "name": "Credentials In Files"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AZURE_SP_OWNER_ADDED",
      "title": "Azure new owner added to service principal",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Service-principal ownership change \u2014 adversary getting persistent control of an app identity. Datadog default 'Azure new owner added to service principal'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AZURE_SQL_FW_RULE_CREATED",
      "title": "Azure SQL Server firewall rule created",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Azure SQL firewall rule added \u2014 opening DB to a new IP range. Datadog default 'Azure SQL Server Firewall rule created'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AZURE_STORAGE_SOFT_DELETE_DISABLED",
      "title": "Azure storage soft-delete disabled",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Soft-delete turned off \u2014 adversary preparing to destroy blobs without recovery. Datadog default 'Azure storage account soft delete disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1485",
          "name": "Data Destruction"
        },
        {
          "id": "T1490",
          "name": "Inhibit System Recovery"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_AZURE_USER_ADDED_ADMIN_GROUP",
      "title": "Azure user added to administrative group",
      "kill_chain": "install",
      "confidence": "High",
      "description": "User joined an Azure admin group \u2014 privilege escalation path. Datadog default 'Azure user added to administrative group'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_COMMAND_INJECTION_EXPLOITED",
      "title": "Command injection exploited (WAF detection)",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "WAF telemetry showing successful command-injection against a web application \u2014 shell metacharacters in a request that returned 2xx. Datadog default 'Command injection exploited'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CONFLUENCE_GLOBAL_SETTING_CHANGED",
      "title": "Confluence global security setting changed",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Global Confluence security setting modified. Datadog default 'Confluence Global Setting Changed'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        },
        {
          "id": "T1562",
          "name": "Impair Defenses"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CONFLUENCE_PUBLIC_LINK",
      "title": "Confluence page public link created",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Public-link sharing of an internal page. Datadog default 'Confluence Public Link Creation'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1537",
          "name": "Transfer Data to Cloud Account"
        },
        {
          "id": "T1538",
          "name": "Cloud Service Dashboard"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CONFLUENCE_SPACE_EXPORT",
      "title": "Confluence space export",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Space-level export \u2014 bulk content exfil. Datadog default 'Confluence Space Export'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1537",
          "name": "Transfer Data to Cloud Account"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CONTAINER_ESCAPE_ATTEMPT",
      "title": "Container escape attempt detected",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Process tried to break out of container isolation \u2014 direct host-compromise primitive. Datadog default 'Container escape attempt detected'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1611",
          "name": "Escape to Host"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CREDENTIAL_STUFFING",
      "title": "Credential-stuffing attack on application",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Compromised credential pairs reused against a public application \u2014 high-volume login attempts where a small fraction succeed. Datadog default 'Credential Stuffing attack'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110.004",
          "name": "Credential Stuffing"
        },
        {
          "id": "T1078",
          "name": "Valid Accounts"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CROWDSTRIKE_ALERT",
      "title": "CrowdStrike Falcon alert ingested",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Forwarded CrowdStrike sensor detection \u2014 pivot to the original CS console. Datadog default 'CrowdStrike Alert Detection'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1566",
          "name": "Phishing"
        },
        {
          "id": "T1204",
          "name": "User Execution"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_ACCESS_DENIED_SPIKE",
      "title": "AWS CloudTrail AccessDenied spike",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Burst of AccessDenied errors from one principal \u2014 privilege discovery. Datadog default 'CloudTrail Access Denied Spike'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1526",
          "name": "Cloud Service Discovery"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_AMI_PUBLIC_SHARING",
      "title": "AWS EC2 AMI shared publicly",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "AMI launchPermission added 'all' \u2014 image now public. Datadog default 'CloudTrail EC2 AMI Public Sharing'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1537",
          "name": "Transfer Data to Cloud Account"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_BEDROCK_LOGGING_DISABLED",
      "title": "AWS Bedrock model invocation logging disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Bedrock invocation logs disabled. Datadog default 'CloudTrail Bedrock Model Invocation Disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.008",
          "name": "Disable or Modify Cloud Logs"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_CLOUDWATCH_RULE_DELETED",
      "title": "AWS CloudWatch rule deleted",
      "kill_chain": "install",
      "confidence": "High",
      "description": "CloudWatch rule removed \u2014 alerting / automation may be silenced. Datadog default 'CloudTrail CloudWatch Rule Deleted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.001",
          "name": "Disable or Modify Tools"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_DETECTIVE_GRAPH_DELETED",
      "title": "AWS Detective behaviour graph deleted",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detective graph deleted \u2014 investigation trail destroyed. Datadog default 'CloudTrail Detective Graph Deleted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1531",
          "name": "Account Access Removal"
        },
        {
          "id": "T1562.008",
          "name": "Disable or Modify Cloud Logs"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_EBS_DEFAULT_ENCRYPTION_DISABLED",
      "title": "AWS EBS default encryption disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Default EBS encryption turned off \u2014 new volumes plaintext. Datadog default 'CloudTrail EBS Default Encryption Disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.001",
          "name": "Disable or Modify Tools"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_EBS_SNAPSHOT_PUBLIC",
      "title": "AWS EBS snapshot made public",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "EBS snapshot exposed to all-AWS \u2014 bulk data exfil path. Datadog default 'CloudTrail EBS Snapshot Made Public'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1537",
          "name": "Transfer Data to Cloud Account"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_EC2_KEY_CREATION",
      "title": "AWS EC2 key-pair created",
      "kill_chain": "install",
      "confidence": "High",
      "description": "New EC2 key pair issued \u2014 backdoor SSH-access primitive. Datadog default 'CloudTrail EC2 Key Pair Creation'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1199",
          "name": "Trusted Relationship"
        },
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_GUARDDUTY_PUBLISH_DISABLED",
      "title": "AWS GuardDuty findings publishing disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "GuardDuty stopped publishing findings to S3. Datadog default 'CloudTrail GuardDuty Publishing Disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.008",
          "name": "Disable or Modify Cloud Logs"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_NACL_MODIFIED",
      "title": "AWS Network ACL modified",
      "kill_chain": "install",
      "confidence": "High",
      "description": "VPC NACL entries changed \u2014 segmentation drift. Datadog default 'CloudTrail Network ACL Modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_ORG_LEAVE",
      "title": "AWS Organization leave initiated",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Account left the AWS organization \u2014 central control loss. Datadog default 'CloudTrail AWS Organization Leave'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1531",
          "name": "Account Access Removal"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_ROUTE53_LOGGING_DISABLED",
      "title": "AWS Route53 query logging disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "DNS query logs deleted \u2014 name-resolution observability lost. Datadog default 'CloudTrail Route53 Query Logging Disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.008",
          "name": "Disable or Modify Cloud Logs"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_S3_EXFILTRATION",
      "title": "AWS S3 anomalous bulk download (exfil)",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Anomalous S3 read pattern \u2014 pivot for staged exfil. Datadog default 'CloudTrail S3 Bucket Exfiltration'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1537",
          "name": "Transfer Data to Cloud Account"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_S3_PUBLIC_ACCESS_REMOVED",
      "title": "AWS S3 public-access-block removed",
      "kill_chain": "install",
      "confidence": "High",
      "description": "S3 public-access-block disabled \u2014 bucket exposure path. Datadog default 'CloudTrail S3 Public Access Block Removed'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1578.001",
          "name": "Create Cloud Instance"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_CT_SECRETS_RETRIEVAL",
      "title": "AWS Secrets Manager retrieval by unfamiliar principal",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Secret read by a human / non-automation principal \u2014 credential-theft indicator. Datadog default 'CloudTrail Secrets Manager Retrieval'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1555",
          "name": "Credentials from Password Stores"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DATADOG_AUDIT_DISABLED",
      "title": "Datadog audit trail disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Datadog's own audit logging turned off \u2014 meta-blinding. Datadog default 'Datadog audit trail disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.008",
          "name": "Disable or Modify Cloud Logs"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DATADOG_DASHBOARD_PUBLIC",
      "title": "Datadog dashboard made publicly accessible",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Dashboard exposed publicly \u2014 possible PII or token leakage. Datadog default 'Datadog dashboard made publicly accessible'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1538",
          "name": "Cloud Service Dashboard"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DATADOG_LOGIN_METHOD_CHANGED",
      "title": "Datadog organization login method changed",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Org SSO/SAML setting changed \u2014 auth-flow tampering. Datadog default 'Datadog organization login method changed'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DATADOG_SECURITY_RULE_MODIFIED",
      "title": "Datadog security notification rule modified or deleted",
      "kill_chain": "install",
      "confidence": "High",
      "description": "SIEM rule edited or deleted in Datadog \u2014 defender losing detection. Datadog default 'Datadog security notification rule modified or deleted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562",
          "name": "Impair Defenses"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DATADOG_SUSPICIOUS_LOGIN",
      "title": "Datadog suspicious login",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Datadog console login flagged as suspicious by geo / device fingerprint. Datadog default 'Datadog suspicious login'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1078.004",
          "name": "Cloud Accounts"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DATA_EXFIL_SUCCESSFUL",
      "title": "Application data exfiltration successful",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Application telemetry showing a large-volume / sensitive-attribute response served to an external endpoint \u2014 data exfil. Datadog default 'Data exfiltration successful'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        },
        {
          "id": "T1567",
          "name": "Exfiltration Over Web Service"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DISTRIBUTED_CREDENTIAL_STUFFING",
      "title": "Distributed credential-stuffing campaign",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Coordinated credential stuffing across many source IPs \u2014 botnet-style spraying designed to evade per-IP rate limits. Datadog default 'Distributed Credential Stuffing campaign'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110",
          "name": "Brute Force"
        },
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DUO_ADMIN_LOCKOUT",
      "title": "Cisco Duo admin lockout",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Duo admin locked out after excessive failures. Datadog default 'Cisco Duo Administrator Lockout'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110",
          "name": "Brute Force"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DUO_APP_ENUMERATION",
      "title": "Cisco Duo application enumeration",
      "kill_chain": "recon",
      "confidence": "High",
      "description": "User browsing the list of Duo-protected applications. Datadog default 'Cisco Duo Application Enumeration'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1592.001",
          "name": "Hardware"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DUO_BRUTE_FORCE",
      "title": "Cisco Duo brute-force on protected user",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Repeated failed Duo authentications on the same user. Datadog default 'Cisco Duo Brute Force Attack'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110",
          "name": "Brute Force"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DUO_BYPASS_CODE_CREATION",
      "title": "Cisco Duo emergency bypass code created",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Admin generated a Duo bypass code \u2014 auth-fallback risk. Datadog default 'Cisco Duo Bypass Code Creation'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_DUO_FRAUD_PUSH",
      "title": "Cisco Duo fraud-marked push notifications",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "User marked Duo push prompts as fraudulent \u2014 push-bombing attempt. Datadog default 'Cisco Duo Fraud Push Notifications'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1621",
          "name": "Multi-Factor Authentication Request Generation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_FALCO_RUNTIME_ALERT",
      "title": "Falco runtime-security alert",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Falco kernel-level runtime alert \u2014 high-fidelity container/host detection. Datadog default 'Falco Runtime Alert'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1611",
          "name": "Escape to Host"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GCP_EXTERNAL_OWNER_ADDED",
      "title": "GCP project external principal added as owner",
      "kill_chain": "install",
      "confidence": "High",
      "description": "External email granted project ownership \u2014 direct full-takeover risk. Datadog default 'GCP project external principal added as owner'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GCP_FIREWALL_RULE_MODIFIED",
      "title": "GCP Compute Engine firewall rule modified",
      "kill_chain": "install",
      "confidence": "High",
      "description": "VPC firewall rule changed \u2014 adversary opening or covering a path. Datadog default 'GCP Compute Engine firewall rule modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GCP_IAM_ROLE_CREATED",
      "title": "GCP custom IAM role created",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Custom GCP IAM role defined \u2014 pivot for hidden-privilege grants. Datadog default 'GCP IAM role created'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GCP_LOGGING_BUCKET_DELETED",
      "title": "GCP Cloud Logging bucket deleted",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Cloud Logging bucket removed \u2014 audit trail destroyed. Datadog default 'GCP Logging Bucket deleted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.008",
          "name": "Disable or Modify Cloud Logs"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GCP_LOGGING_SINK_MODIFIED",
      "title": "GCP Cloud Logging sink modified",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Cloud Logging sink redirected \u2014 adversary cutting off log forwarding. Datadog default 'GCP logging sink modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.008",
          "name": "Disable or Modify Cloud Logs"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GCP_STORAGE_PERMS_MODIFIED",
      "title": "GCP Cloud Storage bucket permissions modified",
      "kill_chain": "install",
      "confidence": "High",
      "description": "GCS bucket IAM changed \u2014 possible public exposure. Datadog default 'GCP Cloud Storage bucket permissions modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GCP_SVC_ACCT_KEY_CREATED",
      "title": "GCP service-account key created",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "New SA key issued \u2014 long-lived credential exfil risk. Datadog default 'GCP Service Account key created'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1136.003",
          "name": "Cloud Account"
        },
        {
          "id": "T1552",
          "name": "Unsecured Credentials"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_BRANCH_PROTECTION_DISABLED",
      "title": "GitHub branch protection disabled with force-push bypass",
      "kill_chain": "install",
      "confidence": "Medium",
      "description": "Branch protection rules removed and force-push allowed \u2014 adversary clearing the guard rails to overwrite history or push malicious code. Datadog default 'GitHub branch protection disabled with force push bypass'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562",
          "name": "Impair Defenses"
        },
        {
          "id": "T1078",
          "name": "Valid Accounts"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_MASS_REPO_DELETION",
      "title": "GitHub mass repository deletion",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Bulk repo deletion in a tight window \u2014 destructive impact. Datadog default 'GitHub Mass Repository Deletion'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1485",
          "name": "Data Destruction"
        },
        {
          "id": "T1531",
          "name": "Account Access Removal"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_MFA_DISABLED",
      "title": "GitHub organization 2FA requirement removed",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Org-wide 2FA requirement removed \u2014 auth bar lowered. Datadog default 'GitHub MFA requirement disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_ORG_REMOVED_ENTERPRISE",
      "title": "GitHub organization removed from enterprise",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Org dropped out of enterprise scope \u2014 admin oversight loss. Datadog default 'GitHub organization removed from enterprise'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_PAT_CREATED",
      "title": "GitHub personal access token created",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "New PAT issued \u2014 long-lived credential watch-list. Datadog default 'GitHub personal access token created'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1136",
          "name": "Create Account"
        },
        {
          "id": "T1552",
          "name": "Unsecured Credentials"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_PAT_IMPOSSIBLE_TRAVEL",
      "title": "GitHub PAT used from impossible-travel locations",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Same PAT seen from geographically distant locations within an impossible-travel window. Datadog default 'GitHub PAT Impossible Travel'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1078.004",
          "name": "Cloud Accounts"
        },
        {
          "id": "T1550.001",
          "name": "Application Access Token"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_PAT_MASS_CLONE",
      "title": "GitHub personal access token cloning many repositories",
      "kill_chain": "actions",
      "confidence": "Medium",
      "description": "PAT used to clone an unusual number of repositories \u2014 credential theft + source-code exfil. Datadog default 'GitHub personal access token used to clone repositories'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1555",
          "name": "Credentials from Password Stores"
        },
        {
          "id": "T1552",
          "name": "Unsecured Credentials"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_REPO_PUBLIC",
      "title": "GitHub repository visibility changed to public",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Private repo flipped to public \u2014 unintentional or intentional source-code exposure. Datadog default 'GitHub repository visibility changed to public'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1538",
          "name": "Cloud Service Dashboard"
        },
        {
          "id": "T1213.003",
          "name": "Code Repositories"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_REPO_TRANSFER",
      "title": "GitHub repository transfer initiated",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Repo transferred to another owner \u2014 IP-leak path. Datadog default 'GitHub repository transfer initiated'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        },
        {
          "id": "T1537",
          "name": "Transfer Data to Cloud Account"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_SAML_OIDC_DISABLED",
      "title": "GitHub SAML/OIDC SSO disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Org SSO turned off \u2014 auth-bar lowering. Datadog default 'GitHub SAML/OIDC Disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_SECRETS_ENUMERATION",
      "title": "GitHub secrets-API enumeration",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Burst of access to secret-scanning alerts \u2014 adversary harvesting leaked creds. Datadog default 'GitHub Secrets Enumeration via API'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1555",
          "name": "Credentials from Password Stores"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_SECRET_SCANNING_DISABLED",
      "title": "GitHub secret scanning disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Secret-scanning turned off \u2014 credentials in code go un-flagged. Datadog default 'GitHub secret scanning disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1562.001",
          "name": "Disable or Modify Tools"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITHUB_SSH_KEY_SUSPICIOUS",
      "title": "GitHub SSH key added from suspicious IP",
      "kill_chain": "install",
      "confidence": "High",
      "description": "SSH public key added \u2014 backdoor access primitive; pivot via IP geo. Datadog default 'GitHub SSH Key Added Suspicious'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098.004",
          "name": "SSH Authorized Keys"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITLAB_ADMIN_ADDED",
      "title": "GitLab administrator role granted",
      "kill_chain": "install",
      "confidence": "High",
      "description": "User gained admin on GitLab tenant. Datadog default 'GitLab administrator added'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITLAB_BRUTE_FORCE",
      "title": "GitLab brute-force attack",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Repeated failed GitLab logins. Datadog default 'GitLab Brute Force Attack'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110",
          "name": "Brute Force"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITLAB_GROUP_PUBLIC",
      "title": "GitLab group visibility changed to public",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Private GitLab group flipped to public \u2014 exposure. Datadog default 'GitLab group visibility changed to public'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1538",
          "name": "Cloud Service Dashboard"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITLAB_MASS_DOWNLOAD",
      "title": "GitLab mass repository download",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Anomalous spike in clones from one user / IP \u2014 exfil signal. Datadog default 'GitLab Mass Repository Download'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1537",
          "name": "Transfer Data to Cloud Account"
        },
        {
          "id": "T1213.003",
          "name": "Code Repositories"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITLAB_MFA_DISABLED",
      "title": "GitLab user MFA disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "User self-disabled MFA on GitLab. Datadog default 'GitLab MFA Disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITLAB_PASSWORD_RESET_SUSPICIOUS",
      "title": "GitLab password reset from suspicious IP",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Password reset event \u2014 investigate alongside originating IP / geo. Datadog default 'GitLab password reset from suspicious IP'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1078",
          "name": "Valid Accounts"
        },
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITLAB_PAT_GENERATED",
      "title": "GitLab personal access token generated",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "New GitLab PAT issued \u2014 credential watch-list. Datadog default 'GitLab personal access token generated'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1136",
          "name": "Create Account"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITLAB_PROJECT_VISIBILITY",
      "title": "GitLab project visibility changed (public)",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Project flipped to public \u2014 IP-leak risk. Datadog default 'GitLab Project Visibility Changed'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1538",
          "name": "Cloud Service Dashboard"
        },
        {
          "id": "T1213.003",
          "name": "Code Repositories"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GITLAB_SSO_DISABLED",
      "title": "GitLab SSO disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "SSO turned off \u2014 auth fallback to local creds. Datadog default 'GitLab SSO disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GWS_ADMIN_2SV_DISABLED",
      "title": "Google Workspace admin disabled 2SV for OU",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Two-step verification turned off for an organisational unit. Datadog default 'Google Workspace Admin 2SV Disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GWS_EMAIL_FORWARDING_EXTERNAL",
      "title": "Google Workspace email auto-forwarding to external domain",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Auto-forward to a non-corporate domain \u2014 classic mail exfil. Datadog default 'Google Workspace Email Forwarding External'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1114.003",
          "name": "Email Forwarding Rule"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GWS_OAUTH_KEY_ACCOUNT_CHANGES",
      "title": "Google Workspace OAuth key making account changes",
      "kill_chain": "install",
      "confidence": "High",
      "description": "OAuth service-account key creating or modifying users. Datadog default 'Google Workspace OAuth Key Account Changes'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1550.001",
          "name": "Application Access Token"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GWS_SVCACCT_UNFAMILIAR",
      "title": "Google Workspace service account modifying group membership",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Unexpected SA modifying group memberships. Datadog default 'Google Workspace Service Account Unfamiliar Activity'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        },
        {
          "id": "T1078.004",
          "name": "Cloud Accounts"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GWS_TOR_CLIENT_ACCESS",
      "title": "Google Workspace access from Tor exit node",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Workspace activity from Tor \u2014 anonymisation flag. Datadog default 'Google Workspace Tor Client Access'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1090.003",
          "name": "Multi-hop Proxy"
        },
        {
          "id": "T1205.002",
          "name": "Socket Filters"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GWS_USER_2SV_DISABLED",
      "title": "Google Workspace user disabled 2SV on own account",
      "kill_chain": "install",
      "confidence": "High",
      "description": "User self-disabled 2SV \u2014 adversary post-compromise persistence step. Datadog default 'Google Workspace User 2SV Disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_GWS_USER_ADMIN_ASSIGNMENT",
      "title": "Google Workspace admin role assigned to user",
      "kill_chain": "install",
      "confidence": "High",
      "description": "User granted Workspace admin privileges. Datadog default 'Google Workspace User Admin Role Assignment'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098.003",
          "name": "Additional Cloud Roles"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_IMPOSSIBLE_TRAVEL_BUSINESS_LOGIC",
      "title": "Impossible travel from application business-logic event",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Successful authentication for the same user from geographically distant locations within a window that's physically impossible to traverse. Datadog default 'Impossible travel observed from business logic event'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1550",
          "name": "Use Alternate Authentication Material"
        },
        {
          "id": "T1078",
          "name": "Valid Accounts"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_K8S_CLUSTER_ROLE_DELETED",
      "title": "Kubernetes ClusterRole / binding deleted",
      "kill_chain": "install",
      "confidence": "High",
      "description": "ClusterRole / binding removed \u2014 defenders losing access or adversary covering tracks. Datadog default 'Kubernetes ClusterRole binding deleted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1531",
          "name": "Account Access Removal"
        },
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_K8S_PRIVILEGED_POD_CREATED",
      "title": "Kubernetes pod created with privileged flag",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Privileged pod \u2014 direct host-takeover primitive. Datadog default 'Kubernetes pod executed with privileged flag'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1611",
          "name": "Escape to Host"
        },
        {
          "id": "T1548",
          "name": "Abuse Elevation Control Mechanism"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_K8S_RBAC_BINDING_CREATED",
      "title": "Kubernetes RBAC role binding created",
      "kill_chain": "install",
      "confidence": "High",
      "description": "RBAC binding added \u2014 privilege drift inside the cluster. Datadog default 'Kubernetes RBAC role binding created'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_K8S_SECRET_ACCESSED",
      "title": "Kubernetes Secret accessed",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Secret read by a non-system principal \u2014 credential-theft signal. Datadog default 'Kubernetes secret accessed'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1552.007",
          "name": "Container API"
        },
        {
          "id": "T1555",
          "name": "Credentials from Password Stores"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_K8S_WEBHOOK_MODIFIED",
      "title": "Kubernetes admission webhook configuration modified",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Admission webhook changed \u2014 adversary inserting cluster-wide interception. Datadog default 'Kubernetes webhook configuration modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1554",
          "name": "Compromise Host Software Binary"
        },
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_LOG4SHELL_RCE",
      "title": "Log4Shell RCE attempts (CVE-2021-44228)",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "JNDI-lookup payloads (${jndi:ldap://...} / ${jndi:rmi://...}) in HTTP headers or request bodies \u2014 Log4Shell exploitation attempts. Datadog default 'Log4shell RCE attempts (CVE-2021-44228)'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_M365_ADMIN_ROLE_ASSIGNED",
      "title": "M365 admin role assigned to user",
      "kill_chain": "install",
      "confidence": "High",
      "description": "User became an M365 admin \u2014 privilege drift. Datadog default 'M365 admin role assigned'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_M365_FORWARDING_RULE",
      "title": "M365 mail-forwarding rule created",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Auto-forward rule \u2014 classic adversary exfil persistence. Datadog default 'M365 forwarding rule created'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1114.003",
          "name": "Email Forwarding Rule"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_M365_MAILBOX_DELEGATION",
      "title": "M365 mailbox delegation granted",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Inbox delegation \u2014 adversary inbox-rule pre-staging. Datadog default 'M365 mailbox delegation granted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098.002",
          "name": "Additional Email Delegate Permissions"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_M365_MFA_DISABLED",
      "title": "M365 MFA disabled for a user",
      "kill_chain": "install",
      "confidence": "High",
      "description": "MFA removed \u2014 adversary auth-bar lowering. Datadog default 'M365 MFA disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_MONGODB_AUTH_DISABLED",
      "title": "MongoDB authentication disabled",
      "kill_chain": "install",
      "confidence": "High",
      "description": "MongoDB auth turned off \u2014 anyone can connect. Datadog default 'MongoDB authentication disabled'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_MONGODB_DB_DELETED",
      "title": "MongoDB database dropped",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "DB dropped \u2014 destructive event. Datadog default 'MongoDB database deleted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1485",
          "name": "Data Destruction"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_MONGODB_USER_CREATED",
      "title": "MongoDB user created",
      "kill_chain": "install",
      "confidence": "High",
      "description": "New DB user \u2014 pivot for legitimacy review. Datadog default 'MongoDB user created'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1136",
          "name": "Create Account"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_MONGODB_USER_PRIVESCAL",
      "title": "MongoDB user role escalated",
      "kill_chain": "install",
      "confidence": "High",
      "description": "DB user got a higher role \u2014 privilege drift. Datadog default 'MongoDB user privilege escalated'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_OKTA_ADMIN_ROLE_ASSIGNED",
      "title": "Okta administrative role assigned to user",
      "kill_chain": "install",
      "confidence": "High",
      "description": "User gets an Okta admin role \u2014 privilege drift. Datadog default 'Okta administrative role assigned'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_OKTA_APP_ACCESS_GRANTED",
      "title": "Okta application access granted to user",
      "kill_chain": "install",
      "confidence": "High",
      "description": "User added to an Okta-mediated app \u2014 pivot for entitlement audits. Datadog default 'Okta application access granted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_OKTA_AUTH_POLICY_MODIFIED",
      "title": "Okta authentication / sign-on policy modified",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Auth policy / rule changed \u2014 adversary lowering the auth bar. Datadog default 'Okta authentication policy modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_OKTA_MFA_BYPASS",
      "title": "Okta MFA bypass attempt",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "MFA challenge failed in a way that suggests a bypass attempt. Datadog default 'Okta MFA bypass attempt'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_OKTA_USER_LOCKED",
      "title": "Okta user account locked",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Okta user lockout \u2014 credential-spray indicator (and a paste-target for IP / target lists). Datadog default 'Okta user account locked'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1110.003",
          "name": "Password Spraying"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_POSTGRES_AUTH_MODIFIED",
      "title": "PostgreSQL authentication method modified",
      "kill_chain": "install",
      "confidence": "High",
      "description": "pg_hba reload or auth setting change \u2014 bar lowering. Datadog default 'PostgreSQL authentication method modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_POSTGRES_DB_DELETED",
      "title": "PostgreSQL database dropped",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "DB dropped \u2014 destructive event. Datadog default 'PostgreSQL database deleted'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1485",
          "name": "Data Destruction"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_POSTGRES_SUPERUSER_CREATED",
      "title": "PostgreSQL superuser role created",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Superuser role created \u2014 DBA-level privilege drift. Datadog default 'PostgreSQL role created with superuser'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_SNOWFLAKE_ROLE_CREATED",
      "title": "Snowflake role created",
      "kill_chain": "install",
      "confidence": "High",
      "description": "New Snowflake role defined \u2014 privilege drift in the warehouse. Datadog default 'Snowflake role created'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_SNOWFLAKE_SHARE_MODIFIED",
      "title": "Snowflake share created or modified",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Cross-account data share opened \u2014 direct data-exfil primitive. Datadog default 'Snowflake share created or modified'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1537",
          "name": "Transfer Data to Cloud Account"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_SNOWFLAKE_USER_ROLE_ADDED",
      "title": "Snowflake user added to role",
      "kill_chain": "install",
      "confidence": "High",
      "description": "User got new role \u2014 entitlement audit. Datadog default 'Snowflake user added to role'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_SQL_INJECTION_EXPLOITED",
      "title": "SQL injection exploited (WAF detection)",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "WAF telemetry showing successful SQL injection against a web application \u2014 request payload matched SQLi signatures and returned a 2xx instead of being blocked. Datadog default 'SQL injection exploited'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DDOG_SSRF_EXPLOITED",
      "title": "SSRF exploited (WAF detection)",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "WAF telemetry showing successful SSRF \u2014 request includes link-local / metadata IP or internal RFC1918 address as the target, application returned 2xx. Datadog default 'SSRF exploited'.",
      "implementations": [],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1021",
          "name": "Remote Services"
        }
      ],
      "data_models": [],
      "splunk_spl": null,
      "defender_kql": null
    },
    {
      "id": "UC_DNS_TUNNEL",
      "title": "DNS tunneling / TXT-heavy domain queries",
      "kill_chain": "c2",
      "confidence": "Medium",
      "description": "Long subdomain labels + frequent queries to a single 2LD = DNS C2/exfil.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1071.004",
          "name": "DNS"
        },
        {
          "id": "T1048.003",
          "name": "Exfiltration Over Unencrypted Non-C2 Protocol"
        }
      ],
      "data_models": [
        "Network_Resolution.DNS",
        "DeviceNetworkEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count from datamodel=Network_Resolution.DNS\n    where DNS.message_type=\"QUERY\"\n    by DNS.src, DNS.query\n| `drop_dm_object_name(DNS)`\n| eval qlen=len(query)\n| where qlen > 50\n| rex field=query \"(?<second_level_domain>[\\w-]+\\.[\\w-]+)$\"\n| stats sum(count) AS qcount, dc(query) AS unique_subs, max(qlen) AS max_label\n    by src, second_level_domain\n| where qcount > 100 AND unique_subs > 20\n| sort - qcount",
      "defender_kql": "DeviceNetworkEvents\n| where Timestamp > ago(1d)\n| where RemotePort == 53 and isnotempty(RemoteUrl)\n| extend qlen = strlen(RemoteUrl)\n| where qlen > 50\n| extend SecondLevelDomain = extract(@\"([\\w-]+\\.[a-zA-Z]{2,})$\", 1, RemoteUrl)\n| summarize qcount = count(), uniqueSubs = dcount(RemoteUrl), maxLabel = max(qlen)\n    by DeviceName, SecondLevelDomain\n| where qcount > 100 and uniqueSubs > 20\n| order by qcount desc"
    },
    {
      "id": "UC_FAKECAPTCHA",
      "title": "Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Browser-pasted PowerShell launched from explorer/Run dialog \u2014 ClickFix social engineering.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1204.004",
          "name": "User Execution: Malicious Copy and Paste"
        },
        {
          "id": "T1059.001",
          "name": "PowerShell"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "DeviceProcessEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Processes\n    where Processes.parent_process_name IN (\"explorer.exe\",\"RuntimeBroker.exe\")\n      AND Processes.process_name IN (\"powershell.exe\",\"pwsh.exe\",\"mshta.exe\")\n      AND (Processes.process=\"*iex*\" OR Processes.process=\"*Invoke-Expression*\"\n        OR Processes.process=\"*FromBase64*\" OR Processes.process=\"*DownloadString*\"\n        OR Processes.process=\"*hxxp*\" OR Processes.process=\"*curl*\" OR Processes.process=\"*wget*\")\n    by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name\n| `drop_dm_object_name(Processes)`",
      "defender_kql": "DeviceProcessEvents\n| where Timestamp > ago(7d)\n| where AccountName !endswith \"$\"\n| where InitiatingProcessFileName in~ (\"explorer.exe\",\"RuntimeBroker.exe\")\n| where FileName in~ (\"powershell.exe\",\"pwsh.exe\",\"mshta.exe\")\n| where ProcessCommandLine matches regex @\"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )\"\n| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine"
    },
    {
      "id": "UC_HASH_IOC",
      "title": "File hash IOCs \u2014 endpoint file/process match",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1027",
          "name": "Obfuscated Files or Information"
        }
      ],
      "data_models": [
        "Endpoint.Filesystem",
        "Endpoint.Processes",
        "DeviceFileEvents",
        "DeviceProcessEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Filesystem\n    where Filesystem.file_hash IN (__HASH_LIST__)\n    by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash\n| `drop_dm_object_name(Filesystem)`\n| append\n    [| tstats `summariesonly` count from datamodel=Endpoint.Processes\n        where Processes.process_hash IN (__HASH_LIST__)\n        by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash\n     | `drop_dm_object_name(Processes)`]",
      "defender_kql": "// Pre-filter each table BEFORE the union \u2014 the engine cannot push\n// predicates across `union`, and DeviceFileEvents lacks ProcessCommandLine\n// (Initiating only) so column-projection has to differ per side.\nunion\n    ( DeviceFileEvents\n        | where Timestamp > ago(7d)\n        | where InitiatingProcessAccountName !endswith \"$\"\n        | where SHA256 in~ (__HASH_LIST__) or SHA1 in~ (__HASH_LIST__) or MD5 in~ (__HASH_LIST__)\n        | project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256,\n                  Cmd = InitiatingProcessCommandLine, Source = \"DeviceFileEvents\" ),\n    ( DeviceProcessEvents\n        | where Timestamp > ago(7d)\n        | where AccountName !endswith \"$\"\n        | where SHA256 in~ (__HASH_LIST__) or SHA1 in~ (__HASH_LIST__) or MD5 in~ (__HASH_LIST__)\n        | project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256,\n                  Cmd = ProcessCommandLine, Source = \"DeviceProcessEvents\" )\n| order by Timestamp desc\n"
    },
    {
      "id": "UC_LATERAL_PSEXEC",
      "title": "Remote service execution \u2014 PsExec / SMB lateral movement",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "psexec / paexec / smbexec / wmic /node \u2014 service install over SMB from remote host.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1021.002",
          "name": "SMB/Windows Admin Shares"
        },
        {
          "id": "T1569.002",
          "name": "Service Execution"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "DeviceProcessEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Processes\n    where Processes.process_name IN (\"psexec.exe\",\"psexesvc.exe\",\"paexec.exe\",\"smbexec.py\")\n       OR (Processes.process_name=\"wmic.exe\" AND Processes.process=\"*/node:*\")\n    by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name\n| `drop_dm_object_name(Processes)`",
      "defender_kql": "DeviceProcessEvents\n| where Timestamp > ago(7d)\n| where AccountName !endswith \"$\"\n| where FileName in~ (\"psexec.exe\",\"psexesvc.exe\",\"paexec.exe\",\"smbexec.py\")\n   or (FileName =~ \"wmic.exe\" and ProcessCommandLine has \"/node:\")\n| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName\n| order by Timestamp desc\n"
    },
    {
      "id": "UC_LSASS",
      "title": "LSASS process access / dump (credential theft)",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1003.001",
          "name": "LSASS Memory"
        },
        {
          "id": "T1003",
          "name": "OS Credential Dumping"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "DeviceEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Processes\n    where (Processes.process=\"*lsass*\" OR Processes.process=\"*sekurlsa*\"\n        OR Processes.process=\"*MiniDump*\" OR Processes.process=\"*comsvcs.dll*MiniDump*\"\n        OR Processes.process=\"*procdump*lsass*\")\n       OR (Processes.process_name=\"rundll32.exe\" AND Processes.process=\"*comsvcs*MiniDump*\")\n    by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name\n| `drop_dm_object_name(Processes)`",
      "defender_kql": "DeviceEvents\n| where Timestamp > ago(7d)\n| where AccountName !endswith \"$\"\n| where ActionType == \"OpenProcessApiCall\"\n| where FileName =~ \"lsass.exe\"\n| where InitiatingProcessFileName !in~ (\"MsSense.exe\",\"MsMpEng.exe\",\"csrss.exe\",\n                                          \"svchost.exe\",\"wininit.exe\",\"services.exe\",\n                                          \"lsm.exe\",\"SearchProtocolHost.exe\")\n| project Timestamp, DeviceName, ActionType, FileName,\n          InitiatingProcessFileName, InitiatingProcessCommandLine,\n          InitiatingProcessFolderPath, AccountName\n| order by Timestamp desc"
    },
    {
      "id": "UC_MFA_FATIGUE",
      "title": "MFA fatigue / push-bombing",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Many MFA pushes to same user in short window \u2014 suggests adversary attempting approval-fatigue.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1621",
          "name": "Multi-Factor Authentication Request Generation"
        }
      ],
      "data_models": [
        "Authentication.Authentication",
        "AADSignInEventsBeta"
      ],
      "splunk_spl": "| tstats `summariesonly` count from datamodel=Authentication.Authentication\n    where Authentication.action=\"failure\" AND Authentication.signature=\"*MFA*\"\n    by _time span=5m, Authentication.user, Authentication.src\n| `drop_dm_object_name(Authentication)`\n| where count > 10",
      "defender_kql": "AADSignInEventsBeta\n| where Timestamp > ago(1d)\n| where ErrorCode in (50074, 50076, 50158, 50125, 50097)\n| extend MfaPrompt = AuthenticationRequirement == \"multiFactorAuthentication\"\n| where MfaPrompt\n| summarize attempts = count(), distinct_ips = dcount(IPAddress)\n    by AccountUpn, bin(Timestamp, 5m)\n| where attempts > 10\n| order by attempts desc"
    },
    {
      "id": "UC_NETWORK_IOC",
      "title": "Network connections to article IPs / domains",
      "kill_chain": "c2",
      "confidence": "High",
      "description": "Outbound traffic to attacker infrastructure named in the article.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1071",
          "name": "Application Layer Protocol"
        }
      ],
      "data_models": [
        "Network_Traffic.All_Traffic",
        "Web",
        "Network_Resolution.DNS",
        "DeviceNetworkEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Network_Traffic.All_Traffic\n    where All_Traffic.dest IN (__IP_LIST__)\n    by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port\n| `drop_dm_object_name(All_Traffic)`\n| append\n    [| tstats `summariesonly` count from datamodel=Web\n        where Web.dest IN (__DOMAIN_LIST__)\n        by Web.src, Web.dest, Web.url, Web.user\n     | `drop_dm_object_name(Web)`]\n| append\n    [| tstats `summariesonly` count from datamodel=Network_Resolution.DNS\n        where DNS.query IN (__DOMAIN_LIST__)\n        by DNS.src, DNS.query, DNS.answer\n     | `drop_dm_object_name(DNS)`]",
      "defender_kql": "DeviceNetworkEvents\n| where Timestamp > ago(7d)\n| where RemoteIP in (__IP_LIST__) or RemoteUrl has_any (__DOMAIN_LIST__)\n| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,\n          InitiatingProcessFileName, InitiatingProcessCommandLine"
    },
    {
      "id": "UC_OAUTH_ABUSE",
      "title": "OAuth consent / suspicious app grant",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Cloud identity abuse: app gets high-priv scopes, often via consent phishing.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1528",
          "name": "Steal Application Access Token"
        },
        {
          "id": "T1098.001",
          "name": "Account Manipulation: Additional Cloud Credentials"
        }
      ],
      "data_models": [
        "Authentication.Authentication",
        "CloudAppEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Authentication.Authentication\n    where Authentication.action=\"success\"\n      AND Authentication.signature IN (\n        \"Consent to application\",\n        \"Add app role assignment grant to user\",\n        \"Add OAuth2PermissionGrant\",\n        \"Add delegated permission grant\")\n    by Authentication.user, Authentication.app, Authentication.src, Authentication.signature\n| `drop_dm_object_name(Authentication)`",
      "defender_kql": "CloudAppEvents\n| where Timestamp > ago(7d)\n| where ActionType in (\"Consent to application.\",\"Add OAuth2PermissionGrant.\",\"Add delegated permission grant.\")\n| project Timestamp, AccountObjectId, AccountDisplayName, ActivityType,\n          ActivityObjects, IPAddress, UserAgent"
    },
    {
      "id": "UC_OFFICE_CHILD",
      "title": "Office app spawning script/LOLBin child process",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Classic macro/exploit pattern \u2014 Office app launching cmd/powershell/wmic/regsvr32.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1059.001",
          "name": "PowerShell"
        },
        {
          "id": "T1059.005",
          "name": "Visual Basic"
        },
        {
          "id": "T1218",
          "name": "System Binary Proxy Execution"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "DeviceProcessEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Processes\n    where Processes.parent_process_name IN (\"winword.exe\",\"excel.exe\",\"powerpnt.exe\",\"outlook.exe\",\"onenote.exe\",\"mspub.exe\",\"visio.exe\")\n      AND Processes.process_name IN (\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"wscript.exe\",\"cscript.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"wmic.exe\",\"bitsadmin.exe\",\"certutil.exe\")\n    by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process\n| `drop_dm_object_name(Processes)`\n| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`",
      "defender_kql": "DeviceProcessEvents\n| where Timestamp > ago(7d)\n| where AccountName !endswith \"$\"\n| where InitiatingProcessFileName in~ (\"winword.exe\",\"excel.exe\",\"powerpnt.exe\",\"outlook.exe\",\"onenote.exe\",\"mspub.exe\",\"visio.exe\")\n| where FileName in~ (\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"wscript.exe\",\"cscript.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"wmic.exe\",\"bitsadmin.exe\",\"certutil.exe\")\n| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine"
    },
    {
      "id": "UC_PHISH_ATTACH",
      "title": "Email attachment opened from external sender",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Attachment delivery + execution by recipient \u2014 most common malware initial access.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1566.001",
          "name": "Spearphishing Attachment"
        },
        {
          "id": "T1204.002",
          "name": "User Execution: Malicious File"
        }
      ],
      "data_models": [
        "Email.All_Email",
        "Endpoint.Processes",
        "EmailAttachmentInfo",
        "DeviceProcessEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count\n    from datamodel=Email.All_Email\n    where All_Email.file_name!=\"-\"\n    by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject\n| rename All_Email.recipient as user\n| join type=inner user\n    [| tstats `summariesonly` count\n        from datamodel=Endpoint.Processes\n        where Processes.parent_process_name IN (\"OUTLOOK.EXE\",\"winword.exe\",\"excel.exe\",\"powerpnt.exe\")\n          AND Processes.process_name IN (\"cmd.exe\",\"powershell.exe\",\"wscript.exe\",\"cscript.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\")\n        by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process\n     | rename Processes.user as user]",
      "defender_kql": "let LookbackDays = 7d;\nlet MalAttachments = EmailAttachmentInfo\n    | where Timestamp > ago(LookbackDays)\n    | where AccountName !endswith \"$\"\n    | project NetworkMessageId, RecipientEmailAddress,\n              AttachmentFileName = FileName, AttachmentSHA256 = SHA256;\nDeviceProcessEvents\n| where Timestamp > ago(LookbackDays)\n| where InitiatingProcessFileName in~ (\"OUTLOOK.EXE\",\"winword.exe\",\"excel.exe\",\"powerpnt.exe\")\n| where FileName in~ (\"cmd.exe\",\"powershell.exe\",\"wscript.exe\",\"cscript.exe\",\n                      \"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\")\n| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress\n| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,\n          InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256"
    },
    {
      "id": "UC_PHISH_LINK",
      "title": "Phishing-link click correlated to endpoint execution",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "User received an external email containing a URL, clicked it, and within\n60 seconds the recipient's endpoint spawned a scripting interpreter or\nnetwork downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).\nThis is the high-fidelity shape \u2014 a raw \"user clicked an emailed URL\"\nquery is too noisy to alert on because every Microsoft 365 tenant\ngenerates thousands of legitimate clicks per day. The exec-correlation\nstep suppresses the noise to roughly the rate at which a click actually\ndrives onto-host execution, which is the actionable signal.\nPair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN\nfor orthogonal high-fidelity variants.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1566.002",
          "name": "Spearphishing Link"
        },
        {
          "id": "T1204.001",
          "name": "User Execution: Malicious Link"
        },
        {
          "id": "T1059.001",
          "name": "PowerShell"
        }
      ],
      "data_models": [
        "Email.All_Email",
        "Web",
        "Endpoint.Processes",
        "EmailEvents",
        "EmailUrlInfo",
        "UrlClickEvents",
        "DeviceProcessEvents"
      ],
      "splunk_spl": "``` Phishing-link click that drives endpoint execution within 60s ```\n| tstats `summariesonly` earliest(_time) AS click_time\n    from datamodel=Web\n    where Web.action=\"allowed\"\n    by Web.src, Web.user, Web.dest, Web.url\n| `drop_dm_object_name(Web)`\n| rename user AS recipient, dest AS clicked_domain, url AS clicked_url\n| join type=inner recipient\n    [| tstats `summariesonly` count\n         from datamodel=Email.All_Email\n         where All_Email.action=\"delivered\" AND All_Email.url!=\"-\"\n         by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject\n     | `drop_dm_object_name(All_Email)`\n     | rex field=url \"https?://(?<email_domain>[^/]+)\"\n     | rename recipient AS recipient]\n| join type=inner src\n    [| tstats `summariesonly` earliest(_time) AS exec_time\n         values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc\n         from datamodel=Endpoint.Processes\n         where Processes.parent_process_name IN (\"chrome.exe\",\"msedge.exe\",\"firefox.exe\",\n                                                   \"outlook.exe\",\"brave.exe\",\"arc.exe\")\n           AND Processes.process_name IN (\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"mshta.exe\",\n                                            \"rundll32.exe\",\"regsvr32.exe\",\"wscript.exe\",\n                                            \"cscript.exe\",\"bitsadmin.exe\",\"certutil.exe\",\n                                            \"curl.exe\",\"wget.exe\")\n         by Processes.dest, Processes.user\n     | `drop_dm_object_name(Processes)`\n     | rename dest AS src]\n| eval delta_sec = exec_time - click_time\n| where delta_sec >= 0 AND delta_sec <= 60\n| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,\n        clicked_domain, clicked_url, exec_proc, exec_cmd\n| sort - click_time",
      "defender_kql": "// Phishing-link click that drives endpoint execution within 60s.\n// Far higher fidelity than \"every clicked URL\" \u2014 most legitimate clicks\n// never spawn a non-browser child process, so the join eliminates the\n// 99% of noise that makes a raw click query unactionable.\nlet LookbackDays = 7d;\nlet SuspectClicks = UrlClickEvents\n    | where Timestamp > ago(LookbackDays)\n    | where AccountName !endswith \"$\"\n    | where ActionType in (\"ClickAllowed\",\"ClickedThrough\")\n    | join kind=inner (\n        EmailEvents\n        | where Timestamp > ago(LookbackDays)\n        | where DeliveryAction == \"Delivered\"\n        | where EmailDirection == \"Inbound\"\n        | project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,\n                  RecipientEmailAddress, EmailTimestamp = Timestamp\n      ) on NetworkMessageId\n    | join kind=leftouter (\n        EmailUrlInfo | project NetworkMessageId, Url, UrlDomain\n      ) on NetworkMessageId, Url\n    | project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,\n              Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,\n              ActionType;\n// Correlate to a non-browser child process spawned within 60 seconds on\n// the recipient's device.\nDeviceProcessEvents\n| where Timestamp > ago(LookbackDays)\n| where InitiatingProcessFileName in~ (\"chrome.exe\",\"msedge.exe\",\"firefox.exe\",\n                                         \"outlook.exe\",\"brave.exe\",\"arc.exe\")\n| where FileName in~ (\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"mshta.exe\",\n                        \"rundll32.exe\",\"regsvr32.exe\",\"wscript.exe\",\"cscript.exe\",\n                        \"bitsadmin.exe\",\"certutil.exe\",\"curl.exe\",\"wget.exe\")\n| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn\n| where Timestamp between (ClickTime .. ClickTime + 60s)\n| project ClickTime, ProcessTime = Timestamp,\n          DelaySec = datetime_diff('second', Timestamp, ClickTime),\n          DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,\n          Subject, Url, UrlDomain, ActionType,\n          FileName, ProcessCommandLine, InitiatingProcessFileName\n| order by ClickTime desc"
    },
    {
      "id": "UC_PHISH_LINK_BYPASS",
      "title": "User clicked through a Safe Links warning page",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Microsoft Safe Links rendered a warning page for a URL and the user\nclicked past it anyway (ActionType=ClickedThrough). This is an\nexplicit user-override of a defender control \u2014 by definition rare and\nhigh-signal. Investigate every hit; the URL was flagged for a reason.",
      "implementations": [
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1566.002",
          "name": "Spearphishing Link"
        },
        {
          "id": "T1204.001",
          "name": "User Execution: Malicious Link"
        }
      ],
      "data_models": [
        "UrlClickEvents",
        "EmailEvents",
        "EmailUrlInfo"
      ],
      "splunk_spl": null,
      "defender_kql": "// Safe Links assessed the URL as risky, showed a warning page, and the\n// user proceeded anyway. ClickedThrough events are inherently rare \u2014\n// every hit deserves analyst review. UrlDomain isn't on UrlClickEvents\n// or EmailEvents \u2014 it's only on EmailUrlInfo, so we left-outer join it.\nUrlClickEvents\n| where Timestamp > ago(30d)\n| where ActionType == \"ClickedThrough\"\n| join kind=leftouter (\n    EmailEvents\n    | where Timestamp > ago(30d)\n    | project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,\n              RecipientEmailAddress, EmailDirection\n  ) on NetworkMessageId\n| join kind=leftouter (\n    EmailUrlInfo\n    | where Timestamp > ago(30d)\n    | project NetworkMessageId, Url, UrlDomain\n  ) on NetworkMessageId, Url\n| project Timestamp, AccountUpn, IPAddress, Url, UrlDomain,\n          EmailDirection, SenderFromAddress, SenderFromDomain,\n          RecipientEmailAddress, Subject, Workload\n| order by Timestamp desc\n"
    },
    {
      "id": "UC_PHISH_LINK_NEWDOMAIN",
      "title": "Click on URL whose host doesn't match the sender domain",
      "kill_chain": "delivery",
      "confidence": "Medium",
      "description": "User clicked an inbound-email URL where the URL host is unrelated to\nthe sender's authenticated domain. Most legitimate B2B / B2C senders\nuse links on their own domain, their CDN, or a small number of\nmarketing trackers. A click on a sender-mismatched URL \u2014 especially\nto a cheap-TLD throwaway (.sbs, .top, .icu, .cyou) \u2014 is a moderate\nphishing-class signal. Pair with UC_PHISH_LINK (exec correlation)\nfor the higher-confidence variant.",
      "implementations": [
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1566.002",
          "name": "Spearphishing Link"
        },
        {
          "id": "T1204.001",
          "name": "User Execution: Malicious Link"
        },
        {
          "id": "T1583.001",
          "name": "Acquire Infrastructure: Domains"
        }
      ],
      "data_models": [
        "UrlClickEvents",
        "EmailEvents",
        "EmailUrlInfo"
      ],
      "splunk_spl": null,
      "defender_kql": "// Click on inbound-email URL where url-domain != sender-domain.\n// The sender-domain root and url-domain root are extracted then\n// compared; a mismatch on a cheap-TLD url-domain is the high-signal\n// case. Tune the legit-marketer allowlist to your environment to\n// suppress the residual newsletter / vendor-tracker noise.\nlet LookbackDays = 7d;\nlet LegitTrackers = dynamic([\"mailchimp.com\",\"sendgrid.net\",\"sendgrid.com\",\n    \"constantcontact.com\",\"hubspotlinks.com\",\"hubspot.com\",\"mktoresp.com\",\n    \"marketo.com\",\"pardot.com\",\"exacttarget.com\",\"klaviyomail.com\",\n    \"list-manage.com\",\"createsend1.com\",\"createsend.com\",\"sg.actblue.com\",\n    \"ml-attach.com\",\"emltrk.com\",\"amazonses.com\",\"amazon.com\",\"sndarsffrt.com\",\n    \"salesforce.com\",\"force.com\",\"docusign.net\",\"echosign.com\",\n    \"office365.com\",\"microsoft.com\",\"onmicrosoft.com\",\"linkedin.com\",\n    \"github.com\",\"githubusercontent.com\",\"atlassian.net\",\"jira.com\"]);\nlet CheapTlds = dynamic([\"sbs\",\"life\",\"top\",\"icu\",\"cyou\",\"gdn\",\"click\",\"loan\",\n    \"rest\",\"monster\",\"work\",\"cn\",\"ru\",\"tk\",\"ml\",\"cf\",\"ga\"]);\nEmailEvents\n| where Timestamp > ago(LookbackDays)\n| where DeliveryAction == \"Delivered\" and EmailDirection == \"Inbound\"\n| extend SenderRoot = extract(@\"([^.]+\\.[a-zA-Z]+)$\", 1, tolower(SenderFromDomain))\n| join kind=inner (\n    EmailUrlInfo | project NetworkMessageId, Url, UrlDomain\n  ) on NetworkMessageId\n| join kind=inner (\n    UrlClickEvents\n    | where Timestamp > ago(LookbackDays)\n    | where ActionType in (\"ClickAllowed\",\"ClickedThrough\")\n    | project Url, ClickTimestamp = Timestamp, AccountUpn, IPAddress\n  ) on Url\n| extend UrlDomLower = tolower(UrlDomain)\n| extend UrlRoot = extract(@\"([^.]+\\.[a-zA-Z]+)$\", 1, UrlDomLower)\n| extend UrlTld = extract(@\"\\.([a-zA-Z]+)$\", 1, UrlDomLower)\n| where UrlRoot != SenderRoot\n   and UrlRoot !in (LegitTrackers)\n   and UrlDomLower !endswith \".microsoft.com\"\n   and UrlDomLower !endswith \".office.com\"\n   and UrlDomLower !endswith \".sharepoint.com\"\n| extend RiskScore = case(UrlTld in (CheapTlds), 3,\n                          UrlRoot endswith \".ru\" or UrlRoot endswith \".cn\", 2,\n                          1)\n| where RiskScore >= 2\n| project ClickTimestamp, AccountUpn, IPAddress, RecipientEmailAddress,\n          SenderFromAddress, SenderRoot, UrlDomain, UrlRoot, UrlTld,\n          RiskScore, Subject, Url\n| order by RiskScore desc, ClickTimestamp desc"
    },
    {
      "id": "UC_PS_OBFUSCATED",
      "title": "PowerShell encoded / obfuscated command",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Encoded or obfuscated PowerShell \u2014 common across loaders, recon, and post-exploitation.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1059.001",
          "name": "PowerShell"
        },
        {
          "id": "T1027",
          "name": "Obfuscated Files or Information"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "DeviceProcessEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Processes\n    where Processes.process_name IN (\"powershell.exe\",\"pwsh.exe\")\n      AND (Processes.process=\"*-enc *\" OR Processes.process=\"*EncodedCommand*\"\n        OR Processes.process=\"*FromBase64String*\" OR Processes.process=\"*-nop*\"\n        OR Processes.process=\"*-w hidden*\" OR Processes.process=\"*Invoke-Expression*\"\n        OR Processes.process=\"*IEX(*\" OR Processes.process=\"*DownloadString*\"\n        OR Processes.process=\"*Net.WebClient*\")\n    by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name\n| `drop_dm_object_name(Processes)`",
      "defender_kql": "DeviceProcessEvents\n| where Timestamp > ago(7d)\n| where AccountName !endswith \"$\"\n| where FileName in~ (\"powershell.exe\",\"pwsh.exe\")\n| where ProcessCommandLine matches regex @\"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\\s+hidden|invoke-expression|iex\\s*\\(|downloadstring|net\\.webclient)\"\n| project Timestamp, DeviceName, AccountName, ProcessCommandLine,\n          InitiatingProcessFileName, InitiatingProcessCommandLine"
    },
    {
      "id": "UC_RANSOM_ENCRYPT",
      "title": "Ransomware-style mass file rename / extension change",
      "kill_chain": "actions",
      "confidence": "Medium",
      "description": "Threshold detection: many files renamed in short window, often with new extension.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1486",
          "name": "Data Encrypted for Impact"
        }
      ],
      "data_models": [
        "Endpoint.Filesystem",
        "DeviceFileEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count, dc(Filesystem.file_name) AS files\n    from datamodel=Endpoint.Filesystem\n    where Filesystem.action IN (\"modified\",\"renamed\")\n    by Filesystem.dest, Filesystem.user, _time span=1m\n| `drop_dm_object_name(Filesystem)`\n| where files > 200\n| sort - files",
      "defender_kql": "DeviceFileEvents\n| where Timestamp > ago(1d)\n| where InitiatingProcessAccountName !endswith \"$\"\n| where ActionType in (\"FileRenamed\",\"FileModified\")\n| summarize files = dcount(FileName) by DeviceName, InitiatingProcessAccountName, bin(Timestamp, 1m)\n| where files > 200    // empirical: > 200 unique-file renames in 1m by one account on one host\n                       //            is well above the P99 of legitimate bulk-tooling\n| order by files desc\n"
    },
    {
      "id": "UC_RMM_TOOLS",
      "title": "RMM tool installed by non-IT user \u2014 remote-access utility for hands-on-keyboard",
      "kill_chain": "install",
      "confidence": "High",
      "description": "ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1219",
          "name": "Remote Access Software"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "DeviceProcessEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Processes\n    where Processes.process_name IN (\"AnyDesk.exe\",\"TeamViewer.exe\",\"TeamViewer_Service.exe\",\n        \"ScreenConnect.ClientService.exe\",\"ConnectWiseControl.ClientService.exe\",\n        \"atera_agent.exe\",\"SplashtopStreamer.exe\",\"RustDesk.exe\",\"NinjaOne.exe\",\"kaseya*.exe\")\n    by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name\n| `drop_dm_object_name(Processes)`",
      "defender_kql": "DeviceProcessEvents\n| where Timestamp > ago(7d)\n| where AccountName !endswith \"$\"\n| where FileName in~ (\"AnyDesk.exe\",\"TeamViewer.exe\",\"TeamViewer_Service.exe\",\n        \"ScreenConnect.ClientService.exe\",\"ConnectWiseControl.ClientService.exe\",\n        \"atera_agent.exe\",\"SplashtopStreamer.exe\",\"RustDesk.exe\",\"NinjaOne.exe\")\n   or FileName matches regex @\"(?i)kaseya.*\\.exe\"\n| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine"
    },
    {
      "id": "UC_SCHEDULED_TASK",
      "title": "Scheduled task created with suspicious image / encoded args",
      "kill_chain": "install",
      "confidence": "High",
      "description": "schtasks.exe /create or Microsoft-Windows-TaskScheduler EventID 4698 with LOLBin actions.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1053.005",
          "name": "Scheduled Task"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "DeviceProcessEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Processes\n    where Processes.process_name=\"schtasks.exe\" AND Processes.process=\"*/create*\"\n      AND (Processes.process=\"*powershell*\" OR Processes.process=\"*cmd.exe*\"\n        OR Processes.process=\"*rundll32*\" OR Processes.process=\"*-enc*\"\n        OR Processes.process=\"*FromBase64*\" OR Processes.process=\"*\\Users\\Public*\"\n        OR Processes.process=\"*\\AppData\\*\")\n    by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name\n| `drop_dm_object_name(Processes)`",
      "defender_kql": "DeviceProcessEvents\n| where Timestamp > ago(7d)\n| where AccountName !endswith \"$\"\n| where FileName =~ \"schtasks.exe\"\n| where ProcessCommandLine has \"/create\"\n| where ProcessCommandLine has_any (\"powershell\",\"cmd.exe\",\"rundll32\",\"-enc\",\"FromBase64\",\"\\Users\\Public\",\"\\AppData\\\")\n| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName"
    },
    {
      "id": "UC_SERVICE_PERSIST",
      "title": "Service install for persistence \u2014 sc.exe / new service registry write",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Service install with binPath pointing to user-writeable path or LOLBin. Detects via process telemetry (sc.exe create) and registry (HKLM\\SYSTEM\\CurrentControlSet\\Services\\* writes).",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1543.003",
          "name": "Windows Service"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Registry",
        "DeviceProcessEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Processes\n    where Processes.process_name=\"sc.exe\" AND Processes.process=\"*create*\"\n      AND (Processes.process=\"*\\Users\\*\" OR Processes.process=\"*\\AppData\\*\"\n        OR Processes.process=\"*\\ProgramData\\*\" OR Processes.process=\"*\\Temp\\*\")\n    by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name\n| `drop_dm_object_name(Processes)`\n| append\n    [| tstats `summariesonly` count from datamodel=Endpoint.Registry\n        where Registry.registry_path=\"*\\\\SYSTEM\\\\CurrentControlSet\\\\Services\\\\*\"\n          AND Registry.registry_value_name=\"ImagePath\"\n          AND (Registry.registry_value_data=\"*\\Users\\*\"\n            OR Registry.registry_value_data=\"*\\AppData\\*\"\n            OR Registry.registry_value_data=\"*\\Temp\\*\")\n        by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.user\n     | `drop_dm_object_name(Registry)`]",
      "defender_kql": "DeviceProcessEvents\n| where Timestamp > ago(7d)\n| where AccountName !endswith \"$\"\n| where FileName =~ \"sc.exe\" and ProcessCommandLine has \"create\"\n| where ProcessCommandLine matches regex @\"(?i)(\\Users\\|\\AppData\\|\\ProgramData\\|\\Temp\\)\"\n| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName"
    },
    {
      "id": "UC_SUPPLY_CHAIN",
      "title": "Trusted vendor binary / installer launching unusual children",
      "kill_chain": "exploit",
      "confidence": "Medium",
      "description": "Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "DeviceProcessEvents"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Endpoint.Processes\n    where Processes.parent_process_name IN (__VENDOR_BINS__)\n      AND Processes.process_name IN (\"powershell.exe\",\"cmd.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"mshta.exe\",\"wscript.exe\",\"cscript.exe\",\"wmic.exe\",\"bitsadmin.exe\")\n    by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process\n| `drop_dm_object_name(Processes)`",
      "defender_kql": "DeviceProcessEvents\n| where Timestamp > ago(7d)\n| where AccountName !endswith \"$\"\n| where InitiatingProcessFileName in~ (__VENDOR_BINS__)\n| where FileName in~ (\"powershell.exe\",\"cmd.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"mshta.exe\",\"wscript.exe\",\"cscript.exe\",\"wmic.exe\",\"bitsadmin.exe\")\n| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine"
    },
    {
      "id": "UC_TEAMS_VISHING",
      "title": "Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonator",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "External Teams chat where displayName contains 'helpdesk' or 'IT support' \u2014 common 2024+ vishing pattern (Storm-1811, Black Basta, UNC6692). No CIM data model maps to Teams chats; uses raw O365 audit logs.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1566.004",
          "name": "Phishing: Spearphishing Voice"
        },
        {
          "id": "T1566",
          "name": "Phishing"
        }
      ],
      "data_models": [
        "CloudAppEvents"
      ],
      "splunk_spl": "`o365_management_activity`\n  Workload=MicrosoftTeams Operation=MessageSent\n  ExternalParticipants=*\n| where match(SenderDisplayName, \"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)\")\n| stats count, earliest(_time) as firstTime, latest(_time) as lastTime\n    by SenderUpn, SenderDisplayName, RecipientUpn, ChatId",
      "defender_kql": "CloudAppEvents\n| where Timestamp > ago(7d)\n| where Application == \"Microsoft Teams\"\n| where ActionType == \"MessageSent\"\n| where RawEventData has \"ExternalParticipants\"\n| extend SenderDisplayName = tostring(parse_json(RawEventData).SenderDisplayName)\n| where SenderDisplayName matches regex @\"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)\"\n| project Timestamp, AccountDisplayName, IPAddress, ActivityType, SenderDisplayName, RawEventData"
    },
    {
      "id": "UC_VULN_EXPOSURE",
      "title": "Asset exposure \u2014 vulnerability matches article CVE(s)",
      "kill_chain": "recon",
      "confidence": "High",
      "description": "Match vulnerability scanner output to the CVE(s) named in the article.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        }
      ],
      "data_models": [
        "Vulnerabilities",
        "DeviceInfo",
        "DeviceTvmSoftwareVulnerabilities"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime\n    from datamodel=Vulnerabilities\n    where Vulnerabilities.signature IN (__CVE_LIST__)\n    by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve\n| `drop_dm_object_name(Vulnerabilities)`\n| sort - severity",
      "defender_kql": "// TVM tables are daily snapshots \u2014 pin a 1-day window so the engine\n// partitions on Timestamp instead of scanning the full retention.\nDeviceTvmSoftwareVulnerabilities\n| where Timestamp > ago(1d)\n| where CveId in~ (__CVE_LIST__)\n| join kind=inner (DeviceInfo | where Timestamp > ago(1d)) on DeviceId\n| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate\n| order by VulnerabilitySeverityLevel asc, DeviceName asc\n"
    },
    {
      "id": "UC_WEEKLY_AUTH_BYPASS_ON_PUBLIC_FACING_SERVICE_POST_EXPLOIT_",
      "title": "[WEEKLY] Auth-Bypass on Public-Facing Service \u2192 Post-Exploit Action on Same Host (\u226410 min)",
      "kill_chain": "exploit",
      "confidence": "Medium",
      "description": "Correlates an externally-sourced inbound connection to an internet-exposed service (PAN-OS GlobalProtect portal, default-auth-disabled Flask APIs emitted by PraisonAI `deploy --type api`, Linux hosts running cgroup-v1 services, K8s/Docker control planes) with a follow-on suspicious process or privileged action on the SAME host within a 10-minute window. This fortnight CISA added CVE-2026-0257 (PAN-OS GlobalProtect authentication bypass \u2014 actively exploited per The Hacker News' 'PAN-OS GlobalProtect Authentication Bypass (CVE-2026-0257) Under Active Exploitation') and CVE-2022-0492 (Linux Kernel Improper Authentication via cgroup v1 release_agent \u2014 see 'CISA Warns of Linux Kernel Improper Authentication Vulnerability Exploited in Attacks') to KEV, and GHSA published CVE-2026-47393 ('PraisonAI deploy --type api emits a Flask server with authentication disabled by default') \u2014 three independent disclosures of the same shape: authentication is missing or bypassed at the edge, immediately followed by attacker action on the asset. Kill-chain stages spanned: exploit \u2192 execution \u2192 privilege-escalation / actions-on-objectives. Fires when an inbound public-IP connection to a high-risk service port is followed within \u226410 minutes by a release_agent write, a reverse-shell-shaped command line, a LOLBin egress, or a new high-privilege process on the receiving host; does NOT fire on routine authenticated user traffic, internally-sourced automation from RFC1918 ranges, or when the post-event process matches the host's 30-day baseline.\n\nRationale: All three primary disclosures this fortnight (PAN-OS GP auth bypass, PraisonAI Flask no-auth, Linux cgroup release_agent improper-auth privesc) hand the attacker an unauthenticated foothold on an internet-reachable host and depend on the SAME post-condition to deliver value \u2014 a process on the receiving host either elevates (release_agent), shells out (reverse shell / -EncodedCommand), or fetches a stage-2 (curl/wget/certutil). Catching that \u226410-minute edge-in\u2192host-action chain detects the joint pattern without depending on the IOC-of-the-week (the listed source IPs and C2 domains will rotate; the temporal shape persists).",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1133",
          "name": "External Remote Services"
        },
        {
          "id": "T1068",
          "name": "Exploitation for Privilege Escalation"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        },
        {
          "id": "T1071",
          "name": "Application Layer Protocol"
        }
      ],
      "data_models": [
        "Network_Traffic.All_Traffic",
        "Endpoint.Processes"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as edge_time max(_time) as edge_last from datamodel=Network_Traffic.All_Traffic where All_Traffic.direction=\"inbound\" AND All_Traffic.action=\"allowed\" AND All_Traffic.dest_port IN (443,4443,5000,8000,8080,6443) by All_Traffic.dest All_Traffic.src All_Traffic.dest_port\n| `drop_dm_object_name(All_Traffic)`\n| search NOT src IN (\"10.0.0.0/8\",\"172.16.0.0/12\",\"192.168.0.0/16\")\n| rename dest as edge_host\n| join type=inner edge_host [\n    | tstats `summariesonly` count min(_time) as proc_time values(Processes.process) as process_cmd values(Processes.process_name) as process_name values(Processes.user) as proc_user values(Processes.parent_process_name) as parent_name from datamodel=Endpoint.Processes where (Processes.process IN (\"*release_agent*\",\"*/sys/fs/cgroup*\",\"*bash -i*\",\"*sh -i*\",\"*/dev/tcp/*\",\"*nc -e*\",\"*nc.exe -e*\",\"*python -c*socket*\",\"*Invoke-Expression*\",\"*DownloadString*\",\"*-EncodedCommand*\") OR Processes.process_name IN (\"bash\",\"sh\",\"dash\",\"python\",\"python3\",\"powershell.exe\",\"pwsh.exe\",\"nc\",\"ncat\",\"certutil.exe\",\"bitsadmin.exe\",\"curl.exe\",\"wget.exe\")) by Processes.dest\n    | `drop_dm_object_name(Processes)`\n    | rename dest as edge_host\n  ]\n| where proc_time >= edge_time AND proc_time <= (edge_time + 600)\n| eval delay_sec = proc_time - edge_time\n| convert ctime(edge_time) ctime(proc_time)\n| table edge_time, proc_time, delay_sec, edge_host, src, dest_port, proc_user, parent_name, process_name, process_cmd\n| sort 0 - edge_time",
      "defender_kql": "let LookbackDays = 14d;\nlet Window = 10m;\nlet EdgePorts = dynamic([443,4443,5000,8000,8080,6443]);\nlet EdgeIns = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackDays)\n    | where ActionType in (\"InboundConnectionAccepted\",\"ListeningConnectionCreated\")\n    | where LocalPort in (EdgePorts)\n    | where RemoteIPType == \"Public\"\n    | project EdgeTs = Timestamp, DeviceName, RemoteIP, LocalPort,\n              EdgeListenProcess = InitiatingProcessFileName;\nlet PostExploit = DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where (ProcessCommandLine has_any (\"release_agent\",\"/sys/fs/cgroup\",\"bash -i\",\"sh -i\",\"/dev/tcp/\",\"nc -e\",\"nc.exe -e\",\"reverse shell\",\"python -c\",\"Invoke-Expression\",\"DownloadString\",\"-EncodedCommand\"))\n         or FileName in~ (\"bash\",\"sh\",\"dash\",\"python\",\"python3\",\"powershell.exe\",\"pwsh.exe\",\"nc\",\"ncat\",\"certutil.exe\",\"bitsadmin.exe\",\"curl.exe\",\"wget.exe\")\n    | where AccountName !endswith \"$\"\n    | where AccountName !in~ (\"system\",\"local service\",\"network service\")\n    | project PostTs = Timestamp, DeviceName, FileName, ProcessCommandLine,\n              InitiatingProcessFileName, InitiatingProcessCommandLine,\n              AccountName, SHA256;\nEdgeIns\n| join kind=inner PostExploit on DeviceName\n| where PostTs between (EdgeTs .. EdgeTs + Window)\n| extend DelaySec = datetime_diff('second', PostTs, EdgeTs)\n| project EdgeTs, PostTs, DelaySec, DeviceName, RemoteIP, LocalPort,\n          EdgeListenProcess, AccountName, FileName, ProcessCommandLine,\n          InitiatingProcessFileName, SHA256\n| order by EdgeTs desc"
    },
    {
      "id": "UC_WEEKLY_BRAND_IMPERSONATION_DOMAIN_FETCH_FOLLOWED_BY_USER_",
      "title": "[WEEKLY] Brand-Impersonation Domain Fetch Followed by User-Context Loader Within 10 Minutes",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Detects the cross-campaign tradecraft where a user-context process (browser, IDE, terminal, installer, package manager) contacts a domain or URL impersonating a trusted AI / developer / mobile brand (Claude, Anthropic, OpenAI, Hugging Face, OpenVSX, OpenClaw, macOS utilities, Google Play companions, etc.) and the same host then spawns a script-interpreter or LOLBin loader (PowerShell, mshta, rundll32, regsvr32, cscript, certutil, bitsadmin, curl/wget, msiexec, node, python, osascript, bash) within a 10-minute window. This fortnight saw an unusual cluster of corroborated brand-impersonation campaigns driving this hook: the Microsoft Security Blog 'ClickFix macOS utilities' write-up, BleepingComputer's 'Fake Claude AI website delivers Beagle' and 'Fake OpenAI repository on Hugging Face' reports, Cyber Security News on the 'Fake OpenClaw installer' and 'InstallFix Fake Claude installer pages', THN/BleepingComputer 'GlassWorm v2 across 73 fake VS Code extensions', THN's 'DPRK AI-inserted npm malware', THN/ESET 'CallPhantom 28 fake call-history apps', THN's 'Fake CAPTCHA / Keitaro ClickFix', and Cyber Security News' 'Fake moustache age-bypass'. Kill-chain coverage spans delivery (impersonation lure) \u2192 user execution \u2192 install/loader \u2192 c2 fetch, so the joint signal survives even as IOCs rotate. Fires only when BOTH legs occur on the same device in order; does NOT fire on the lookalike-domain visit alone (analyst-fatigue trap) and does NOT fire on script-interpreter / LOLBin spawn without the impersonation precursor \u2014 those cases are covered by other catalogue rules.\n\nRationale: All twelve articles converge on brand-impersonation as the delivery hook (fake Claude / OpenAI / Hugging Face / OpenVSX / OpenClaw / macOS utility / Keitaro CAPTCHA / Play-Store companion apps), and the post-lure behaviour in every case is a user-context script interpreter or LOLBin staging credential theft or further C2 fetch. Joining a lookalike-brand network leg with a short-window loader spawn on the same host catches that joint chain without leaning on IOCs that the actors are already rotating weekly.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1583.008",
          "name": "Acquire Infrastructure: Malvertising"
        },
        {
          "id": "T1656",
          "name": "Impersonation"
        },
        {
          "id": "T1566.002",
          "name": "Phishing: Spearphishing Link"
        },
        {
          "id": "T1204.001",
          "name": "User Execution: Malicious Link"
        },
        {
          "id": "T1204.002",
          "name": "User Execution: Malicious File"
        },
        {
          "id": "T1204.004",
          "name": "User Execution: Malicious Copy and Paste"
        },
        {
          "id": "T1195.002",
          "name": "Supply Chain Compromise: Compromise Software Supply Chain"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1218.005",
          "name": "System Binary Proxy Execution: Mshta"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        }
      ],
      "data_models": [
        "Network_Traffic.All_Traffic",
        "Web",
        "Endpoint.Processes"
      ],
      "splunk_spl": "``` Brand-impersonation domain contact + user-context loader within 10 min (joint pattern across THN/BleepingComputer/Microsoft/ESET/Cyber Security News briefings) ```\n| tstats `summariesonly` earliest(_time) AS dl_time\n    values(All_Traffic.dest) AS dl_dest values(All_Traffic.dest_ip) AS dl_ip values(All_Traffic.app) AS dl_app values(All_Traffic.dest) AS dl_url\n    from datamodel=Network_Traffic.All_Traffic\n    where All_Traffic.action=\"allowed\" AND All_Traffic.dest_category!=\"internal\"\n      AND (\n           All_Traffic.dest=\"*claude-pro*\" OR All_Traffic.dest=\"*claudecode*\"\n        OR All_Traffic.dest=\"*anthropic-*\" OR All_Traffic.dest=\"*openai-*\"\n        OR All_Traffic.dest=\"*huggingface-*\" OR All_Traffic.dest=\"*openclaw*\"\n        OR All_Traffic.dest=\"*download-version.1-5-8*\" OR All_Traffic.dest=\"*claudecodedoc*\"\n        OR All_Traffic.dest=\"*macclean*\" OR All_Traffic.dest=\"*mac-storage-guide*\"\n        OR All_Traffic.dest=\"*csec-c2-server*\" OR All_Traffic.dest=\"*steamhostserver*\"\n        OR All_Traffic.dest=\"*serverconect*\" OR All_Traffic.dest=\"*transcloud*\"\n        OR All_Traffic.dest=\"*recargapopular*\" OR All_Traffic.dest=\"*.msixbundle*\"\n        OR All_Traffic.dest=\"*claude-pro*\" OR All_Traffic.dest=\"*download-version.1-5-8*\"\n        OR All_Traffic.dest IN (<brand_lookalike_domain_list>)\n        OR All_Traffic.dest_ip IN (<c2_ip_list>)\n      )\n    by All_Traffic.src All_Traffic.user\n| `drop_dm_object_name(All_Traffic)`\n| rename src AS host\n| join type=inner host\n    [| tstats `summariesonly` earliest(_time) AS exec_time\n        values(Processes.process_name) AS child_proc values(Processes.process) AS child_cmd\n        values(Processes.parent_process_name) AS parent_proc values(Processes.process_hash) AS child_hash\n        from datamodel=Endpoint.Processes\n        where Processes.process_name IN (\"powershell.exe\",\"pwsh.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"cscript.exe\",\"wscript.exe\",\"cmd.exe\",\"bitsadmin.exe\",\"certutil.exe\",\"curl.exe\",\"wget.exe\",\"msiexec.exe\",\"node.exe\",\"python.exe\",\"python3.exe\",\"osascript\",\"bash\",\"sh\")\n          AND Processes.user!=\"*$\"\n        by Processes.dest Processes.user\n     | `drop_dm_object_name(Processes)`\n     | rename dest AS host]\n| eval delta_sec = exec_time - dl_time\n| where delta_sec >= 0 AND delta_sec <= 600\n| table host user dl_time exec_time delta_sec dl_app dl_dest dl_url dl_ip parent_proc child_proc child_cmd child_hash\n| sort - dl_time",
      "defender_kql": "let LookbackDays = 7d;\nlet WindowMin = 10m;\nlet _impersonation_regex = @\"(?i)(claude\\-pro|claudecode|claudecodedoc|anthropic\\-[a-z0-9]|openai\\-[a-z0-9]|huggingface\\-[a-z0-9]|openvsx\\-[a-z0-9]|openclaw|cursor\\-[a-z0-9]|gemini\\-[a-z0-9]|copilot\\-[a-z0-9]|claude\\.msixbundle|download\\-version\\.1\\-5\\-8|macclean|mac\\-storage\\-guide|cleanmymacos|recargapopular|csec\\-c2\\-server|steamhostserver|serverconect|transcloud)\";\nlet _suspicious_children = dynamic([\"powershell.exe\",\"pwsh.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"cscript.exe\",\"wscript.exe\",\"cmd.exe\",\"bitsadmin.exe\",\"certutil.exe\",\"curl.exe\",\"wget.exe\",\"msiexec.exe\",\"node.exe\",\"python.exe\",\"python3.exe\",\"osascript\",\"bash\",\"sh\"]);\nlet _user_facing_parents = dynamic([\"msedge.exe\",\"chrome.exe\",\"firefox.exe\",\"brave.exe\",\"arc.exe\",\"opera.exe\",\"Code.exe\",\"cursor.exe\",\"explorer.exe\",\"RuntimeBroker.exe\",\"outlook.exe\",\"teams.exe\",\"slack.exe\",\"WindowsTerminal.exe\",\"Terminal\",\"iTerm2\",\"Safari\"]);\nlet Downloads = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackDays)\n    | where ActionType in (\"ConnectionSuccess\",\"HttpConnectionInspected\")\n    | where RemoteIPType == \"Public\"\n    | where isnotempty(RemoteUrl)\n    | where RemoteUrl matches regex _impersonation_regex\n         or tostring(parse_url(RemoteUrl)[\"Host\"]) matches regex _impersonation_regex\n    | project DlTime = Timestamp, DeviceId, DeviceName,\n              DlUrl = RemoteUrl, DlIp = RemoteIP, DlPort = RemotePort,\n              DlInitiator = InitiatingProcessFileName,\n              DlAccount = InitiatingProcessAccountName;\nDeviceProcessEvents\n| where Timestamp > ago(LookbackDays)\n| where AccountName !endswith \"$\"\n| where tolower(FileName) in (_suspicious_children)\n   and InitiatingProcessFileName in~ (_user_facing_parents)\n| project ExecTime = Timestamp, DeviceId, DeviceName, AccountName,\n          ChildProc = FileName, ChildCmd = ProcessCommandLine,\n          ParentProc = InitiatingProcessFileName,\n          ParentCmd = InitiatingProcessCommandLine,\n          ChildHash = SHA256\n| join kind=inner Downloads on DeviceId\n| where ExecTime between (DlTime .. DlTime + WindowMin)\n| extend DelaySec = datetime_diff('second', ExecTime, DlTime)\n| project DlTime, ExecTime, DelaySec, DeviceName, AccountName,\n          DlUrl, DlIp, DlInitiator, ParentProc, ChildProc, ChildCmd, ChildHash\n| order by DlTime desc"
    },
    {
      "id": "UC_WEEKLY_CROSS_CATEGORY_CREDENTIAL_STORE_ENUMERATION_WITH_R",
      "title": "[WEEKLY] Cross-category credential-store enumeration with rapid egress to anonymizing tunnel/CDN",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Detects a single endpoint process that touches credential material from THREE or more distinct categories \u2014 browser logins/cookies, DevOps & cloud secrets (.aws, .kube, .npmrc, .pypirc, .docker, .git-credentials, .netrc), SSH private keys, DPAPI/Vault/Credential-Manager stores, password-manager vaults, or crypto-wallet directories \u2014 within a 5-minute window, AND the same host egresses to an anonymizing tunnel or CDN (bore.pub, *.trycloudflare.com, *.ngrok.io, *.workers.dev, *.web.app, pastebin/transfer.sh, Telegram API) within 10 minutes of the last credential read. This fortnight saw at least eight distinct intrusion lures converge on identical actions-on-objectives: PCPJack cloud-worm and TeamPCP-eviction (SentinelLabs, BleepingComputer 2026-05-07), PamDOORa PAM backdoor on Linux (THN/CSN 2026-05-08), QLNX Quasar Linux RAT (THN 2026-05-08), MuddyWater Teams-vishing false-flag ransomware (THN 2026-05-06), CloudZ RAT/Pheno plugin via Windows Phone Link (THN 2026-05-06), DEEP#DOOR Python backdoor exfilling over bore.pub (THN 2026-04-30), malicious NuGet packages (CSN 2026-05-07), Fake OpenClaw installer hitting 250+ extensions (CSN 2026-05-08), event-invitation phishing (CSN 2026-05-07), and the SAP-npm supply-chain compromise (BleepingComputer 2026-04-29) \u2014 all delivery paths differ but the collection+exfiltration stage is the same heterogeneous-secret fan-out followed by tunneled egress. Spans Collection (T1555/T1552/T1539) and Exfiltration (T1041/T1567/T1572) \u2014 the only two kill-chain stages every article shares. FIRES when one process_guid (or PID on the same host within 60s) reads \u22653 distinct credential CATEGORIES then the host egresses to a tunneling/CDN host or otherwise-rare public destination. DOES NOT FIRE on browsers reading only their own cookie/Login-Data store, native password managers reading their own vault, git/aws-cli/kubectl/npm reading a single category for legitimate use, or baselined backup/EDR/SCM agents.\n\nRationale: Every one of the 12 articles converges on the same actions-on-objectives step regardless of the unique delivery vector \u2014 enumerate every credential store the user has and exfiltrate them, often via an anonymizing tunnel (bore.pub in DEEP#DOOR, *.cloudfront-js.com in PCPJack, *.njalla in NuGet packages, Telegram/transfer.sh elsewhere). Catching the rare combination 'one process touches \u22653 credential-store CATEGORIES inside 5 min, then the host beacons to a tunneling/CDN host within 10 min' is invariant to which CVE was exploited, which lure was used, or which IOC rotates next week, while remaining structurally rare in legitimate use (browsers, password managers, and dev CLIs each stay inside a single category).",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1555",
          "name": "Credentials from Password Stores"
        },
        {
          "id": "T1555.003",
          "name": "Credentials from Web Browsers"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1539",
          "name": "Steal Web Session Cookie"
        },
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        },
        {
          "id": "T1572",
          "name": "Protocol Tunneling"
        }
      ],
      "data_models": [
        "Endpoint.Filesystem",
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as touched_paths from datamodel=Endpoint.Filesystem where Filesystem.action IN (\"read\",\"opened\",\"accessed\",\"queried\") AND (Filesystem.file_path IN (\"*\\\\AppData\\\\*\\\\Login Data*\",\"*\\\\AppData\\\\*\\\\Cookies*\",\"*\\\\AppData\\\\*\\\\Web Data*\",\"*\\\\Mozilla\\\\Firefox\\\\Profiles\\\\*\\\\logins.json\",\"*\\\\Mozilla\\\\Firefox\\\\Profiles\\\\*\\\\key4.db\",\"*\\\\AppData\\\\*\\\\Edge\\\\*\\\\Login Data*\") OR Filesystem.file_path IN (\"*\\\\.aws\\\\credentials\",\"*\\\\.kube\\\\config\",\"*\\\\.npmrc\",\"*\\\\.pypirc\",\"*\\\\.git-credentials\",\"*\\\\.docker\\\\config.json\",\"*\\\\.netrc\",\"*\\\\.azure\\\\*\",\"*\\\\gcloud\\\\credentials*\",\"*\\\\.vault-token\",\"/home/*/.aws/credentials\",\"/root/.aws/credentials\",\"/home/*/.kube/config\",\"/home/*/.npmrc\",\"/home/*/.pypirc\",\"/home/*/.git-credentials\",\"/home/*/.docker/config.json\",\"/home/*/.netrc\") OR Filesystem.file_path IN (\"/home/*/.ssh/id_*\",\"/root/.ssh/id_*\",\"/home/*/.ssh/known_hosts\",\"*\\\\.ssh\\\\id_*\",\"*\\\\.ssh\\\\known_hosts\") OR Filesystem.file_path IN (\"*\\\\AppData\\\\Roaming\\\\Microsoft\\\\Protect\\\\*\",\"*\\\\AppData\\\\Local\\\\Microsoft\\\\Credentials\\\\*\",\"*\\\\AppData\\\\Roaming\\\\Microsoft\\\\Vault\\\\*\",\"*\\\\AppData\\\\Roaming\\\\Microsoft\\\\Crypto\\\\*\") OR Filesystem.file_path IN (\"*\\\\AppData\\\\Roaming\\\\Ethereum\\\\keystore\\\\*\",\"*\\\\AppData\\\\Roaming\\\\Exodus\\\\*\",\"*\\\\AppData\\\\Roaming\\\\Electrum\\\\wallets\\\\*\",\"*\\\\AppData\\\\Local\\\\Google\\\\Chrome\\\\*\\\\Local Extension Settings\\\\nkbihfbeogaeaoehlefnkodbefgpgknn\\\\*\",\"*\\\\AppData\\\\Local\\\\Google\\\\Chrome\\\\*\\\\Local Extension Settings\\\\*\\\\*.ldb\",\"*\\\\AppData\\\\Roaming\\\\Atomic\\\\Local Storage\\\\*\",\"/home/*/.ethereum/keystore/*\") OR Filesystem.file_path IN (\"*\\\\KeePass\\\\*.kdbx\",\"*\\\\1Password\\\\*\\\\*.opvault\",\"*\\\\Bitwarden\\\\data.json\",\"*\\\\AppData\\\\Roaming\\\\KeePass\\\\*\",\"/home/*/.password-store/*\")) by Filesystem.dest Filesystem.process_guid Filesystem.process_name Filesystem.user _time span=5m | eval cred_cat=case(match(touched_paths,\"(?i)(Login Data|Cookies|logins\\\\.json|key4\\\\.db|Web Data)\"),\"browser\",match(touched_paths,\"(?i)(\\\\.aws[\\\\\\\\/]credentials|\\\\.kube[\\\\\\\\/]config|\\\\.npmrc|\\\\.pypirc|\\\\.git-credentials|\\\\.docker[\\\\\\\\/]config|\\\\.netrc|\\\\.azure|gcloud|\\\\.vault-token)\"),\"devops\",match(touched_paths,\"(?i)(\\\\.ssh[\\\\\\\\/](id_|known_hosts))\"),\"ssh\",match(touched_paths,\"(?i)Microsoft\\\\\\\\(Protect|Credentials|Vault|Crypto)\"),\"dpapi\",match(touched_paths,\"(?i)(Ethereum|Exodus|Electrum|Atomic|MetaMask|nkbihfbeogae|keystore)\"),\"wallet\",match(touched_paths,\"(?i)(KeePass|1Password|Bitwarden|password-store|opvault|kdbx)\"),\"passmgr\",1=1,\"other\") | stats min(firstTime) as fanout_first max(lastTime) as fanout_last dc(cred_cat) as category_count values(cred_cat) as categories values(touched_paths) as files by dest process_guid process_name user | where category_count>=3 AND NOT process_name IN (\"chrome.exe\",\"msedge.exe\",\"firefox.exe\",\"keepass.exe\",\"1password.exe\",\"bitwarden.exe\",\"git.exe\",\"aws.exe\",\"kubectl.exe\",\"docker.exe\",\"backup-agent\",\"MsMpEng.exe\") | join type=inner dest [ | tstats `summariesonly` min(_time) as egress_first max(_time) as egress_last values(All_Traffic.dest) as egress_dest values(All_Traffic.dest_port) as egress_port from datamodel=Network_Traffic.All_Traffic where All_Traffic.action=\"allowed\" AND All_Traffic.dest_category!=\"internal\" AND (All_Traffic.dest IN (\"*.bore.pub\",\"bore.pub\",\"*.trycloudflare.com\",\"*.ngrok.io\",\"*.ngrok-free.app\",\"*.workers.dev\",\"*.web.app\",\"*.pages.dev\",\"transfer.sh\",\"pastebin.com\",\"*.pastebin.com\",\"api.telegram.org\",\"*.serveo.net\",\"*.localtunnel.me\",\"*.loca.lt\",\"*.cloudfront-js.com\") OR All_Traffic.dest IN (\"<c2_domain_list>\")) by All_Traffic.src ] | rename src as dest | where egress_first >= fanout_first AND egress_first <= (fanout_last + 600) | eval delay_sec=egress_first-fanout_last | `drop_dm_object_name(Filesystem)` | table fanout_first fanout_last dest user process_name process_guid category_count categories egress_first egress_dest egress_port delay_sec files | sort -fanout_first",
      "defender_kql": "let LookbackHours = 24h;\nlet FanoutWindow = 5m;\nlet EgressWindow = 10m;\nlet _tunnel_domains = dynamic([\"bore.pub\",\"trycloudflare.com\",\"ngrok.io\",\"ngrok-free.app\",\"workers.dev\",\"web.app\",\"pages.dev\",\"transfer.sh\",\"pastebin.com\",\"api.telegram.org\",\"serveo.net\",\"localtunnel.me\",\"loca.lt\",\"cloudfront-js.com\"]);\nlet _allow_procs = dynamic([\"chrome.exe\",\"msedge.exe\",\"firefox.exe\",\"keepass.exe\",\"1password.exe\",\"bitwarden.exe\",\"git.exe\",\"aws.exe\",\"kubectl.exe\",\"docker.exe\",\"MsMpEng.exe\",\"OneDrive.exe\",\"explorer.exe\"]);\nlet CredReads = DeviceFileEvents\n| where Timestamp > ago(LookbackHours)\n| where ActionType in (\"FileCreated\",\"FileModified\",\"FileRenamed\",\"FileOpened\")\n| where InitiatingProcessFileName !in~ (_allow_procs)\n| where InitiatingProcessAccountName !endswith \"$\"\n| extend FullPath = strcat(FolderPath, \"\\\\\", FileName)\n| extend cred_cat = case(\n    FullPath matches regex @\"(?i)(Login Data|Cookies|logins\\.json|key4\\.db|Web Data)\", \"browser\",\n    FullPath matches regex @\"(?i)(\\\\\\.aws\\\\credentials|\\\\\\.kube\\\\config|\\\\\\.npmrc|\\\\\\.pypirc|\\\\\\.git-credentials|\\\\\\.docker\\\\config|\\\\\\.netrc|\\\\\\.azure\\\\|gcloud\\\\|\\\\\\.vault-token)\", \"devops\",\n    FullPath matches regex @\"(?i)\\\\\\.ssh\\\\(id_|known_hosts)\", \"ssh\",\n    FullPath matches regex @\"(?i)Microsoft\\\\(Protect|Credentials|Vault|Crypto)\\\\\", \"dpapi\",\n    FullPath matches regex @\"(?i)(Ethereum|Exodus|Electrum|Atomic|MetaMask|nkbihfbeogaeaoehlefnkodbefgpgknn|keystore)\", \"wallet\",\n    FullPath matches regex @\"(?i)(KeePass|1Password|Bitwarden|\\.kdbx|\\.opvault)\", \"passmgr\",\n    \"\")\n| where isnotempty(cred_cat)\n| summarize FanoutFirst = min(Timestamp), FanoutLast = max(Timestamp),\n            CategoryCount = dcount(cred_cat),\n            Categories = make_set(cred_cat),\n            FilesTouched = make_set(FullPath, 100)\n            by DeviceId, DeviceName, InitiatingProcessId, InitiatingProcessFileName,\n               InitiatingProcessAccountName, bin(Timestamp, FanoutWindow)\n| where CategoryCount >= 3;\nlet Egress = DeviceNetworkEvents\n| where Timestamp > ago(LookbackHours)\n| where ActionType in (\"ConnectionSuccess\",\"HttpConnectionInspected\",\"ConnectionRequest\")\n| where RemoteIPType == \"Public\"\n| where RemoteUrl has_any (_tunnel_domains)\n     or RemoteUrl has_any (dynamic([\"<c2_domain_list>\"]))\n| project EgressTime = Timestamp, DeviceId, DeviceName,\n          EgressProcess = InitiatingProcessFileName,\n          RemoteUrl, RemoteIP, RemotePort;\nCredReads\n| join kind=inner Egress on DeviceId\n| where EgressTime between (FanoutFirst .. FanoutLast + EgressWindow)\n| extend DelaySec = datetime_diff('second', EgressTime, FanoutLast)\n| project FanoutFirst, FanoutLast, DeviceName,\n          User = InitiatingProcessAccountName,\n          FanoutProcess = InitiatingProcessFileName,\n          FanoutPid = InitiatingProcessId,\n          CategoryCount, Categories, FilesTouched,\n          EgressTime, EgressProcess, RemoteUrl, RemoteIP, RemotePort, DelaySec\n| order by FanoutFirst desc"
    },
    {
      "id": "UC_WEEKLY_CROSS_PLATFORM_CLICKFIX_PASTE_TO_PIPE_LOADER_UI_PA",
      "title": "[WEEKLY] Cross-Platform ClickFix Paste-to-Pipe Loader (UI-Parent Shell with Decode-and-Execute Payload)",
      "kill_chain": "delivery",
      "confidence": "High",
      "description": "Detects an interactive UI surface (web browser, file-explorer Run dialog, or terminal emulator) spawning a command interpreter whose command line carries a one-shot decode-and-pipe loader signature \u2014 base64-decode piped to a shell, curl/wget piped to bash/sh/python, PowerShell `-EncodedCommand` / `FromBase64String` / `IEX DownloadString` cradles, or `osascript -e` with encoded payload. This fortnight the technique drives the delivery stage across most of the catalogue: the New Linux PamDOORa Backdoor explicitly distributes via T1204.004 paste-to-shell, Quasar Linux RAT (QLNX) uses the same vector to land on developer hosts, TCLBANKER chains it into a banking-trojan installer, and both the One Click / Patient Zero webinar piece and the 25M Alerts retrospective name ClickFix as the dominant 2026 Patient-Zero technique. The UC spans the delivery and execution kill-chain stages in a single event window because, for ClickFix, the clipboard paste IS the delivery and the spawned pipeline IS the execution. It fires when the parent is an interactive UI process AND the child is a shell/interpreter AND the command line contains a decode-and-execute pattern \u2014 the textbook ClickFix signature. It will NOT fire on scripted automation where the shell's parent is sshd / cron / systemd / a build agent, nor on PowerShell launched from a console host with a static cmdline (no decode primitive), so DevOps scripted pipelines, scheduled installers, and admin SSH sessions are excluded by construction.\n\nRationale: Every article in this fortnight's bundle that maps T1204.004 \u2014 PamDOORa, Quasar Linux RAT, TCLBANKER, the One Click Patient Zero piece and the 25M Alerts retrospective \u2014 converges on the same shape: a UI surface (browser, file-explorer Run dialog, or terminal pasted into by the victim) immediately spawns an interpreter whose command line carries a decode-then-execute primitive. Joining on UI-parent + interpreter-child + decode/cradle-payload in a single event captures the cross-platform ClickFix signature without leaning on any rotating IOC.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1204.004",
          "name": "User Execution: Malicious Copy and Paste"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1140",
          "name": "Deobfuscate/Decode Files or Information"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        }
      ],
      "data_models": [
        "Endpoint.Processes"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmd values(Processes.parent_process) as parent_cmd values(Processes.user) as user from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"explorer.exe\",\"chrome.exe\",\"msedge.exe\",\"firefox.exe\",\"brave.exe\",\"safari\",\"Safari\",\"Terminal\",\"Terminal.app\",\"gnome-terminal\",\"gnome-terminal-server\",\"konsole\",\"xterm\",\"iTerm2\",\"WindowsTerminal.exe\",\"wt.exe\") AND Processes.process_name IN (\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"bash\",\"sh\",\"zsh\",\"dash\",\"osascript\",\"python\",\"python3\",\"perl\") AND (Processes.process=\"*base64 -d*|*\" OR Processes.process=\"*base64 --decode*|*\" OR Processes.process=\"*echo *|*base64*|*\" OR Processes.process=\"*curl *|*sh*\" OR Processes.process=\"*curl *|*bash*\" OR Processes.process=\"*curl *|*python*\" OR Processes.process=\"*wget *|*sh*\" OR Processes.process=\"*wget *|*bash*\" OR Processes.process=\"*-EncodedCommand*\" OR Processes.process=\"* -enc *\" OR Processes.process=\"*FromBase64String*\" OR Processes.process=\"*DownloadString(*\" OR Processes.process=\"*IEX(*\" OR Processes.process=\"*Invoke-Expression*\" OR Processes.process=\"*iwr *-useb*\" OR Processes.process=\"*osascript -e*do shell script*\") by Processes.dest Processes.user Processes.process_name Processes.process Processes.parent_process_name Processes.parent_process | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`",
      "defender_kql": "let _shell_names = dynamic([\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"bash\",\"sh\",\"zsh\",\"dash\",\"osascript\",\"python\",\"python3\",\"perl\"]);\nlet _ui_parents = dynamic([\"explorer.exe\",\"chrome.exe\",\"msedge.exe\",\"firefox.exe\",\"brave.exe\",\"safari\",\"Terminal\",\"Terminal.app\",\"gnome-terminal\",\"gnome-terminal-server\",\"konsole\",\"xterm\",\"iTerm2\",\"WindowsTerminal.exe\",\"wt.exe\"]);\nDeviceProcessEvents\n| where Timestamp > ago(7d)\n| where AccountName !endswith \"$\"\n| where FileName in~ (_shell_names)\n| where InitiatingProcessFileName in~ (_ui_parents)\n| where ProcessCommandLine matches regex @\"(?i)(base64\\s+(-d|--decode)[^|]*\\||echo\\s+[A-Za-z0-9+/=]{20,}[^|]*\\|[^|]*base64[^|]*\\||curl\\s+\\S+\\s*\\|\\s*(sh|bash|zsh|python|perl)|wget\\s+\\S+\\s*\\|\\s*(sh|bash|zsh|python|perl)|-(encodedcommand|enc)\\s+[A-Za-z0-9+/=]{20,}|frombase64string\\s*\\(|downloadstring\\s*\\(|iex\\s*\\(|invoke-expression|iwr\\s+-useb|osascript\\s+-e.*do shell script)\"\n| project Timestamp, DeviceName, AccountName,\n          Parent = InitiatingProcessFileName,\n          ParentCmd = InitiatingProcessCommandLine,\n          Shell = FileName,\n          ProcessCommandLine,\n          IsRemoteSession = IsInitiatingProcessRemoteSession,\n          SHA256\n| order by Timestamp desc"
    },
    {
      "id": "UC_WEEKLY_DEVELOPER_AI_TOOLING_RUNTIME_SPAWNS_SHELL_OR_EGRES",
      "title": "[WEEKLY] Developer/AI tooling runtime spawns shell or egress LOLBin (unauth RCE post-expl)",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects the common post-exploitation footprint shared by the recent wave of unauthenticated RCE flaws in developer-/AI-adjacent web services: LiteLLM CVE-2026-42271 (CISA KEV, MCP test-endpoint command injection), Langflow CVE-2026-5027 (KEV, code-validate RCE), DbGate CVE-2026-47668 (JSON script runner functionName injection executed in a forked Node child), Phoenix Storybook CVE-2026-8467 (HEEx template injection over WebSocket), NASA AMMOS AIT-BSC CVE-2026-47731 (path-traversal write), Meta Ads MCP CVE-2026-48039 (unauth /mcp tool execution), and the WWBN AVideo YPTSocket stored DOM XSS \u2014 all of which land code execution inside a python/node/uvicorn/erlang/php-fpm interpreter that does NOT normally fork shells or call out to the internet. The UC fires when a service-tier interpreter parent (python/uvicorn/gunicorn/node/erl/beam.smp/php-fpm/ruby) spawns a shell or known download LOLBin (curl/wget/nc/powershell/certutil/bitsadmin) AND that child or its parent makes an outbound public network connection within five minutes. This spans exploit \u2192 install \u2192 c2 phases of the kill chain. It will NOT fire on normal API traffic, on developer workstations where a human launches `node` interactively (filtered via process-tree depth and user context), or on CI runners executing `npm`/`pip` against trusted registries; it WILL fire when a vulnerable AI/dev service runtime is coerced into running an attacker-chosen command, regardless of which CVE was used to get there.\n\nRationale: All seven articles describe distinct vulnerabilities, but post-exploitation collapses to the same shape: an internet-reachable interpreter (python/node/erlang/php) running an unsanitized attacker-controlled payload that immediately shells out and fetches a second-stage. Detecting that runtime-spawns-LOLBin-then-egress chain catches every variant of the campaign without binding to any single CVE's request signature, which is why this UC survives IOC and CVE rotation.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as child_cmdline values(Processes.process_path) as child_path values(Processes.parent_process) as parent_cmdline values(Processes.user) as user values(Processes.process_id) as child_pid from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"python\",\"python3\",\"python.exe\",\"uvicorn\",\"gunicorn\",\"node\",\"node.exe\",\"nodejs\",\"erl\",\"erl.exe\",\"beam.smp\",\"php-fpm\",\"php-fpm7\",\"php-fpm8\",\"ruby\",\"java\",\"dotnet\")) AND Processes.process_name IN (\"sh\",\"bash\",\"dash\",\"zsh\",\"ksh\",\"cmd.exe\",\"powershell.exe\",\"pwsh\",\"pwsh.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"nc\",\"ncat\",\"certutil.exe\",\"bitsadmin.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"perl\",\"socat\") by host Processes.dest Processes.user Processes.parent_process_name Processes.parent_process Processes.process_name Processes.process Processes.process_id _time | `drop_dm_object_name(Processes)` | rename dest as host_dest | join type=inner host_dest [| tstats summariesonly=t min(_time) as net_first max(_time) as net_last values(All_Traffic.dest) as remote_ip values(All_Traffic.dest_port) as remote_port from datamodel=Network_Traffic.All_Traffic where All_Traffic.action=\"allowed\" NOT (All_Traffic.dest IN (\"10.0.0.0/8\",\"172.16.0.0/12\",\"192.168.0.0/16\",\"127.0.0.0/8\",\"169.254.0.0/16\")) by All_Traffic.src _time | `drop_dm_object_name(All_Traffic)` | rename src as host_dest] | where (net_first >= firstTime AND net_first <= firstTime + 300) OR (net_last >= firstTime AND net_last <= firstTime + 300) | convert ctime(firstTime) ctime(lastTime) ctime(net_first) ctime(net_last) | sort - lastTime",
      "defender_kql": "let LookbackPeriod = 7d;\nlet CorrelationWindow = 5m;\nlet InterpreterParents = dynamic([\"python.exe\",\"python\",\"python3\",\"uvicorn\",\"gunicorn\",\"node.exe\",\"node\",\"nodejs\",\"erl.exe\",\"erl\",\"beam.smp\",\"php-fpm\",\"php-fpm7\",\"php-fpm8\",\"ruby\",\"java.exe\",\"java\",\"dotnet.exe\",\"dotnet\"]);\nlet ShellOrLolbin = dynamic([\"sh\",\"bash\",\"dash\",\"zsh\",\"ksh\",\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"pwsh\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\",\"nc\",\"ncat\",\"certutil.exe\",\"bitsadmin.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"perl\",\"socat\"]);\nlet SuspectChildren = DeviceProcessEvents\n    | where Timestamp > ago(LookbackPeriod)\n    | where InitiatingProcessFileName in~ (InterpreterParents)\n    | where FileName in~ (ShellOrLolbin)\n    | where AccountName !endswith \"$\"\n    | where InitiatingProcessTokenElevation !in~ (\"TokenElevationTypeFull\")\n        or InitiatingProcessAccountName !in~ (\"system\",\"local service\",\"network service\")\n    | project ChildTime = Timestamp, DeviceId, DeviceName, AccountName,\n              ParentImage = InitiatingProcessFolderPath,\n              ParentCmd = InitiatingProcessCommandLine,\n              ChildImage = FolderPath, ChildName = FileName,\n              ChildCmd = ProcessCommandLine, ChildPid = ProcessId,\n              ChildProcessGuid = ProcessUniqueId;\nDeviceNetworkEvents\n| where Timestamp > ago(LookbackPeriod)\n| where ActionType == \"ConnectionSuccess\"\n| where RemoteIPType == \"Public\"\n| project NetTime = Timestamp, DeviceId, NetInitiator = InitiatingProcessFileName,\n          NetInitiatorPid = InitiatingProcessId, RemoteIP, RemoteUrl, RemotePort\n| join kind=inner SuspectChildren on DeviceId\n| where NetTime between (ChildTime .. ChildTime + CorrelationWindow)\n| project ChildTime, NetTime,\n          DelaySec = datetime_diff('second', NetTime, ChildTime),\n          DeviceName, AccountName,\n          ParentImage, ParentCmd, ChildImage, ChildName, ChildCmd,\n          NetInitiator, RemoteIP, RemoteUrl, RemotePort\n| order by ChildTime desc"
    },
    {
      "id": "UC_WEEKLY_DEVELOPER_DATA_TOOLING_DAEMON_SPAWNS_SHELL_CHILD_S",
      "title": "[WEEKLY] Developer/Data-tooling Daemon Spawns Shell Child Seconds After POST to Runner/Exec Endpoint",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects the joint pattern across this fortnight's GHSA critical-severity disclosures where a long-running developer or data-tooling daemon (Node.js, Python, Jupyter kernel/Enterprise Gateway, Stata) receives an inbound HTTP POST to a code-execution-adjacent endpoint (`/runners/start`, `/runners/load-reader`, `/api/kernels`, `/start`, `/stata_do`, `/workspaces/*/members`, etc.) and within seconds spawns an OS shell, LOLBin, or alternate interpreter as a child \u2014 the universal post-condition of code injection (DbGate CVE-2026-47668/47670), arbitrary-file-write turned RCE (DbGate Zip Slip CVE-2026-47669), command injection (MCP-for-Stata CVE-2026-47708), SSTI (Jupyter EG CVE-2026-44181), YAML manifest injection (Jupyter EG CVE-2026-44182), and path-traversal-to-exec (NASA AMMOS CVE-2026-47731). It spans the exploit -> install kill-chain boundary by correlating the network ingress (vulnerable endpoint hit) with the host-side proof of code execution (parent-child anomaly) inside a 2-minute window on the same host. Fires when a vulnerable runner endpoint is POSTed AND the same daemon process spawns sh/bash/cmd/powershell/curl/wget/python/perl within 120s; does NOT fire on the routine background tasks these daemons spawn (helper Node workers, child Python kernels of the same family, scheduled cron-jobs) nor on POSTs to read-only endpoints, keeping FP rate low even in noisy CI/MLOps environments.\n\nRationale: The 12 articles share one observable joint pattern: a long-lived developer/data-tooling daemon accepts an HTTP POST to an execution-adjacent endpoint and then immediately spawns a shell, LOLBin, or alternate interpreter \u2014 the universal payoff of code injection, SSTI, manifest injection, command injection, and path-traversal-to-RCE. Correlating the inbound POST with the parent-child anomaly within a 2-minute window on the same host catches every variant in the fortnight (DbGate Node, Jupyter EG Python, Stata, AMMOS Python) while suppressing routine helper-process spawns that don't follow a vulnerable-endpoint POST.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1059.003",
          "name": "Command and Scripting Interpreter: Windows Command Shell"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1505.003",
          "name": "Server Software Component: Web Shell"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Web",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats summariesonly=true count min(_time) as web_time max(_time) as web_last values(Web.url) as web_urls values(Web.http_user_agent) as web_uas values(Web.src) as src_ip values(Web.http_method) as web_methods from datamodel=Web where Web.http_method=POST (Web.uri_path=\"*/runners/start*\" OR Web.uri_path=\"*/runners/load-reader*\" OR Web.uri_path=\"*/api/kernels*\" OR Web.uri_path=\"*/stata_do*\" OR Web.uri_path=\"*/workspaces/*/members*\" OR Web.uri_path=\"*/start\" OR Web.uri_path=\"*/auth/login\") by Web.dest _time | `drop_dm_object_name(Web)` | rename dest as host | eval web_time=_time | fields - _time | join type=inner host [| tstats summariesonly=true count min(_time) as proc_time max(_time) as proc_last values(Processes.process) as child_cmd values(Processes.process_name) as child_bin values(Processes.parent_process_name) as parent_bin values(Processes.parent_process) as parent_cmd values(Processes.user) as proc_user from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"node\",\"node.exe\",\"python\",\"python.exe\",\"python3\",\"jupyter-kernel\",\"enterprise_gateway\",\"stata\",\"stata.exe\",\"stata-mp\",\"stata-mp.exe\",\"xstata\",\"xstata.exe\",\"php\",\"php-fpm\",\"php-fpm.exe\",\"w3wp.exe\",\"uvicorn\",\"gunicorn\")) AND (Processes.process_name IN (\"sh\",\"bash\",\"dash\",\"zsh\",\"ksh\",\"cmd.exe\",\"powershell.exe\",\"pwsh\",\"pwsh.exe\",\"wget\",\"curl\",\"nc\",\"ncat\",\"perl\",\"ruby\",\"python\",\"python3\")) by Processes.dest _time | `drop_dm_object_name(Processes)` | rename dest as host | eval proc_time=_time | fields - _time ] | where proc_time >= web_time AND proc_time <= web_time + 120 | eval delay_sec = proc_time - web_time | table web_time, proc_time, delay_sec, host, src_ip, web_urls, web_methods, parent_bin, parent_cmd, child_bin, child_cmd, proc_user, web_uas | sort 0 - proc_time",
      "defender_kql": "let Window = 2m;\nlet RunnerPaths = dynamic([\"/runners/start\",\"/runners/load-reader\",\"/api/kernels\",\"/stata_do\",\"/workspaces/\",\"/auth/login\"]);\nlet RunnerHits =\n    DeviceNetworkEvents\n    | where Timestamp > ago(7d)\n    | where ActionType in (\"InboundConnectionAccepted\",\"ConnectionFound\",\"NetworkSignatureInspected\")\n    | where isnotempty(RemoteUrl)\n    | where RemoteUrl has_any (RunnerPaths) or AdditionalFields has_any (RunnerPaths)\n    | project NetTime=Timestamp, DeviceId, DeviceName, RemoteIP, RemoteUrl, InboundProcess=InitiatingProcessFileName;\nDeviceProcessEvents\n| where Timestamp > ago(7d)\n| where InitiatingProcessFileName in~ (\"node\",\"node.exe\",\"python\",\"python.exe\",\"python3\",\"jupyter-kernel\",\"enterprise_gateway\",\"stata\",\"stata.exe\",\"stata-mp\",\"stata-mp.exe\",\"xstata\",\"xstata.exe\",\"php\",\"php-fpm\",\"php-fpm.exe\",\"w3wp.exe\",\"uvicorn\",\"gunicorn\")\n| where FileName in~ (\"sh\",\"bash\",\"dash\",\"zsh\",\"ksh\",\"cmd.exe\",\"powershell.exe\",\"pwsh\",\"pwsh.exe\",\"wget\",\"curl\",\"nc\",\"ncat\",\"perl\",\"ruby\",\"python\",\"python3\")\n| where AccountName !endswith \"$\"\n| where AccountName !in~ (\"system\",\"local service\",\"network service\")\n| join kind=inner RunnerHits on DeviceId\n| where Timestamp between (NetTime .. NetTime + Window)\n| extend DelaySec = datetime_diff('second', Timestamp, NetTime)\n| project Timestamp, NetTime, DelaySec, DeviceName, RemoteIP, RemoteUrl,\n          ParentBinary=InitiatingProcessFileName,\n          ParentCmd=InitiatingProcessCommandLine,\n          ChildBinary=FileName,\n          ChildCmd=ProcessCommandLine,\n          AccountName, SHA256\n| order by Timestamp desc"
    },
    {
      "id": "UC_WEEKLY_DEVELOPER_INTERPRETER_PACKAGE_MANAGER_PROCESS_EXFI",
      "title": "[WEEKLY] Developer interpreter / package-manager process exfiltrating tokens to public code-hosting / worker domains",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the multi-stage Miasma/Hades supply-chain pattern: a package manager (npm/pip/yarn/pnpm/uv) or its lifecycle hook spawns a script interpreter (node, python, bash, powershell), which within a short window reads local credential/token stores (.npmrc, .pypirc, .aws/credentials, .git-credentials, .env, browser SQLite cookie DBs) and egresses to a non-registry public-code-hosting or anonymous-worker endpoint (raw.githubusercontent.com, gist.github.com, *.workers.dev, *.trycloudflare.com, *.web.app, transfer.sh, telegram bot API). The fortnight's Miasma compromise of @redhat-cloud-services (Microsoft 2026-06-03), the Hades PyPI splinter (THN 2026-06-09), the 73 Microsoft GitHub repos pulled offline (THN 2026-06-09), and the WinRAR CVE-2025-8088 stealer chains reaching the same Cloudflare-Worker dead-drops (THN 2026-06-09) all converge on this exact preinstall->cred-harvest->bidirectional-C2 chain. Spans Initial Access (T1195.002), Execution (T1059), Credential Access (T1552.001/T1555.003), Exfiltration (T1567/T1102.002). Fires when ALL three stages co-occur on one host within 10 minutes; does NOT fire on a clean `npm install` against a registry (registry.npmjs.org / pypi.org are excluded), on isolated cred-file reads by password managers, or on developer git pushes that don't follow a package-install event.\n\nRationale: Every article this fortnight (Miasma npm, Hades PyPI, Microsoft's 73 GitHub repo pulls, the WinRAR delivery chains) ends in the same three-event sequence on a developer or build host: package-manager spawns interpreter, interpreter reads credential artifacts, interpreter or its child egresses to a public anonymous-worker / code-host dead-drop. Detecting on that joint sequence \u2014 rather than the rotating package names, hashes or domains \u2014 keeps the rule alive across the campaign's known splinters and the next ecosystem to be targeted.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain: Compromise Software Dependencies and Development Tools"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1555.003",
          "name": "Credentials from Password Stores: Credentials from Web Browsers"
        },
        {
          "id": "T1567.001",
          "name": "Exfiltration Over Web Service: Exfiltration to Code Repository"
        },
        {
          "id": "T1102.002",
          "name": "Web Service: Bidirectional Communication"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem",
        "Network_Traffic.All_Traffic",
        "Network_Resolution.DNS"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as install_time from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"npm.exe\",\"npm-cli.js\",\"yarn.exe\",\"pnpm.exe\",\"pip\",\"pip.exe\",\"pip3\",\"pip3.exe\",\"uv\",\"uv.exe\",\"node.exe\",\"node\") (Processes.process=\"*install*\" OR Processes.process=\"*add *\" OR Processes.parent_process=\"*lifecycle*\" OR Processes.parent_process=\"*preinstall*\" OR Processes.parent_process=\"*postinstall*\") Processes.process_name IN (\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"bash\",\"sh\",\"curl.exe\",\"wget.exe\") by host Processes.user Processes.process_name Processes.process_guid Processes.parent_process_name\n| `drop_dm_object_name(Processes)`\n| join type=inner host process_guid [\n    | tstats `summariesonly` count from datamodel=Endpoint.Filesystem where Filesystem.file_path IN (\"*\\\\.npmrc\",\"*\\\\.pypirc\",\"*\\\\.aws\\\\credentials\",\"*\\\\.git-credentials\",\"*\\\\.ssh\\\\id_rsa\",\"*\\\\.docker\\\\config.json\",\"*\\\\.env\",\"*\\\\AppData\\\\*Login Data\",\"*\\\\AppData\\\\*Cookies\",\"*/.npmrc\",\"*/.pypirc\",\"*/.aws/credentials\",\"*/.git-credentials\",\"*/.ssh/id_rsa\",\"*/.docker/config.json\",\"*/.env\") by host Filesystem.process_guid\n    | `drop_dm_object_name(Filesystem)`\n    | rename process_guid as process_guid\n]\n| join type=inner host [\n    | tstats `summariesonly` count values(All_Traffic.dest) as egress_dest from datamodel=Network_Traffic.All_Traffic where All_Traffic.action=allowed All_Traffic.dest_category!=internal NOT All_Traffic.dest IN (\"registry.npmjs.org\",\"pypi.org\",\"files.pythonhosted.org\",\"registry.yarnpkg.com\",\"*.docker.io\") by host\n    | `drop_dm_object_name(All_Traffic)`\n    | join type=inner host [\n        | tstats `summariesonly` count from datamodel=Network_Resolution.DNS where (DNS.query=\"*.workers.dev\" OR DNS.query=\"*.trycloudflare.com\" OR DNS.query=\"*.web.app\" OR DNS.query=\"raw.githubusercontent.com\" OR DNS.query=\"gist.github.com\" OR DNS.query=\"transfer.sh\" OR DNS.query=\"*.ngrok.io\" OR DNS.query=\"api.telegram.org\" OR DNS.query=\"discord.com\" OR DNS.query=\"*.pastebin.com\") by host DNS.query\n        | `drop_dm_object_name(DNS)`\n    ]\n]\n| where count >= 1\n| sort - install_time",
      "defender_kql": "let Window = 10m;\nlet RegistryHosts = dynamic([\"registry.npmjs.org\",\"pypi.org\",\"files.pythonhosted.org\",\"registry.yarnpkg.com\",\"index.docker.io\",\"ghcr.io\",\"crates.io\"]);\nlet PublicDropDomains = dynamic([\"workers.dev\",\"trycloudflare.com\",\"web.app\",\"raw.githubusercontent.com\",\"gist.github.com\",\"transfer.sh\",\"ngrok.io\",\"ngrok-free.app\",\"api.telegram.org\",\"discord.com\",\"discordapp.com\",\"pastebin.com\",\"hastebin.com\",\"bot.discord.com\"]);\nlet CredArtifacts = dynamic([\".npmrc\",\".pypirc\",\"credentials\",\".git-credentials\",\"id_rsa\",\"id_ed25519\",\"config.json\",\".env\",\"Login Data\",\"Cookies\",\"key4.db\",\"logins.json\"]);\nlet PkgMgrParents = dynamic([\"npm.exe\",\"npm-cli.js\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"pip\",\"pip.exe\",\"pip3\",\"pip3.exe\",\"uv\",\"uv.exe\",\"poetry\",\"poetry.exe\",\"node.exe\",\"node\"]);\nlet ScriptChildren = dynamic([\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"bash\",\"sh\",\"curl.exe\",\"wget.exe\"]);\nlet PkgInstallStage = DeviceProcessEvents\n    | where Timestamp > ago(2d)\n    | where AccountName !endswith \"$\"\n    | where InitiatingProcessFileName in~ (PkgMgrParents)\n    | where FileName in~ (ScriptChildren)\n    | where InitiatingProcessCommandLine has_any (\"install\",\"add\",\"i \",\"ci\",\"preinstall\",\"postinstall\",\"lifecycle\",\"build\")\n    | project InstallTime = Timestamp, DeviceId, DeviceName, AccountName,\n              PkgMgr = InitiatingProcessFileName, PkgMgrCmd = InitiatingProcessCommandLine,\n              ChildProc = FileName, ChildCmd = ProcessCommandLine, ChildPid = ProcessId;\nlet CredReadStage = DeviceFileEvents\n    | where Timestamp > ago(2d)\n    | where ActionType in (\"FileCreated\",\"FileModified\",\"FileRenamed\") or ActionType startswith \"File\"\n    | where FileName in~ (CredArtifacts) or FolderPath has_any (\".aws\",\".ssh\",\"User Data\\\\Default\",\".npm\",\".config\\\\gh\")\n    | project CredTime = Timestamp, DeviceId, CredFile = FolderPath, CredInitiator = InitiatingProcessFileName, CredInitiatorPid = InitiatingProcessId;\nlet EgressStage = DeviceNetworkEvents\n    | where Timestamp > ago(2d)\n    | where RemoteIPType == \"Public\"\n    | where isnotempty(RemoteUrl)\n    | where not(RemoteUrl has_any (RegistryHosts))\n    | extend HitDrop = iff(RemoteUrl has_any (PublicDropDomains), true, false)\n    | where HitDrop == true\n    | project EgressTime = Timestamp, DeviceId, RemoteUrl, RemoteIP, EgressInitiator = InitiatingProcessFileName, EgressPid = InitiatingProcessId;\nPkgInstallStage\n| join kind=inner CredReadStage on DeviceId\n| where CredTime between (InstallTime .. InstallTime + Window)\n| join kind=inner EgressStage on DeviceId\n| where EgressTime between (InstallTime .. InstallTime + Window)\n| where EgressInitiator in~ (ScriptChildren) or EgressInitiator =~ ChildProc or CredInitiator in~ (ScriptChildren)\n| summarize InstallTime = min(InstallTime), CredReads = dcount(CredFile),\n            CredFiles = make_set(CredFile, 25), DropDomains = make_set(RemoteUrl, 25),\n            DropIPs = make_set(RemoteIP, 25)\n          by DeviceName, AccountName, PkgMgr, ChildProc, ChildCmd\n| where CredReads >= 1\n| order by InstallTime desc"
    },
    {
      "id": "UC_WEEKLY_DEVELOPER_PACKAGE_INSTALL_SPAWNING_SCRIPT_HOST_WIT",
      "title": "[WEEKLY] Developer package install spawning script-host with non-registry C2 within 5 minutes",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the cross-ecosystem supply-chain pattern in which a developer package manager (npm/pnpm/yarn, pip/uv/poetry, NuGet/dotnet, or a VS Code/Cursor extension install) executes a lifecycle hook (preinstall/postinstall, setup.py, .csproj install task, extension activation) that within seconds spawns a script host (node, python, powershell, bash, curl, wget) which then beacons to an internet destination that is NOT a known package registry or marketplace. This fortnight that exact chain drove the Mini Shai-Hulud/AntV npm wave (Snyk, 2026-05-18), the malicious mistralai 2.4.6 PyPI dropper (GHSA-wx9m-wx4f-4cmg), node-ipc credential thief (StepSecurity 2026-05-19), guardrails-ai 0.10.1 (CVE-2026-45758), @cap-js/* (CVE-2026-46421), the malicious VS Code extension that breached GitHub and TeamPCP's Megalodon CI/CD push (Aikido / The Hacker News), the CISA-KEV-added Nx Console and Daemon Tools Lite embedded-malicious-code CVEs (CVE-2026-48027, CVE-2026-8398), the Claude AI-targeting mouse5212-super-formatter npm (THN 2026-05-27), the Sicoob.Sdk NuGet credential stealer (THN 2026-05-29) and Microsoft's dependency-confusion npm campaign (MSRC 2026-05-30). The chain spans Delivery (registry pull) \u2192 Installation (lifecycle hook execution) \u2192 C2 (first call-home). It fires when the install\u2192egress delay is under 5 minutes AND the destination is outside an allow-list of registry/marketplace/GitHub hostnames; it will NOT fire on routine `npm install` traffic to registry.npmjs.org/pypi.org, on background telemetry from already-installed packages (no install-process ancestor), or on egress that pre-dates the install event.\n\nRationale: Every one of the 12 articles describes the same chained behaviour \u2014 a package or extension install whose own lifecycle hook reaches the internet \u2014 but each cites a different IOC set (mistralai 83.142.209.194, AntV m-kosche.com, node-ipc sh.azurestaticprovider.net, @cap-js zero.masscan.cloud, Nx Console check.git-service.com, Megalodon polymarketbot.polymarketdev.workers.dev, Sicoob oob.moika.tech, MSRC's cloudplatform-single-spa.io, etc.). Correlating the install-process ancestor with a script-host egress to a non-registry destination inside a 5-minute window catches the joint TTP without pinning to any single rotating IOC, and the registry/marketplace allow-list keeps the false-positive rate low on normal developer workflow.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1204.002",
          "name": "User Execution: Malicious File"
        },
        {
          "id": "T1546.016",
          "name": "Event Triggered Execution: Installer Packages"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic",
        "Network_Resolution.DNS"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as _time values(Processes.process_name) as proc values(Processes.process) as cmd from datamodel=Endpoint.Processes where (Processes.process_name IN (\"npm\",\"npm.cmd\",\"npm.exe\",\"pnpm\",\"pnpm.exe\",\"yarn\",\"yarn.exe\",\"pip\",\"pip3\",\"pip.exe\",\"pip3.exe\",\"uv\",\"uv.exe\",\"poetry\",\"poetry.exe\",\"nuget.exe\",\"dotnet.exe\",\"code.exe\",\"cursor.exe\") AND (Processes.process=\"*install*\" OR Processes.process=\"* add *\" OR Processes.process=\"*restore*\" OR Processes.process=\"*--install-extension*\" OR Processes.process=\"* ci *\")) by Processes.dest Processes.user | eval kind=\"install\" | append [ | tstats `summariesonly` count min(_time) as _time values(All_Traffic.dest) as remote_dest values(All_Traffic.dest_ip) as remote_ip values(All_Traffic.dest_port) as remote_port from datamodel=Network_Traffic.All_Traffic where (All_Traffic.app IN (\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3.exe\",\"python3\",\"pwsh.exe\",\"powershell.exe\",\"bash\",\"sh\",\"dash\",\"curl.exe\",\"wget.exe\",\"cmd.exe\") AND NOT (All_Traffic.dest IN (\"registry.npmjs.org\",\"pypi.org\",\"files.pythonhosted.org\",\"registry.yarnpkg.com\",\"api.nuget.org\",\"marketplace.visualstudio.com\",\"open-vsx.org\",\"github.com\",\"raw.githubusercontent.com\",\"ghcr.io\",\"go.dev\",\"crates.io\",\"rubygems.org\") OR All_Traffic.dest=\"*.npmjs.org\" OR All_Traffic.dest=\"*.pypi.org\" OR All_Traffic.dest=\"*.pythonhosted.org\" OR All_Traffic.dest=\"*.yarnpkg.com\" OR All_Traffic.dest=\"*.nuget.org\" OR All_Traffic.dest=\"*.visualstudio.com\" OR All_Traffic.dest=\"*.github.com\" OR All_Traffic.dest=\"*.githubusercontent.com\")) by All_Traffic.src All_Traffic.user | rename All_Traffic.src as dest All_Traffic.user as user | eval kind=\"egress\" ] | sort 0 dest user _time | streamstats current=f window=10 last(_time) as prev_time last(kind) as prev_kind last(cmd) as prev_cmd last(proc) as prev_proc by dest user | where kind=\"egress\" AND prev_kind=\"install\" AND (_time - prev_time) <= 300 | eval delay_sec = (_time - prev_time) | `security_content_ctime(_time)` | table _time delay_sec dest user prev_proc prev_cmd remote_dest remote_ip remote_port",
      "defender_kql": "let LookbackDays = 7d;\nlet WindowSec = 300;\nlet PkgMgr = dynamic([\"npm.exe\",\"npm\",\"pnpm.exe\",\"pnpm\",\"yarn.exe\",\"yarn\",\"pip.exe\",\"pip\",\"pip3.exe\",\"pip3\",\"uv.exe\",\"uv\",\"poetry.exe\",\"poetry\",\"nuget.exe\",\"dotnet.exe\",\"code.exe\",\"cursor.exe\"]);\nlet ScriptHosts = dynamic([\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3.exe\",\"python3\",\"pwsh.exe\",\"powershell.exe\",\"bash\",\"sh\",\"dash\",\"curl.exe\",\"wget.exe\",\"cmd.exe\"]);\nlet RegistryAllow = dynamic([\"registry.npmjs.org\",\"pypi.org\",\"files.pythonhosted.org\",\"registry.yarnpkg.com\",\"api.nuget.org\",\"nuget.org\",\"marketplace.visualstudio.com\",\"open-vsx.org\",\"github.com\",\"raw.githubusercontent.com\",\"ghcr.io\",\"go.dev\",\"crates.io\",\"rubygems.org\"]);\nlet Installs = DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where FileName in~ (PkgMgr)\n    | where ProcessCommandLine has_any (\"install\",\" add \",\"restore\",\"--install-extension\",\" ci \")\n    | where AccountName !endswith \"$\"\n    | project InstallTime = Timestamp, DeviceId, DeviceName, AccountName,\n              PkgMgr = FileName, PkgCmd = ProcessCommandLine;\nlet Egress = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackDays)\n    | where RemoteIPType == \"Public\"\n    | where InitiatingProcessFileName in~ (ScriptHosts)\n         or InitiatingProcessParentFileName in~ (PkgMgr)\n    | extend HostOnly = tolower(tostring(split(RemoteUrl, \"/\")[0]))\n    | where not(HostOnly in~ (RegistryAllow))\n         and not(HostOnly endswith \".npmjs.org\")\n         and not(HostOnly endswith \".pypi.org\")\n         and not(HostOnly endswith \".pythonhosted.org\")\n         and not(HostOnly endswith \".yarnpkg.com\")\n         and not(HostOnly endswith \".nuget.org\")\n         and not(HostOnly endswith \".visualstudio.com\")\n         and not(HostOnly endswith \".github.com\")\n         and not(HostOnly endswith \".githubusercontent.com\")\n    | project EgressTime = Timestamp, DeviceId, DeviceName, AccountName,\n              ChildProc = InitiatingProcessFileName,\n              ChildCmd  = InitiatingProcessCommandLine,\n              ChildParent = InitiatingProcessParentFileName,\n              RemoteUrl, RemoteIP, RemotePort;\nInstalls\n| join kind=inner Egress on DeviceId, AccountName\n| where EgressTime between (InstallTime .. InstallTime + WindowSec * 1s)\n| extend DelaySec = datetime_diff('second', EgressTime, InstallTime)\n| summarize InstallTime = min(InstallTime),\n            EgressTime = min(EgressTime),\n            DelaySec   = min(DelaySec),\n            RemoteHits = dcount(strcat(RemoteIP,\":\",tostring(RemotePort))),\n            SampleRemoteUrl = any(RemoteUrl),\n            SampleRemoteIP  = any(RemoteIP),\n            SamplePkgCmd    = any(PkgCmd),\n            SampleChildCmd  = any(ChildCmd)\n         by DeviceName, AccountName, PkgMgr, ChildProc, ChildParent\n| order by EgressTime desc"
    },
    {
      "id": "UC_WEEKLY_EDGE_SERVICE_POST_EXPLOITATION_CHAIN_INTERNET_FACI",
      "title": "[WEEKLY] Edge-service post-exploitation chain: internet-facing daemon \u2192 child shell or token redemption within 10 min of external request",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Detects an internet-facing edge service (VPN portal, identity broker, Coder workspace daemon, OIDC relying party, or Node.js web tier) spawning an OS shell or invoking a token / role / agent redemption call within 10 minutes of an external inbound request on the same host \u2014 the post-condition signature of an authentication or sandbox bypass that converts an anonymous wire interaction into in-host execution or credential issuance. This fortnight three converging campaign clusters all collapse to exactly this chain and drive the rule: active in-the-wild exploitation of CVE-2026-0257 (BleepingComputer 'Palo Alto GlobalProtect VPN auth bypass flaw now exploited in attacks', Hacker News 'PAN-OS GlobalProtect Authentication Bypass (CVE-2026-0257) Under Active Exploitation', CISA KEV listing 2026-05-29), unauthenticated Azure-identity PKCS#7 forgery against Coder (GHSA-6x44-w3xg-hqqf / CVE-2026-46354) and the matching @hulumi/policies OIDC trust-policy bypass (GHSA-q2f7-m237-v562), and a fresh wave of vm2 sandbox escapes (CVE-2026-47137, CVE-2026-47140, CVE-2026-47210) where sandboxed Node.js code reaches the host process to spawn a shell. It spans the exploit \u2192 actions kill-chain stages by temporally joining an external HTTP/VPN inbound event (stage 1) with a server-side process spawn or privileged downstream call parented by the edge daemon (stage 2) inside a 10-minute window. It fires when node / coderd / nginx / gunicorn / sshd / globalprotect / panvpnd parents cmd|powershell|bash|sh, or executes a recon / token-redemption command (whoami, /etc/passwd, gitsshkey, external-auth, AssumeRoleWithWebIdentity, child_process), within 10 min of an external 2xx hit on that host. It does NOT fire on routine VPN tunnel establishment from corporate-IP-allow-listed sources, CI/CD webhook \u2192 handler pipelines whose parent is a build agent rather than the edge daemon, or interactive admin operations launched via SSM / kube exec / RBAC tooling because those skip the edge-daemon parent process and rarely originate from a freshly-seen external IP.\n\nRationale: All eight in-scope articles collapse to the same operational shape: an unauthenticated or sandboxed external interaction produces a privileged in-host outcome on an internet-facing edge component. CVE-2026-0257 turns an external GlobalProtect probe into a VPN session and downstream shell pivot; CVE-2026-46354 + the @hulumi/policies OIDC bypass turn an anonymous POST into a redeemable session/role assumed seconds later; the three vm2 escapes turn an HTTP-served sandboxed script into a node.exe child shell. The temporal join (external 2xx / accepted inbound \u2192 edge-daemon-parented child shell or token/role redemption within 10 minutes on the same host) is the single rule that survives weekly IOC rotation because it keys on the universal post-condition rather than any specific endpoint path, CVE, or IP.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1133",
          "name": "External Remote Services"
        },
        {
          "id": "T1528",
          "name": "Steal Application Access Token"
        },
        {
          "id": "T1550.001",
          "name": "Use Alternate Authentication Material: Application Access Token"
        },
        {
          "id": "T1550.004",
          "name": "Use Alternate Authentication Material: Web Session Cookie"
        },
        {
          "id": "T1611",
          "name": "Escape to Host"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        },
        {
          "id": "T1078.004",
          "name": "Valid Accounts: Cloud Accounts"
        }
      ],
      "data_models": [
        "Web",
        "Network_Traffic.All_Traffic",
        "Authentication",
        "Endpoint.Processes",
        "Change"
      ],
      "splunk_spl": "| tstats summariesonly=t count as web_hits min(_time) as stage1_time max(_time) as stage1_end values(Web.uri_path) as endpoint values(Web.status) as http_status from datamodel=Web where Web.http_method IN (\"POST\",\"GET\") AND Web.status IN (200,201,202,204) by Web.src Web.dest | `drop_dm_object_name(Web)` | rename src as src_ip, dest as edge_host | where match(src_ip, \"^(?!10\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.|192\\.168\\.|127\\.|169\\.254\\.).*\") | join type=inner edge_host [ | tstats summariesonly=t count as child_spawns min(_time) as stage2_time values(Processes.process) as downstream_cmd values(Processes.process_name) as downstream_proc values(Processes.parent_process_name) as edge_daemon values(Processes.user) as runas_user from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"node.exe\",\"node\",\"coderd\",\"gunicorn\",\"nginx\",\"httpd\",\"panvpnd\",\"globalprotect\",\"openvpn\",\"sshd\",\"java.exe\",\"java\",\"dotnet.exe\") AND Processes.process_name IN (\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"bash\",\"sh\",\"dash\",\"zsh\",\"ash\",\"wmic.exe\",\"wscript.exe\",\"cscript.exe\")) OR Processes.process IN (\"*AssumeRoleWithWebIdentity*\",\"*gitsshkey*\",\"*external-auth*\",\"*azure-instance-identity*\",\"*child_process*\",\"*execSync*\",\"*whoami*\",\"*/etc/passwd*\",\"*net user*\",\"*ipconfig*\") by Processes.dest | `drop_dm_object_name(Processes)` | rename dest as edge_host ] | where stage2_time>=stage1_time AND (stage2_time-stage1_time)<=600 | eval delay_sec=stage2_time-stage1_time, firstTime=stage1_time, lastTime=stage2_time | stats min(firstTime) as firstTime max(lastTime) as lastTime values(endpoint) as endpoints values(http_status) as http_status values(downstream_cmd) as downstream_cmds values(downstream_proc) as downstream_procs values(edge_daemon) as edge_daemons values(runas_user) as runas_user min(delay_sec) as min_delay_sec by src_ip edge_host | convert ctime(firstTime) ctime(lastTime) | sort 0 - firstTime",
      "defender_kql": "let LookbackDays = 14d;\nlet CorrelationWindow = 10m;\nlet EdgeDaemons = dynamic([\"node.exe\",\"node\",\"coderd\",\"gunicorn\",\"nginx\",\"nginx.exe\",\"httpd\",\"httpd.exe\",\"panvpnd\",\"globalprotect\",\"openvpn\",\"openvpn.exe\",\"sshd\",\"java.exe\",\"java\",\"dotnet.exe\"]);\nlet DownstreamShells = dynamic([\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"bash\",\"sh\",\"dash\",\"zsh\",\"ash\",\"wmic.exe\",\"wscript.exe\",\"cscript.exe\"]);\nlet DownstreamTokens = dynamic([\"AssumeRoleWithWebIdentity\",\"gitsshkey\",\"external-auth\",\"azure-instance-identity\",\"child_process\",\"execSync\"]);\nlet ReconTokens = dynamic([\"whoami\",\"/etc/passwd\",\"net user\",\"ipconfig\",\"hostname\",\"uname -a\",\"cat /etc\"]);\nlet EdgeContact =\n    DeviceNetworkEvents\n    | where Timestamp > ago(LookbackDays)\n    | where RemoteIPType == \"Public\"\n    | where ActionType in~ (\"InboundConnectionAccepted\",\"ConnectionSuccess\",\"InboundConnectionEstablished\")\n    | where InitiatingProcessFileName has_any (EdgeDaemons)\n    | project Stage1Time = Timestamp, DeviceName, SourceIP = RemoteIP,\n              RemotePort, EdgeProc = InitiatingProcessFileName, EdgeCmd = InitiatingProcessCommandLine;\nlet DownstreamAction =\n    DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where InitiatingProcessFileName has_any (EdgeDaemons)\n    | where FileName has_any (DownstreamShells)\n        or ProcessCommandLine has_any (DownstreamTokens)\n        or ProcessCommandLine has_any (ReconTokens)\n    | where AccountName !endswith \"$\"\n    | where AccountName !in~ (\"system\",\"local service\",\"network service\")\n    | project Stage2Time = Timestamp, DeviceName, ChildImage = FileName,\n              ChildCmd = ProcessCommandLine, EdgeDaemon = InitiatingProcessFileName,\n              EdgeDaemonCmd = InitiatingProcessCommandLine, RunAs = AccountName, SHA256;\nEdgeContact\n| join kind=inner DownstreamAction on DeviceName\n| where Stage2Time between (Stage1Time .. Stage1Time + CorrelationWindow)\n| extend DelaySec = datetime_diff('second', Stage2Time, Stage1Time)\n| summarize arg_min(Stage1Time, *), MinDelaySec = min(DelaySec),\n            Endpoints = make_set(RemotePort, 10), Children = make_set(ChildImage, 10),\n            ChildCmds = make_set(ChildCmd, 10)\n            by DeviceName, SourceIP, EdgeDaemon, RunAs\n| project Stage1Time, Stage2Time, MinDelaySec, DeviceName, SourceIP, RemotePort,\n          EdgeDaemon, EdgeDaemonCmd, Children, ChildCmds, RunAs, SHA256\n| order by Stage1Time desc"
    },
    {
      "id": "UC_WEEKLY_INSTALL_TRIGGERED_REGISTRY_PUBLISH_OR_GIT_PUSH_SUP",
      "title": "[WEEKLY] Install-Triggered Registry Publish or Git Push (Supply-Chain Worm Self-Propagation)",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Detects the self-propagation primitive shared by the Miasma / Shai-Hulud npm worm wave, the Laravel-Lang Composer tag-rewrite attack, the @cap-js/openapi advisory, and the Atomic Arch AUR campaign: a package-manager install lifecycle (npm/yarn/pnpm/composer/pip/makepkg) is followed within 30 minutes on the same endpoint by a registry publish (`npm publish`, `yarn publish`, `composer publish`) or a `git push` to a developer-owned repository, indicating that stolen npm/PAT/SSH credentials harvested by the install-time payload are being weaponised to push trojanised tags back into the supply chain. This fortnight's articles \u2014 StepSecurity (Laravel-Lang, Miasma binding.gyp, Microsoft Azure 73-repo Miasma, AUR Atomic Arch), Snyk (@redhat-cloud-services, Node-gyp worm), GHSA @cap-js/openapi, Aikido (EDR/proxy bypass), and The Hacker News (npm 12 disabling install scripts) \u2014 all describe the same execution-to-exfil-to-republish chain. It spans execution \u2192 credential access \u2192 exfiltration / lateral movement (the worm's hop). It fires when an interactive developer workstation or build agent emits a publish/push within the correlation window of an install on the same host; it will NOT fire on isolated registry mirrors, scheduled CI release jobs where the install host and publishing host differ, or on hosts that only `install` without ever publishing \u2014 those are the legitimate consumer pattern.\n\nRationale: Every article in scope describes the same self-propagation primitive: install-time code execution harvests publish credentials, then re-uses them in-place to push the worm forward. Anchoring on the parent-process install command + same-host publish/push within a tight window catches that primitive regardless of which registry, OS, language, or specific IOC (binding.gyp, preinstall script, AUR PKGBUILD, Composer tag) the campaign rotates to next.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1546.016",
          "name": "Event Triggered Execution: Installer Packages"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1528",
          "name": "Steal Application Access Token"
        },
        {
          "id": "T1567",
          "name": "Exfiltration Over Web Service"
        },
        {
          "id": "T1098",
          "name": "Account Manipulation"
        }
      ],
      "data_models": [
        "Endpoint.Processes"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as installTime from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"npm.exe\",\"node.exe\",\"yarn.exe\",\"pnpm.exe\",\"composer.exe\",\"composer.phar\",\"pip.exe\",\"pip3.exe\",\"makepkg\",\"pacman\",\"yay\",\"paru\")) OR (Processes.process IN (\"*npm install*\",\"*npm i *\",\"*yarn install*\",\"*pnpm install*\",\"*composer install*\",\"*composer update*\",\"*pip install*\",\"*makepkg -si*\",\"*pacman -S *\",\"*yay -S *\")) by Processes.dest Processes.user Processes.parent_process_name Processes.process | `drop_dm_object_name(Processes)` | rename process AS installCmd, parent_process_name AS installParent | join type=inner dest [ | tstats `summariesonly` count min(_time) as publishTime from datamodel=Endpoint.Processes where (Processes.process_name IN (\"git.exe\",\"git\") AND Processes.process=\"*push*\") OR Processes.process IN (\"*npm publish*\",\"*yarn publish*\",\"*pnpm publish*\",\"*composer publish*\") OR (Processes.process IN (\"*curl*\",\"*wget*\",\"*Invoke-WebRequest*\",\"*Invoke-RestMethod*\") AND (Processes.process=\"*registry.npmjs.org/-/package*\" OR Processes.process=\"*api.github.com/repos*\" OR Processes.process=\"*packagist.org/api*\")) by Processes.dest Processes.user Processes.process_name Processes.process | `drop_dm_object_name(Processes)` | rename process AS publishCmd, process_name AS publishProcess ] | eval delaySec = publishTime - installTime | where delaySec >= 0 AND delaySec <= 1800 | table installTime, publishTime, delaySec, dest, user, installParent, installCmd, publishProcess, publishCmd | sort - installTime",
      "defender_kql": "let LookbackDays = 7d;\nlet CorrelationWindowSec = 1800;\nlet InstallEvents = DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where (FileName in~ (\"npm.exe\",\"node.exe\",\"yarn.exe\",\"pnpm.exe\",\"composer.exe\",\"composer.phar\",\"pip.exe\",\"pip3.exe\",\"makepkg\",\"pacman\",\"yay\",\"paru\"))\n        or ProcessCommandLine has_any (\"npm install\",\"npm i \",\"yarn install\",\"pnpm install\",\"composer install\",\"composer update\",\"pip install\",\"makepkg -si\",\"pacman -S \",\"yay -S \")\n    | project InstallTime = Timestamp, DeviceId, DeviceName, AccountName,\n              InstallFile = FileName, InstallCmd = ProcessCommandLine;\nlet PublishEvents = DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where (FileName in~ (\"git.exe\",\"git\") and ProcessCommandLine has \"push\")\n        or ProcessCommandLine has_any (\"npm publish\",\"yarn publish\",\"pnpm publish\",\"composer publish\")\n        or (ProcessCommandLine has_any (\"curl\",\"wget\",\"Invoke-WebRequest\",\"Invoke-RestMethod\")\n            and ProcessCommandLine has_any (\"registry.npmjs.org/-/package\",\"api.github.com/repos\",\"packagist.org/api\"))\n    | project PublishTime = Timestamp, DeviceId, DeviceName, AccountName,\n              PublishFile = FileName, PublishCmd = ProcessCommandLine;\nInstallEvents\n| join kind=inner PublishEvents on DeviceId\n| where PublishTime between (InstallTime .. InstallTime + CorrelationWindowSec * 1s)\n| where not(AccountName endswith \"$\")\n| where AccountName !in~ (\"system\",\"local service\",\"network service\")\n| project InstallTime, PublishTime,\n          DelaySec = datetime_diff('second', PublishTime, InstallTime),\n          DeviceName, AccountName, InstallFile, InstallCmd, PublishFile, PublishCmd\n| order by InstallTime desc"
    },
    {
      "id": "UC_WEEKLY_INTERNET_FACING_SERVER_PROCESS_SPAWNS_INTERPRETER_",
      "title": "[WEEKLY] Internet-facing server process spawns interpreter then beacons to first-seen external host within 5 minutes",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the post-exploitation chain in which a server-side process exposed to the internet (web-worker, edge appliance daemon, endpoint-management agent, IIS worker) spawns a scripting interpreter or LOLBin, and that child process \u2014 within a 5-minute window \u2014 initiates outbound traffic to an external host the organisation has not contacted in the prior 30 days. This fortnight the pattern dominates the briefing set: Microsoft's 'From edge appliance to enterprise compromise: Multi-stage Linux intrusion via F5 and Confluence' (2026-05-22) chains CVE-2025-33073 from edge to lateral, Arctic Wolf's 'Threat Actors Exploit Critical FortiClient EMS Flaw to Deploy Credential Stealer' (2026-05-28, CVE-2026-35616) abuses EmsService.exe to drop stealers, Talos's 'From PDB strings to MaaS: Tracking a commodity BadIIS ecosystem' (2026-05-19) places its loader under w3wp, and the LiquidJS SSTI advisory (CVE-2026-45618, 2026-05-27) hands attackers a node-driven RCE primitive \u2014 every one of these surfaces an interpreter child of a server parent followed almost immediately by attacker-infra callout. Spans exploit -> install -> c2 in a single signal. It fires when both the parent-child relation and a first-seen public destination hold inside the window; it will NOT fire on routine sysadmin shells against the same parent, scheduled patching/telemetry to long-established vendor endpoints (those clear the 30-day baseline), or interpreter spawns whose only egress is to internal/RFC1918 ranges.\n\nRationale: Each campaign this fortnight (F5/Confluence on Linux, FortiClient EMS, BadIIS, LiquidJS SSTI) ends the exploit stage with the same observable: a server-shaped parent process spawning an interpreter that immediately reaches attacker infrastructure. Joining the spawn to a per-process-GUID egress within a 5-minute window and anti-joining against a 30-day baseline of org-wide destinations isolates that joint signal without relying on any rotating hash, IP, or domain.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        },
        {
          "id": "T1505.004",
          "name": "Server Software Component: IIS Components"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats summariesonly=true count min(_time) as proc_time from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"w3wp.exe\",\"httpd.exe\",\"httpd\",\"nginx.exe\",\"nginx\",\"java.exe\",\"java\",\"node.exe\",\"node\",\"tomcat.exe\",\"EmsService.exe\",\"fmsd\",\"jboss.exe\",\"confluence\",\"atlassian-confluence\",\"forticlient.exe\")) AND (Processes.process_name IN (\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"mshta.exe\",\"wscript.exe\",\"cscript.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"bitsadmin.exe\",\"certutil.exe\",\"bash\",\"sh\",\"python.exe\",\"python\",\"python3\",\"perl\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\")) by host Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_guid | `drop_dm_object_name(\"Processes\")` | join type=inner host [ | tstats summariesonly=true min(_time) as net_time values(All_Traffic.dest_ip) as remote_ip values(All_Traffic.dest) as remote_host from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_category=\"external\" by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | rename src as host ] | eval delay_sec = net_time - proc_time | where delay_sec>=0 AND delay_sec<=300 | join type=left remote_host [ | tstats summariesonly=true dc(All_Traffic.src) as baseline_hosts from datamodel=Network_Traffic.All_Traffic where earliest=-30d@d latest=-1d@d by All_Traffic.dest | rename All_Traffic.dest as remote_host ] | where isnull(baseline_hosts) OR baseline_hosts<3 | table proc_time net_time delay_sec host user parent_process_name process_name process remote_ip remote_host | sort - proc_time",
      "defender_kql": "let Lookback = 7d;\nlet WindowSec = 300;\nlet Baseline = DeviceNetworkEvents\n    | where Timestamp between (ago(30d) .. ago(1d))\n    | where RemoteIPType == \"Public\" and isnotempty(RemoteUrl)\n    | summarize BaselineHosts = dcount(DeviceName) by RemoteUrl\n    | where BaselineHosts >= 3;     // 3 = empirical: drops one-off vendor blips while keeping novel infra\nlet ServerSpawns = DeviceProcessEvents\n    | where Timestamp > ago(Lookback)\n    | where InitiatingProcessFileName in~ (\"w3wp.exe\",\"httpd.exe\",\"httpd\",\"nginx.exe\",\"nginx\",\"java.exe\",\"java\",\"node.exe\",\"node\",\"tomcat.exe\",\"emsservice.exe\",\"fmsd\",\"jboss.exe\",\"confluence\",\"forticlient.exe\",\"f5-bigip.exe\")\n    | where FileName in~ (\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"mshta.exe\",\"wscript.exe\",\"cscript.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"bitsadmin.exe\",\"certutil.exe\",\"bash\",\"sh\",\"python.exe\",\"python\",\"python3\",\"perl\",\"curl.exe\",\"wget.exe\")\n    | where AccountName !endswith \"$\"           // exclude machine accounts\n    | where AccountName !in~ (\"system\",\"local service\",\"network service\")\n    | project SpawnTime = Timestamp, DeviceId, DeviceName, AccountName,\n              ParentImage = InitiatingProcessFileName,\n              ChildImage = FileName,\n              ChildCmd = ProcessCommandLine,\n              ChildPGUID = tostring(ProcessUniqueId);\nlet Egress = DeviceNetworkEvents\n    | where Timestamp > ago(Lookback)\n    | where RemoteIPType == \"Public\"\n    | project NetTime = Timestamp, DeviceId,\n              InitPGUID = tostring(InitiatingProcessId),\n              RemoteIP, RemoteUrl, RemotePort;\nServerSpawns\n| join kind=inner Egress on DeviceId, $left.ChildPGUID == $right.InitPGUID\n| extend DelaySec = datetime_diff('second', NetTime, SpawnTime)\n| where DelaySec between (0 .. WindowSec)\n| join kind=leftanti Baseline on RemoteUrl\n| project SpawnTime, NetTime, DelaySec, DeviceName, AccountName,\n          ParentImage, ChildImage, ChildCmd, RemoteIP, RemoteUrl, RemotePort\n| order by SpawnTime desc"
    },
    {
      "id": "UC_WEEKLY_INTERNET_FACING_SERVICE_PROCESS_SPAWNING_UNIX_SHEL",
      "title": "[WEEKLY] Internet-Facing Service Process Spawning Unix Shell or Ingress-Tool LOLBin (Edge Zero-Day Post-Exploit)",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the canonical post-exploit transition on internet-facing edge appliances and Linux servers: a service process (PAN-OS sslmgr/useridd/authd, Ivanti EPMM mifs/tomcat/java/catalina, Apache/Nginx, sshd, generic node/python application servers) directly spawns a Unix shell interpreter or an ingress-tool LOLBin (bash/sh/python/perl/curl/wget/nc/socat/busybox) with a non-automation parent. This fortnight three high-severity zero-days dominated the briefings \u2014 CVE-2026-0300 PAN-OS Captive Portal unauthenticated RCE exploited in-the-wild since April (Unit 42 'Threat Brief: Exploitation of PAN-OS Captive Portal Zero-Day', Cyber Security News 'Palo Alto Networks Firewall Zero-Day RCE', BleepingComputer 'Palo Alto Networks firewall zero-day exploited for nearly a month'), CVE-2026-6973 Ivanti EPMM remote-code-execution under CISA four-day emergency directive (BleepingComputer 'CISA gives feds four days to patch Ivanti flaw', 'Ivanti warns of new EPMM flaw'), and the Linux kernel 'Dirty Frag' LPE (BleepingComputer 8 May) \u2014 all set against the 'Patient Zero', 'Day Zero Readiness', and 'After Mythos' essays from The Hacker News framing edge compromise as the new front door, plus the 'Zero-Auth' Schemata API and Android CVE-2026-0073 RCE pieces that share the same exploit\u2192post-exploit shape. The chain spans MITRE exploit (T1190) \u2192 install (T1505.003 webshell or T1059.004 shell) \u2192 C2 staging (T1105 / T1572 reverse-tunnel like the EarthWorm reversesocks5 dropper described by Unit 42); the inbound exploit is invisible at the endpoint, but the resulting service\u2192shell parentage is unmistakable. It fires when sslmgr/useridd/mifs/tomcat/java/sshd/httpd/nginx are the direct parent of bash/sh/python/curl/wget/nc with non-trivial command lines; it does NOT fire on routine sysadmin maintenance (interactive parents like login/sudo), on configuration-management tools (puppet/ansible/chef/salt-minion explicitly excluded), or on health-check style invocations (`status`, `--version`, `health`).\n\nRationale: All twelve articles describe the same exploit\u2192install transition on internet-exposed services (PAN-OS Captive Portal, Ivanti EPMM Tomcat/mifs, Android adbd, Linux kernel LPE, Schemata API, plus the THN 'Patient Zero' essays calling out edge compromise). The exact inbound exploit varies per CVE and is often invisible at the endpoint, but the post-exploit signature \u2014 a long-running service process directly parenting bash/sh/python/curl \u2014 is shared, doesn't rotate with IOCs, and is structurally rare in production because legitimate maintenance flows through interactive shells or configuration-management agents that we explicitly exclude.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1505.003",
          "name": "Server Software Component: Web Shell"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        },
        {
          "id": "T1572",
          "name": "Protocol Tunneling"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) AS firstTime max(_time) AS lastTime values(Processes.process) AS cmdlines values(Processes.process_path) AS exec_paths values(Processes.parent_process) AS parent_cmds from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"httpd\",\"nginx\",\"apache2\",\"java\",\"tomcat\",\"sslmgr\",\"useridd\",\"authd\",\"mifs\",\"node\",\"python\",\"python3\",\"sshd\",\"pan_comm_mgr\",\"panlogd\",\"catalina.sh\",\"ws_TomcatService.sh\") AND Processes.process_name IN (\"bash\",\"sh\",\"csh\",\"zsh\",\"dash\",\"ksh\",\"tcsh\",\"python\",\"python3\",\"perl\",\"ruby\",\"curl\",\"wget\",\"nc\",\"ncat\",\"socat\",\"busybox\",\"gawk\") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.parent_process | `drop_dm_object_name(Processes)` | where NOT match(parent_process,\"(?i)(puppet|ansible|chef-client|salt-minion|systemd|/cron[/ ]|crond)\") AND NOT match(process,\"(?i)(\\s--version|\\sstatus(\\s|$)|\\shealthcheck|\\s-V(\\s|$))\") | join type=inner dest [| tstats `summariesonly` count from datamodel=Endpoint.Filesystem where Filesystem.action=\"created\" AND (Filesystem.file_path=\"*/webapps/*\" OR Filesystem.file_path=\"*/htdocs/*\" OR Filesystem.file_path=\"*/www/*\" OR Filesystem.file_path=\"*/mi/tomcat/*\") AND (Filesystem.file_name=\"*.jsp\" OR Filesystem.file_name=\"*.jspx\" OR Filesystem.file_name=\"*.php\" OR Filesystem.file_name=\"*.aspx\" OR Filesystem.file_name=\"*.sh\") by Filesystem.dest Filesystem.file_path Filesystem.file_name | `drop_dm_object_name(Filesystem)` | rename dest AS dest] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | sort - lastTime",
      "defender_kql": "// Edge zero-day post-exploit \u2014 service process spawning shell/ingress LOLBin on Linux/macOS\nlet LookbackDays = 7d;\nlet _WebServerProcs = dynamic([\n    \"httpd\",\"nginx\",\"apache2\",\"java\",\"tomcat\",\"sslmgr\",\"useridd\",\n    \"authd\",\"mifs\",\"node\",\"python\",\"python3\",\"sshd\",\"pan_comm_mgr\",\n    \"panlogd\",\"catalina.sh\",\"ws_TomcatService.sh\"]);\nlet _ShellChildren = dynamic([\n    \"bash\",\"sh\",\"csh\",\"zsh\",\"dash\",\"ksh\",\"tcsh\",\"python\",\"python3\",\n    \"perl\",\"ruby\",\"curl\",\"wget\",\"nc\",\"ncat\",\"socat\",\"busybox\",\"gawk\"]);\nlet _AutomationParents = dynamic([\n    \"puppet\",\"ansible-playbook\",\"chef-client\",\"salt-minion\",\"cron\",\"crond\",\"systemd\"]);\nlet ShellSpawns = DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where OSPlatform in (\"Linux\",\"macOS\")\n    | where InitiatingProcessFileName in~ (_WebServerProcs)\n    | where FileName in~ (_ShellChildren)\n    | where InitiatingProcessParentFileName !in~ (_AutomationParents)\n    | where ProcessCommandLine !has \" --version\"\n        and ProcessCommandLine !has \" -V\"\n        and not(ProcessCommandLine matches regex @\"(?i)\\bstatus\\b\")\n        and ProcessCommandLine !has \"healthcheck\"\n    | project Timestamp, DeviceId, DeviceName, AccountName,\n              ParentProc = InitiatingProcessFileName,\n              ParentCmd  = InitiatingProcessCommandLine,\n              ChildProc  = FileName,\n              ChildCmd   = ProcessCommandLine,\n              FolderPath, SHA256;\n// Corroborating signal \u2014 webshell/script drop into a web root by the same kind of service process\nlet WebshellDrops = DeviceFileEvents\n    | where Timestamp > ago(LookbackDays)\n    | where ActionType == \"FileCreated\"\n    | where FolderPath has_any (\n        @\"/mi/tomcat/webapps/\", @\"/var/www/\", @\"/usr/share/nginx/html/\",\n        @\"/opt/pan/\", @\"/var/panlogs/\", @\"/opt/tomcat/webapps/\",\n        @\"/srv/www/\", @\"/htdocs/\")\n    | where FileName endswith \".jsp\" or FileName endswith \".jspx\"\n         or FileName endswith \".php\" or FileName endswith \".aspx\"\n         or FileName endswith \".sh\"  or FileName endswith \".py\"\n    | where InitiatingProcessFileName in~ (_WebServerProcs)\n    | project DropTime = Timestamp, DeviceId, DroppedFile = FileName, DropPath = FolderPath, DroppedBy = InitiatingProcessFileName;\nShellSpawns\n| join kind=leftouter WebshellDrops on DeviceId\n| extend CorroboratingDropMin = iff(isnotempty(DropTime), datetime_diff('minute', Timestamp, DropTime), int(null))\n| where isnull(CorroboratingDropMin) or abs(CorroboratingDropMin) <= 10\n| project Timestamp, DeviceName, AccountName, ParentProc, ParentCmd, ChildProc, ChildCmd, FolderPath, SHA256, CorroboratingDropMin, DroppedFile, DropPath, DroppedBy\n| order by Timestamp desc"
    },
    {
      "id": "UC_WEEKLY_INTERNET_FACING_SERVICE_PROCESS_SPAWNS_SHELL_LOLBI",
      "title": "[WEEKLY] Internet-facing service process spawns shell/LOLBin within minutes of public inbound connection \u2014 post-RCE command execution",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects the post-exploitation behavioural fingerprint shared by every public-facing-app RCE in this fortnight: a long-running server, daemon, or middleware process (httpd/nginx/apache2, java/tomcat, node, w3wp, php-fpm, sshd, xrdp, panos_pkt_srv, mifs, python/dotnet AI-agent hosts) spawning an interactive shell or reconnaissance LOLBin (cmd, powershell, sh/bash, curl/wget/certutil/bitsadmin, whoami/id/uname) within 10 minutes of an inbound connection from a public IP to that host. The driving campaigns are CVE-2026-0300 (PAN-OS User-ID Captive Portal, exploited since April per Palo Alto / Cyber Security News / The Hacker News), CVE-2026-6973 (Ivanti EPMM), CVE-2026-23918 (Apache HTTPD double-free), CVE-2026-3854 (GitHub git-push -o injection), CVE-2026-22679 (Weaver E-cology dubboApi), CVE-2026-27174 (MajorDoMo /admin.php), CVE-2026-3965/4047 (Qinglong scheduler crypto-mining), CVE-2025-68670 (xrdp pre-auth), and CVE-2026-26030 / 25592 / 31431 (Microsoft Semantic Kernel and other AI-agent-framework RCEs). The chain spans exploit (T1190) and the immediately following actions/install stages: although every CVE has different IOCs and entry points, all 12 articles converge on the same post-exploit pivot \u2014 the service process drops to shell to stage tooling. WILL fire on httpd spawning /bin/sh shortly after a public-IP HTTPS hit, panos_pkt_srv spawning curl, or python.exe (Semantic Kernel host) launching cmd.exe after an inbound API call. WILL NOT fire on cron/systemd-triggered scripts (no preceding inbound public connection), pre-forked CGI workers spawned at boot, intra-org admin SSH (private-IP source filtered out), or service-account contexts where the parent isn't on the internet-facing service list.\n\nRationale: All 12 articles describe distinct CVEs in distinct internet-facing products (PAN-OS, Ivanti EPMM, Apache HTTPD, GitHub, MajorDoMo, Weaver E-cology, Qinglong, xrdp, Microsoft Semantic Kernel) but the post-exploit step is identical across every one: the long-running service process executes an interactive shell, recon binary, or download utility to stage follow-on tooling. Correlating that child-spawn against an inbound public-IP connection in the preceding 10-minute window cleanly separates exploitation traffic from legitimate CGI/installer behaviour and survives the inevitable IOC rotation that would invalidate per-CVE rules.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1505.003",
          "name": "Server Software Component: Web Shell"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1133",
          "name": "External Remote Services"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as proc_time values(Processes.process) as cmdline values(Processes.parent_process) as parent_cmdline from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"httpd\",\"httpd.exe\",\"nginx\",\"nginx.exe\",\"apache2\",\"apache.exe\",\"java\",\"java.exe\",\"tomcat.exe\",\"node\",\"node.exe\",\"w3wp.exe\",\"php-fpm\",\"php-fpm7\",\"php-fpm8\",\"sshd\",\"xrdp\",\"mifs\",\"panos_pkt_srv\",\"gunicorn\",\"uwsgi\",\"python\",\"python.exe\",\"python3\",\"pythonw.exe\",\"dotnet.exe\",\"ruby\",\"ruby.exe\") AND Processes.process_name IN (\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"wscript.exe\",\"cscript.exe\",\"bitsadmin.exe\",\"certutil.exe\",\"curl.exe\",\"wget.exe\",\"whoami.exe\",\"net.exe\",\"systeminfo.exe\",\"sh\",\"bash\",\"dash\",\"zsh\",\"ksh\",\"curl\",\"wget\",\"whoami\",\"id\",\"uname\",\"python\",\"python3\",\"perl\") AND NOT Processes.user=\"*$\" by Processes.dest Processes.user Processes.parent_process_name Processes.process_name | `drop_dm_object_name(Processes)` | rename dest as host | join type=inner host [ | tstats `summariesonly` min(_time) as net_time values(All_Traffic.src) as src_ip values(All_Traffic.dest_port) as dest_port from datamodel=Network_Traffic.All_Traffic where All_Traffic.action!=\"blocked\" AND All_Traffic.action!=\"deny\" AND All_Traffic.action!=\"drop\" AND NOT (cidrmatch(\"10.0.0.0/8\",All_Traffic.src) OR cidrmatch(\"172.16.0.0/12\",All_Traffic.src) OR cidrmatch(\"192.168.0.0/16\",All_Traffic.src) OR cidrmatch(\"127.0.0.0/8\",All_Traffic.src) OR cidrmatch(\"169.254.0.0/16\",All_Traffic.src) OR cidrmatch(\"100.64.0.0/10\",All_Traffic.src)) by All_Traffic.dest | `drop_dm_object_name(All_Traffic)` | rename dest as host ] | eval delay_sec=proc_time-net_time | where delay_sec>=0 AND delay_sec<=600 | table host user parent_process_name process_name cmdline parent_cmdline src_ip dest_port delay_sec net_time proc_time | sort - proc_time",
      "defender_kql": "let LookbackDays = 7d;\nlet WindowMinutes = 10m;\nlet _server_parents = dynamic([\"httpd\",\"httpd.exe\",\"nginx\",\"nginx.exe\",\"apache2\",\"apache.exe\",\"java\",\"java.exe\",\"tomcat.exe\",\"node\",\"node.exe\",\"w3wp.exe\",\"php-fpm\",\"php-fpm7\",\"php-fpm8\",\"sshd\",\"xrdp\",\"mifs\",\"panos_pkt_srv\",\"gunicorn\",\"uwsgi\",\"python\",\"python.exe\",\"python3\",\"pythonw.exe\",\"dotnet.exe\",\"ruby\",\"ruby.exe\"]);\nlet _shell_children = dynamic([\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"wscript.exe\",\"cscript.exe\",\"bitsadmin.exe\",\"certutil.exe\",\"curl.exe\",\"wget.exe\",\"whoami.exe\",\"net.exe\",\"systeminfo.exe\",\"sh\",\"bash\",\"dash\",\"zsh\",\"ksh\",\"curl\",\"wget\",\"whoami\",\"id\",\"uname\",\"python\",\"python3\",\"perl\"]);\nlet InboundPublic = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackDays)\n    | where ActionType in (\"InboundConnectionAccepted\",\"ConnectionSuccess\",\"ConnectionAttempt\",\"HttpConnectionInspected\")\n    | where RemoteIPType == \"Public\"\n    | project NetTime = Timestamp, DeviceId, RemoteIP, RemotePort, LocalPort;\nDeviceProcessEvents\n| where Timestamp > ago(LookbackDays)\n| where AccountName !endswith \"$\"\n| where InitiatingProcessFileName has_any (_server_parents)\n| where FileName has_any (_shell_children)\n| join kind=inner InboundPublic on DeviceId\n| where Timestamp between (NetTime .. NetTime + WindowMinutes)\n| project Timestamp, DeviceName, AccountName,\n          ParentProcess = InitiatingProcessFileName,\n          ParentCmd     = InitiatingProcessCommandLine,\n          ChildProcess  = FileName,\n          ChildCmd      = ProcessCommandLine,\n          DelaySec      = datetime_diff('second', Timestamp, NetTime),\n          RemoteIP, RemotePort, LocalPort\n| order by Timestamp desc"
    },
    {
      "id": "UC_WEEKLY_LANGUAGE_RUNTIME_SERVER_NODE_PYTHON_JAVA_SPAWNS_OS",
      "title": "[WEEKLY] Language-runtime server (node/python/java) spawns OS shell shortly after inbound request \u2014 eval / sandbox-escape exploitation chain",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects the joint pattern across this fortnight's wave of server-side code-injection and sandbox-escape CVEs: a language-runtime process that hosts a network listener (Node.js, Python/uvicorn/gunicorn, Java/Yamcs) spawns an OS interpreter or LOLBin within ~5 minutes of accepting an inbound HTTP/JSON-RPC request. The pattern is the common terminal step across the five vm2 sandbox escapes ('CVE-2026-47131 vm2 Sandbox Escape', 'CVE-2026-47137 vm2 nesting:true patch bypass', 'CVE-2026-47140 NodeVM builtin denylist bypass', 'CVE-2026-47208 vm2 Promise species sandbox breakout', 'CVE-2026-47210 vm2 JSPI .finally() species bypass'), the PraisonAI eval/exec chain ('CVE-2026-47391 PraisonAI unauthenticated A2A eval()', 'CVE-2026-47392 PraisonAI print.__self__ builtins leak', 'CVE-2026-47393 PraisonAI deploy --type api auth-disabled Flask'), and the Yamcs Janino server-side code injection ('CVE-2026-44632'). It spans the exploit \u2192 install kill-chain transition: a sandboxed evaluator inside the runtime is bypassed, runtime gains arbitrary code execution in-process, and the very first observable artefact is a child OS shell or downloader. Fires when the runtime is reachable from an external IP and produces a shell/curl/wget/certutil child within the correlation window; does NOT fire on developer workstations running `npm test`/`pytest` (no inbound public connection) nor on legitimate uvicorn worker forks (filtered by child filename), and does NOT fire on a runtime child-shell with no preceding inbound request.\n\nRationale: Each of the 12 advisories is its own ecosystem-specific vulnerability (Node vm2 escapes, Python PraisonAI eval/exec leaks, Java Janino injection) but they all collapse to the same final observable: a long-lived server-role runtime process gains in-process arbitrary code execution and is forced to spawn an OS interpreter or downloader it would never legitimately invoke. Requiring an inbound connection on the runtime's listener within 5 minutes of the shell spawn \u2014 instead of just flagging any runtime\u2192shell pair \u2014 strips out CI/test/dev noise and turns the catalog's per-CVE hunts into one alerting-grade signal that covers any future variant of this campaign.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1068",
          "name": "Exploitation for Privilege Escalation"
        },
        {
          "id": "T1611",
          "name": "Escape to Host"
        },
        {
          "id": "T1211",
          "name": "Exploitation for Defense Evasion"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic",
        "Web"
      ],
      "splunk_spl": "| tstats `summariesonly` count from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"java.exe\",\"java\",\"uvicorn\",\"uvicorn.exe\",\"gunicorn\",\"yamcsd\",\"yamcs\")) AND (Processes.process_name IN (\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh\",\"bash\",\"dash\",\"zsh\",\"ash\",\"wmic.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\",\"certutil.exe\",\"bitsadmin.exe\",\"nc\",\"ncat\",\"ncat.exe\")) AND NOT (Processes.parent_process_name IN (\"uvicorn\",\"uvicorn.exe\",\"gunicorn\") AND Processes.process_name IN (\"python\",\"python.exe\",\"python3\")) by _time span=1s Processes.dest Processes.user Processes.parent_process_name Processes.parent_process Processes.process_name Processes.process Processes.process_path | rename Processes.* as * | eval proc_time=_time | join type=inner dest [ | tstats `summariesonly` earliest(_time) as listen_time values(All_Traffic.src) as remote_srcs values(All_Traffic.dest_port) as listen_ports from datamodel=Network_Traffic.All_Traffic where All_Traffic.direction=\"inbound\" AND All_Traffic.dest_port IN (80,443,3000,5000,8000,8080,8443,8888,8090,5050,4242) by All_Traffic.dest | rename All_Traffic.* as * ] | where (proc_time - listen_time) BETWEEN 0 AND 300 | eval delay_sec = proc_time - listen_time | stats min(listen_time) as listen_time, min(proc_time) as proc_time, min(delay_sec) as min_delay_sec, values(process_name) as children, values(process) as child_cmd, values(parent_process) as parent_cmd, values(remote_srcs) as remote_srcs, values(listen_ports) as listen_ports by dest user parent_process_name | where mvcount(children)>=1 | convert ctime(listen_time) ctime(proc_time) | sort - proc_time",
      "defender_kql": "let Lookback = 7d;\nlet WindowMin = 5m;\nlet ServerRuntimes = dynamic([\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"java.exe\",\"java\",\"uvicorn.exe\",\"uvicorn\",\"gunicorn\",\"yamcsd\",\"yamcs\"]);\nlet ShellAndLolbins = dynamic([\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh\",\"bash\",\"dash\",\"zsh\",\"ash\",\"wmic.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\",\"certutil.exe\",\"bitsadmin.exe\",\"nc\",\"ncat\",\"ncat.exe\"]);\nlet ServerPorts = dynamic([80,443,3000,5000,8000,8080,8443,8888,8090,5050,4242]);\nlet Inbound =\n    DeviceNetworkEvents\n    | where Timestamp > ago(Lookback)\n    | where ActionType in (\"InboundConnectionAccepted\",\"ListeningConnectionCreated\",\"ConnectionAcknowledged\")\n    | where InitiatingProcessFileName in~ ServerRuntimes\n    | where LocalPort in (ServerPorts) or RemoteIPType == \"Public\"\n    | project ConnTime = Timestamp, DeviceId, DeviceName,\n              LocalPort, RemoteIP, RemoteIPType,\n              RuntimePid = InitiatingProcessId,\n              RuntimeName = InitiatingProcessFileName,\n              RuntimeCmd = InitiatingProcessCommandLine;\nlet ShellSpawns =\n    DeviceProcessEvents\n    | where Timestamp > ago(Lookback)\n    | where InitiatingProcessFileName in~ ServerRuntimes\n    | where FileName in~ ShellAndLolbins\n    | where not (InitiatingProcessFileName in~ (\"uvicorn.exe\",\"uvicorn\",\"gunicorn\") and FileName in~ (\"python.exe\",\"python\",\"python3\"))   // legitimate worker fork\n    | where AccountName !endswith \"$\"\n    | where AccountName !in~ (\"system\",\"local service\",\"network service\")\n    | project ProcTime = Timestamp, DeviceId, DeviceName, AccountName,\n              RuntimePid = InitiatingProcessId,\n              RuntimeName = InitiatingProcessFileName,\n              RuntimeCmd = InitiatingProcessCommandLine,\n              ChildName = FileName, ChildCmd = ProcessCommandLine, ChildSha256 = SHA256;\nShellSpawns\n| join kind=inner Inbound on DeviceId, RuntimePid\n| where ProcTime between (ConnTime .. ConnTime + WindowMin)\n| extend DelaySec = datetime_diff('second', ProcTime, ConnTime)\n| summarize FirstConn = min(ConnTime), FirstSpawn = min(ProcTime),\n            MinDelaySec = min(DelaySec),\n            Children = make_set(ChildName, 10), SampleChildCmd = any(ChildCmd),\n            ChildHashes = make_set(ChildSha256, 10),\n            RemoteIPs = make_set(RemoteIP, 10), ListenPorts = make_set(LocalPort, 5),\n            SpawnCount = count()\n            by DeviceName, AccountName, RuntimeName, RuntimePid, RuntimeCmd\n| order by FirstSpawn desc"
    },
    {
      "id": "UC_WEEKLY_LINUX_LPE_CHAIN_ANOMALOUS_ALGIF_AEAD_ESP4_ESP6_RXR",
      "title": "[WEEKLY] Linux LPE chain \u2014 anomalous algif_aead/esp4/esp6/rxrpc kernel-module load followed by same-user root transition",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects the behavioural pattern shared by CVE-2026-31431 'Copy Fail' (Theori, April 30; CISA KEV May 1; active exploitation by May 4) and its 'Dirty Frag' successor (CVE-2026-43284 esp4/esp6, CVE-2026-43500 rxrpc; PoC May 7-8 per Microsoft Defender, Unit 42 and Cyber Security News): an unprivileged Linux user triggers an autoload or explicit modprobe/insmod of an otherwise-rare networking/crypto kernel module (algif_aead, esp4, esp6, rxrpc, xfrm_user, af_alg) and within 10 minutes a process owned by that same user spawns a root-owned child or transitions to euid=0 on the same host. The chain spans the exploit phase (T1068 privilege escalation via kernel-module abuse / T1547.006) and is the convergence point Microsoft, Unit 42 and CISA all called out this fortnight regardless of how the unprivileged shell was obtained (web RCE per Article 6 PAN-OS, container break-out per Article 11, malicious package per Article 2's supply-chain framing, or AI-agent RCE per Article 9). Fires when stage 1 (kmod load) and stage 2 (root transition by the same actor) both occur within the correlation window; will NOT fire on routine boot-time module loads by systemd-modules-load/udevd, on dpkg/apt/yum/snapd/unattended-upgrade installs, on configuration-management agents (ansible/puppet/chef/salt) bringing modules in, or on root sessions where no anomalous kmod load preceded.\n\nRationale: Every article in scope \u2014 Copy Fail PoC, CISA KEV listing, Dirty Frag esp4/esp6/rxrpc disclosure, Microsoft Defender's active-attack post and Unit 42's deep-dive \u2014 converges on the same observable: an unprivileged actor causes an unusual kernel networking/crypto module to be brought into the running kernel and then immediately gains root on that host. That two-stage shape is structurally rare in production (legitimate module loads come from boot-time systemd-modules-load or the package manager, never from a user-context parent) and resists IOC rotation because the modules themselves are the vulnerable code paths, not the exploit payload.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1068",
          "name": "Exploitation for Privilege Escalation"
        },
        {
          "id": "T1547.006",
          "name": "Boot or Logon Autostart Execution: Kernel Modules and Extensions"
        },
        {
          "id": "T1611",
          "name": "Escape to Host"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem"
      ],
      "splunk_spl": "| tstats summariesonly=true count min(_time) AS stage1_time values(Processes.process) AS kmod_cmd values(Processes.parent_process_name) AS kmod_parent FROM datamodel=Endpoint.Processes WHERE Processes.os=Linux Processes.process_name IN (\"modprobe\",\"insmod\",\"kmod\") (Processes.process=\"*algif_aead*\" OR Processes.process=\"*esp4*\" OR Processes.process=\"*esp6*\" OR Processes.process=\"*rxrpc*\" OR Processes.process=\"*xfrm_user*\" OR Processes.process=\"*af_alg*\") BY host Processes.user | `drop_dm_object_name(Processes)` | where NOT match(kmod_parent,\"(?i)^(systemd|systemd-udevd|udevd|init|kmod|systemd-modules-load|dpkg|apt|apt-get|yum|dnf|zypper|snapd|unattended-upgrade|ansible-playbook|puppet|chef-client|salt-minion)$\") | where user!=\"root\" AND NOT match(user,\"\\\\$$\") | rename user AS stage1_user | join host [| tstats summariesonly=true count min(_time) AS stage2_time values(Processes.process) AS root_cmd values(Processes.process_name) AS root_proc values(Processes.parent_process_name) AS root_parent FROM datamodel=Endpoint.Processes WHERE Processes.os=Linux Processes.user=\"root\" AND Processes.parent_process_name NOT IN (\"systemd\",\"init\",\"cron\",\"crond\",\"sshd\",\"login\",\"getty\",\"agetty\",\"gdm\",\"gdm3\",\"lightdm\",\"systemd-logind\",\"dbus-daemon\",\"rsyslogd\") BY host] | where stage2_time>=stage1_time AND stage2_time<=relative_time(stage1_time,\"+10m\") | eval delay_sec=stage2_time-stage1_time | table host stage1_user stage1_time kmod_cmd kmod_parent stage2_time delay_sec root_proc root_cmd root_parent | sort - stage1_time",
      "defender_kql": "let LookbackDays = 7d;\nlet CorrelationWindow = 10m;\nlet _baseline_parents = dynamic([\"systemd\",\"systemd-udevd\",\"udevd\",\"init\",\"kmod\",\"systemd-modules-load\",\"dpkg\",\"apt\",\"apt-get\",\"yum\",\"dnf\",\"zypper\",\"snapd\",\"unattended-upgrade\",\"ansible-playbook\",\"puppet\",\"chef-client\",\"salt-minion\"]);\nlet _root_legit_parents = dynamic([\"systemd\",\"init\",\"cron\",\"crond\",\"sshd\",\"login\",\"getty\",\"agetty\",\"gdm\",\"gdm3\",\"lightdm\",\"systemd-logind\",\"dbus-daemon\",\"rsyslogd\"]);\nlet _suspect_modules = dynamic([\"algif_aead\",\"esp4\",\"esp6\",\"rxrpc\",\"xfrm_user\",\"af_alg\"]);\nlet LinuxHosts =\n    DeviceInfo\n    | where Timestamp > ago(1d)\n    | where OSPlatform == \"Linux\"\n    | summarize arg_max(Timestamp, OSDistribution, IsInternetFacing) by DeviceId, DeviceName;\nlet KmodStage =\n    DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where DeviceId in ((LinuxHosts | project DeviceId))\n    | where FileName in~ (\"modprobe\",\"insmod\",\"kmod\")\n    | where ProcessCommandLine has_any (_suspect_modules)\n    | where InitiatingProcessFileName !in~ (_baseline_parents)\n    | where AccountName !in~ (\"root\",\"\")\n    | project StageOneTime = Timestamp, DeviceId, DeviceName,\n              KmodUser = AccountName,\n              KmodCmd = ProcessCommandLine,\n              KmodParent = InitiatingProcessFileName,\n              KmodParentCmd = InitiatingProcessCommandLine;\nlet RootStage =\n    DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where DeviceId in ((LinuxHosts | project DeviceId))\n    | where AccountName =~ \"root\"\n    | where InitiatingProcessAccountName !in~ (\"root\",\"\")\n    | where InitiatingProcessFileName !in~ (_root_legit_parents)\n    | project StageTwoTime = Timestamp, DeviceId,\n              RootProc = FileName,\n              RootCmd = ProcessCommandLine,\n              RootParent = InitiatingProcessFileName,\n              RootParentUser = InitiatingProcessAccountName;\nKmodStage\n| join kind=inner RootStage on DeviceId\n| where StageTwoTime between (StageOneTime .. StageOneTime + CorrelationWindow)\n| where RootParentUser =~ KmodUser\n| project StageOneTime, StageTwoTime,\n          DelaySec = datetime_diff('second', StageTwoTime, StageOneTime),\n          DeviceName, KmodUser, KmodParent, KmodParentCmd, KmodCmd,\n          RootParent, RootProc, RootCmd\n| order by StageOneTime desc"
    },
    {
      "id": "UC_WEEKLY_LOW_CODE_AI_WORKFLOW_RUNTIME_SANDBOX_ESCAPE_SERVER",
      "title": "[WEEKLY] Low-Code / AI Workflow Runtime Sandbox-Escape \u2014 Server Process Spawns Shell + Public Egress",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects the post-exploitation pivot common to this fortnight's cluster of GHSA-published RCEs in Node.js, Python and Rust application runtimes: an embedded code-execution feature that is *supposed* to be sandboxed (n8n HTTP Request / XML nodes \u2014 CVE-2026-44789, CVE-2026-44791; FlowiseAI NodeVM Custom JS \u2014 CVE-2026-46442; vm2 async-generator sandbox breakout \u2014 CVE-2026-45411; DeepSeek-TUI run_tests / task_create auto-approved exec \u2014 CVE-2026-45311, CVE-2026-45374; utcp-cli unsanitized arg substitution \u2014 CVE-2026-45369; Portainer Docker plugin auth bypass \u2014 CVE-2026-44848) causes the runtime parent (node, python, dotnet, cargo, portainer, deepseek-tui, n8n, flowise) to fork a real OS shell, LOLBin, or download tool, optionally followed by outbound egress to a public destination from the same parent. It spans exploit -> install (T1190 -> T1059 / T1105). It fires when the runtime spawns a child it has no business spawning AND the child cmdline carries an injection / staging marker (base64, /dev/tcp/, IEX, bash -i, child_process, getBuiltinModule, constructor.constructor, `etc/passwd`, backticked subshells) AND the parent runtime made an outbound public connection in the surrounding 5 minutes; it will NOT fire on normal developer scaffolding (npm/yarn/pip/poetry/cargo build|test|run) because those cmdline patterns are explicitly suppressed.\n\nRationale: Every one of these 12 GHSAs reaches RCE through a sandboxed code-execution surface inside a long-running app runtime \u2014 vm2 / NodeVM (Flowise, n8n), Python interpreter (utcp-cli), Rust cargo test under deepseek-tui, or the Portainer/Docker-engine plugin path. The single behavioural invariant the catalog doesn't already cover is: the runtime parent itself spawns an OS shell or LOLBin whose cmdline contains exploit-staging tokens AND that runtime egresses to a public destination within five minutes \u2014 a join that survives IOC rotation because it keys on the parent-process / child-binary / behaviour triple, not on any one CVE's payload.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1211",
          "name": "Exploitation for Defense Evasion"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as child_cmdline values(Processes.process_path) as child_path values(Processes.process_hash) as child_hash values(Processes.user) as user from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"node.exe\",\"node\",\"n8n\",\"n8n.exe\",\"flowise\",\"flowise.exe\",\"deepseek-tui\",\"deepseek-tui.exe\",\"deepseek\",\"deepseek.exe\",\"python.exe\",\"python\",\"python3\",\"python3.exe\",\"pythonw.exe\",\"dotnet.exe\",\"dotnet\",\"portainer\",\"portainer.exe\",\"cargo.exe\",\"cargo\",\"ts-node\",\"tsx\",\"nodemon\") AND Processes.process_name IN (\"sh\",\"bash\",\"zsh\",\"dash\",\"ksh\",\"fish\",\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"cscript.exe\",\"wscript.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"nc\",\"ncat\",\"ncat.exe\",\"socat\",\"certutil.exe\",\"bitsadmin.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"whoami.exe\",\"whoami\",\"id\",\"hostname\",\"uname\") by host Processes.user Processes.parent_process_name Processes.parent_process Processes.process_name Processes.process Processes.process_path Processes.process_hash\n| `drop_dm_object_name(Processes)`\n| eval injection_marker=if(match(child_cmdline,\"(?i)(https?://|/dev/tcp/|base64\\\\s+-d|-enc(odedcommand)?\\\\s|IEX\\\\s|Invoke-Expression|nc\\\\s+-e|bash\\\\s+-i|/etc/(passwd|shadow|hosts)|child_process|exec[sS]ync|spawn[sS]ync|getBuiltinModule|constructor\\\\.constructor|;\\\\s*(curl|wget|nc|bash|sh|python|perl)|`[^`]+`|\\\\$\\\\([^)]+\\\\)|chmod\\\\s+\\\\+x\\\\s+/tmp|CMD_\\\\d+_OUTPUT=)\"),1,0)\n| eval benign_dev=if(match(parent_process,\"(?i)(npm\\\\s+(install|run|test|build|ci)|yarn\\\\s+(install|run|test|build)|pnpm\\\\s+(install|run|test)|pip\\\\s+install|poetry\\\\s+install|conda\\\\s+install|cargo\\\\s+(build|check|fmt|clippy)|webpack|tsc\\\\s|eslint|prettier|jest)\"),1,0)\n| where injection_marker=1 AND benign_dev=0\n| join type=left host max=0 [\n    | tstats `summariesonly` count as egress_count values(All_Traffic.dest) as egress_dest values(All_Traffic.dest_port) as egress_port from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_category!=\"internal\" AND All_Traffic.app IN (\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"dotnet.exe\",\"dotnet\",\"portainer.exe\",\"portainer\",\"n8n\",\"flowise\",\"deepseek-tui\",\"cargo.exe\",\"cargo\") earliest=-5m@m by All_Traffic.src\n    | rename All_Traffic.src as host\n  ]\n| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`\n| table firstTime, lastTime, host, user, parent_process_name, parent_process, process_name, child_cmdline, child_path, child_hash, egress_dest, egress_port, egress_count\n| sort - firstTime",
      "defender_kql": "let WindowMin = 5m;\nlet RuntimeParents = dynamic([\"node.exe\",\"node\",\"n8n\",\"n8n.exe\",\"flowise\",\"flowise.exe\",\"deepseek-tui\",\"deepseek-tui.exe\",\"deepseek\",\"deepseek.exe\",\"python.exe\",\"python\",\"python3\",\"python3.exe\",\"pythonw.exe\",\"dotnet.exe\",\"dotnet\",\"portainer\",\"portainer.exe\",\"cargo.exe\",\"cargo\",\"ts-node\",\"tsx\",\"nodemon\"]);\nlet ShellOrLolbin = dynamic([\"sh\",\"bash\",\"zsh\",\"dash\",\"ksh\",\"fish\",\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"cscript.exe\",\"wscript.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"nc\",\"ncat\",\"ncat.exe\",\"socat\",\"certutil.exe\",\"bitsadmin.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"whoami.exe\",\"whoami\",\"id\",\"hostname\",\"uname\"]);\nlet BenignDevPatterns = @\"(?i)(npm\\s+(install|run|test|build|ci)|yarn\\s+(install|run|test|build)|pnpm\\s+(install|run|test)|pip\\s+install|poetry\\s+install|conda\\s+install|cargo\\s+(build|check|fmt|clippy)|webpack|tsc\\s|eslint|prettier|jest)\";\nlet InjectionMarker = @\"(?i)(https?://|/dev/tcp/|base64\\s+-d|-enc(odedcommand)?\\s|IEX\\s|Invoke-Expression|nc\\s+-e|bash\\s+-i|/etc/(passwd|shadow|hosts)|child_process|exec[sS]ync|spawn[sS]ync|getBuiltinModule|constructor\\.constructor|;\\s*(curl|wget|nc|bash|sh|python|perl)|`[^`]+`|\\$\\([^)]+\\)|chmod\\s+\\+x\\s+/tmp|CMD_\\d+_OUTPUT=)\";\nlet RuntimeShells = DeviceProcessEvents\n    | where Timestamp > ago(1h)\n    | where AccountName !endswith \"$\"\n    | where InitiatingProcessFileName in~ (RuntimeParents)\n    | where FileName in~ (ShellOrLolbin)\n    | where not(InitiatingProcessCommandLine matches regex BenignDevPatterns)\n    | where ProcessCommandLine matches regex InjectionMarker\n    | project ShellTime = Timestamp, DeviceId, DeviceName, AccountName,\n              ParentImage = InitiatingProcessFileName,\n              ParentCmd   = InitiatingProcessCommandLine,\n              ChildImage  = FileName,\n              ChildCmd    = ProcessCommandLine,\n              ChildHash   = SHA256,\n              ChildPath   = FolderPath;\nlet RuntimeEgress = DeviceNetworkEvents\n    | where Timestamp > ago(2h)\n    | where InitiatingProcessFileName in~ (RuntimeParents)\n    | where RemoteIPType == \"Public\"\n    | where ActionType in (\"ConnectionSuccess\", \"HttpConnectionInspected\")\n    | project EgressTime = Timestamp, DeviceId,\n              EgressParent = InitiatingProcessFileName,\n              RemoteIP, RemotePort, RemoteUrl;\nRuntimeShells\n| join kind=inner RuntimeEgress on DeviceId\n| where EgressTime between (ShellTime - WindowMin .. ShellTime + WindowMin)\n| summarize EgressIPs = make_set(RemoteIP, 25),\n            EgressUrls = make_set(RemoteUrl, 25),\n            FirstEgress = min(EgressTime),\n            LastEgress  = max(EgressTime)\n            by ShellTime, DeviceName, AccountName, ParentImage, ParentCmd, ChildImage, ChildCmd, ChildHash, ChildPath\n| order by ShellTime desc"
    },
    {
      "id": "UC_WEEKLY_NON_BROWSER_PROCESS_READS_BROWSER_CREDENTIAL_COOKI",
      "title": "[WEEKLY] Non-Browser Process Reads Browser Credential / Cookie SQLite Then Egresses to Public Destination Within 10 Minutes",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Detects the joint actions-on-objectives pattern shared across this fortnight's 12 intrusion reports: a process that is NOT a known web-browser binary opens one of the Chromium / Edge / Gecko credential or cookie stores (Login Data, Cookies, Web Data, Local State, logins.json, key4.db) and, within 10 minutes, the same PID initiates an outbound TCP/HTTP/HTTPS connection to a public IP. This fortnight the pattern is driven by the fake-OpenClaw infostealer that hunts 250+ browser/wallet extensions, the DEEP#DOOR Python backdoor exfiltrating browser & cloud creds via bore.pub tunneling, the TeamPCP / Mini Shai-Hulud supply-chain compromises of @cap-js SAP packages and PyTorch-Lightning v2.6.2/2.6.3 that drop Bun runtimes to grab tokens, CloudZ RAT + Pheno staging at C:\\ProgramData\\Microsoft\\whealth\\ to siphon Phone Link OTPs, MuddyWater's Stagecomp/Darkcomp Teams-delivered loaders, UAT-8302's wagent.exe collector against government browsers, the event-invitation phishing kits, and PamDOORa's post-PAM-hijack browser fan-out on Linux. Kill-chain stages spanned: Collection (T1555.003 / T1539 / T1005) \u2192 Exfiltration (T1041 / T1567 / T1071.001). It WILL fire on stealers, RAT collectors and supply-chain payloads that read those files from python.exe / node.exe / bun.exe / pwsh.exe / custom .exes; it will NOT fire on the browser process itself, on baselined EDR / AV / backup readers (allowlisted), on credential reads with no egress (those surface as a hunting drop-rule), or on egress without a prior credential read.\n\nRationale: All 12 articles describe different delivery (Bun supply-chain in SAP & PyTorch-Lightning, fake OpenClaw installer, MuddyWater Teams social-engineering, ClickFix, Phone Link plugin, Linux PAM hijack, event-invitation phishing, fake Android apps, government-targeted UAT-8302 custom loader) and persistence (Scheduled Task, Run keys, PAM module, WMI subscription) but converge on the same impact behaviour: a NON-browser process opening Chromium/Edge/Gecko credential SQLite files and exfiltrating shortly after. Correlating same-PID FileEvent \u2192 NetworkEvent inside 10 minutes catches the joint TTP regardless of which loader/family delivered the stealer this week.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1555.003",
          "name": "Credentials from Password Stores: Credentials from Web Browsers"
        },
        {
          "id": "T1539",
          "name": "Steal Web Session Cookie"
        },
        {
          "id": "T1005",
          "name": "Data from Local System"
        },
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        },
        {
          "id": "T1567",
          "name": "Exfiltration Over Web Service"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        }
      ],
      "data_models": [
        "Endpoint.Filesystem",
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats summariesonly=t count min(_time) as firstFile values(Filesystem.file_path) as cred_files from datamodel=Endpoint.Filesystem where (Filesystem.file_name IN (\"Login Data\",\"Login Data For Account\",\"Cookies\",\"Web Data\",\"Local State\",\"logins.json\",\"key3.db\",\"key4.db\",\"cert8.db\",\"cert9.db\",\"signons.sqlite\") OR Filesystem.file_path IN (\"*\\\\Google\\\\Chrome\\\\User Data\\\\*\\\\Login Data*\",\"*\\\\Google\\\\Chrome\\\\User Data\\\\*\\\\Cookies*\",\"*\\\\Google\\\\Chrome\\\\User Data\\\\*\\\\Web Data*\",\"*\\\\Microsoft\\\\Edge\\\\User Data\\\\*\\\\Login Data*\",\"*\\\\Microsoft\\\\Edge\\\\User Data\\\\*\\\\Cookies*\",\"*\\\\BraveSoftware\\\\Brave-Browser\\\\User Data\\\\*\\\\Login Data*\",\"*\\\\Mozilla\\\\Firefox\\\\Profiles\\\\*\\\\logins.json\",\"*\\\\Mozilla\\\\Firefox\\\\Profiles\\\\*\\\\key4.db\",\"/home/*/.config/google-chrome/*/Login Data\",\"/home/*/.config/google-chrome/*/Cookies\",\"/home/*/.mozilla/firefox/*/logins.json\",\"/home/*/.mozilla/firefox/*/key4.db\",\"/Users/*/Library/Application Support/Google/Chrome/*/Login Data\",\"/Users/*/Library/Application Support/Firefox/Profiles/*/logins.json\")) AND NOT Filesystem.process_name IN (\"chrome.exe\",\"msedge.exe\",\"firefox.exe\",\"brave.exe\",\"opera.exe\",\"vivaldi.exe\",\"arc.exe\",\"msedgewebview2.exe\",\"iexplore.exe\",\"chrome\",\"firefox\",\"MsMpEng.exe\",\"MpCmdRun.exe\",\"NisSrv.exe\",\"TrustedInstaller.exe\",\"SearchProtocolHost.exe\",\"SenseIR.exe\",\"MsSense.exe\",\"SenseNdr.exe\",\"CSFalconService.exe\",\"CarbonBlack.exe\",\"explorer.exe\") AND NOT match(Filesystem.user, \"(?i).*\\\\$$\") by host Filesystem.process_guid Filesystem.process_name Filesystem.process_path Filesystem.user Filesystem.file_path Filesystem.file_name | `drop_dm_object_name(Filesystem)` | rename process_guid as src_guid | join type=inner host [ | tstats summariesonly=t count as net_count values(All_Traffic.dest_ip) as dst_ips values(All_Traffic.dest) as dst values(All_Traffic.dest_port) as ports min(_time) as firstNet from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_category!=\"internal\" AND All_Traffic.action!=\"blocked\" by host | `drop_dm_object_name(All_Traffic)` ] | eval delaySec = firstNet - firstFile | where delaySec >= 0 AND delaySec <= 600 | table host user process_name process_path file_path file_name cred_files dst_ips dst ports firstFile firstNet delaySec | `security_content_ctime(firstFile)` | `security_content_ctime(firstNet)`",
      "defender_kql": "let LookbackHours = 24h;\nlet WindowMin = 10m;\nlet BrowserBins = dynamic([\"chrome.exe\",\"msedge.exe\",\"firefox.exe\",\"brave.exe\",\"opera.exe\",\"vivaldi.exe\",\"arc.exe\",\"msedgewebview2.exe\",\"iexplore.exe\"]);\nlet SystemBins = dynamic([\"MsMpEng.exe\",\"MpCmdRun.exe\",\"NisSrv.exe\",\"TrustedInstaller.exe\",\"SearchProtocolHost.exe\",\"SenseIR.exe\",\"MsSense.exe\",\"SenseNdr.exe\",\"CSFalconService.exe\",\"explorer.exe\"]);\nlet CredFileNames = dynamic([\"Login Data\",\"Login Data For Account\",\"Cookies\",\"Web Data\",\"Local State\",\"logins.json\",\"key3.db\",\"key4.db\",\"cert8.db\",\"cert9.db\",\"signons.sqlite\"]);\nlet CredFolderTokens = dynamic([@\"\\Google\\Chrome\\User Data\\\",@\"\\Microsoft\\Edge\\User Data\\\",@\"\\Mozilla\\Firefox\\Profiles\\\",@\"\\BraveSoftware\\Brave-Browser\\User Data\\\",@\"\\Opera Software\\Opera Stable\\\",@\"\\Vivaldi\\User Data\\\",@\"/.config/google-chrome/\",@\"/.config/chromium/\",@\"/.mozilla/firefox/\"]);\nlet CredReads = DeviceFileEvents\n    | where Timestamp > ago(LookbackHours)\n    | where ActionType in (\"FileCreated\",\"FileModified\",\"FileRenamed\")\n        or isnotempty(InitiatingProcessId)\n    | where FileName in~ (CredFileNames) or FolderPath has_any (CredFolderTokens)\n    | where InitiatingProcessFileName !in~ (BrowserBins)\n    | where InitiatingProcessFileName !in~ (SystemBins)\n    | where isnotempty(InitiatingProcessAccountName)\n    | where InitiatingProcessAccountName !endswith \"$\"\n    | project FileTime = Timestamp, DeviceId, DeviceName,\n              InitiatingProcessId, InitiatingProcessFileName, InitiatingProcessFolderPath,\n              InitiatingProcessSHA256, InitiatingProcessCommandLine, InitiatingProcessAccountName,\n              CredFile = FolderPath, CredName = FileName;\nlet NetEgress = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackHours)\n    | where RemoteIPType == \"Public\"\n    | where ActionType in (\"ConnectionSuccess\",\"ConnectionAttempt\",\"HttpConnectionInspected\")\n    | project NetTime = Timestamp, DeviceId, InitiatingProcessId,\n              InitiatingProcessFileName, RemoteIP, RemoteUrl, RemotePort;\nCredReads\n| join kind=inner NetEgress on DeviceId, InitiatingProcessId, InitiatingProcessFileName\n| where NetTime between (FileTime .. FileTime + WindowMin)\n| summarize FirstFile = min(FileTime),\n            FirstNet  = min(NetTime),\n            DelaySec  = min(datetime_diff('second', NetTime, FileTime)),\n            CredFiles = make_set(strcat(CredFile, CredName), 25),\n            RemoteIPs = make_set(RemoteIP, 25),\n            RemoteUrls = make_set(RemoteUrl, 25),\n            RemotePorts = make_set(RemotePort, 25),\n            EgressCount = count()\n            by DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName,\n               InitiatingProcessFolderPath, InitiatingProcessSHA256, InitiatingProcessCommandLine\n| where DelaySec >= 0 and DelaySec <= 600\n| order by FirstFile desc"
    },
    {
      "id": "UC_WEEKLY_NPM_INSTALL_SPAWNED_PROCESS_PERFORMING_CRED_FILE_F",
      "title": "[WEEKLY] npm-install spawned process performing cred-file fan-out plus IMDS reach",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Detects the post-execution credential-harvest stage of an npm supply-chain compromise: a process spawned under an npm/node/yarn/pnpm or node-gyp (python) parent that, within 10 minutes on the same host, reads two or more distinct cloud/dev credential file paths (.aws/credentials, .ssh/id_rsa, .npmrc, .docker/config.json, ~/.config/gcloud, .kube/config, .azure/accessTokens.json) or contacts cloud instance metadata (169.254.169.254 / metadata.google.internal / metadata.azure.com). This fortnight the @redhat-cloud-services 'Miasma' campaign (Snyk 2026-06-01, StepSecurity 2026-06-02, Microsoft 2026-06-03, Unit 42 2026-06-02) and the self-propagating binding.gyp worm (Snyk + StepSecurity 2026-06-04, Aikido 2026-06-09) drop the same multi-cloud secret harvester targeting AWS/GCP/Azure/GitHub Actions/npm/Vault/SSH/wallet secrets; the npm v12 install-scripts disablement announcement (The Hacker News + Aikido 2026-06-11) and a fresh Web3/cloud wave (Cyber Security News 2026-06-12) confirm the harvest behaviour persists across hook variants (preinstall, postinstall, binding.gyp), making the fan-out the durable signal rather than any single lifecycle hook or IOC. Spans install \u2192 credential_access in the kill chain. Fires on developer workstations and CI/CD runners exhibiting the spawn-plus-fan-out pair; does NOT fire on plain `npm install` compiling native modules without touching cred paths, nor on a single legitimate aws-sdk IMDS lookup from a known package's runtime.\n\nRationale: Across all 10 articles the post-spawn behavior \u2014 a single npm/node/gyp-spawned process touching multiple cloud/dev credential paths and/or IMDS \u2014 is the invariant; the entry vector rotates between preinstall hooks (Miasma), postinstall hooks (npm v12 announcement), and binding.gyp (worm) but the harvest fan-out persists. Requiring \u22652 distinct cred paths within 10 minutes of an npm-rooted spawn eliminates the single-cred SDK use-case while catching every campaign variant the fortnight surfaced.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1552.005",
          "name": "Unsecured Credentials: Cloud Instance Metadata API"
        },
        {
          "id": "T1528",
          "name": "Steal Application Access Token"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats summariesonly=true min(_time) as install_time values(Processes.process) as install_cmd values(Processes.parent_process_name) as install_parent from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"npm\",\"npm.exe\",\"npm-cli.js\",\"node\",\"node.exe\",\"yarn\",\"yarn.exe\",\"pnpm\",\"pnpm.exe\",\"python\",\"python.exe\",\"python3\",\"python3.exe\") AND Processes.process_name IN (\"node\",\"node.exe\",\"python\",\"python.exe\",\"python3\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"sh\",\"bash\") by Processes.dest, Processes.user | `drop_dm_object_name(Processes)` | join type=inner dest [| tstats summariesonly=true min(_time) as harvest_time dc(Filesystem.file_path) as cred_paths_touched values(Filesystem.file_path) as cred_paths values(Filesystem.process_name) as harvest_proc from datamodel=Endpoint.Filesystem where Filesystem.action=\"read\" AND Filesystem.process_name IN (\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh\",\"bash\",\"curl.exe\",\"wget.exe\") AND (Filesystem.file_path=\"*\\\\.aws\\\\credentials*\" OR Filesystem.file_path=\"*/.aws/credentials*\" OR Filesystem.file_path=\"*\\\\.aws\\\\config*\" OR Filesystem.file_path=\"*\\\\.ssh\\\\id_rsa*\" OR Filesystem.file_path=\"*/.ssh/id_rsa*\" OR Filesystem.file_path=\"*\\\\.ssh\\\\id_ed25519*\" OR Filesystem.file_path=\"*/.ssh/id_ed25519*\" OR Filesystem.file_path=\"*\\\\.npmrc*\" OR Filesystem.file_path=\"*/.npmrc*\" OR Filesystem.file_path=\"*\\\\.docker\\\\config.json*\" OR Filesystem.file_path=\"*/.docker/config.json*\" OR Filesystem.file_path=\"*/.config/gcloud/*\" OR Filesystem.file_path=\"*/.kube/config*\" OR Filesystem.file_path=\"*/.azure/accessTokens.json*\" OR Filesystem.file_name IN (\".env\",\".envrc\",\"github_token\",\"credentials\",\"id_rsa\",\"id_ed25519\")) by Filesystem.dest | `drop_dm_object_name(Filesystem)` | where cred_paths_touched >= 2] | eval delay_sec = harvest_time - install_time | where delay_sec >= 0 AND delay_sec <= 600 | table install_time, harvest_time, delay_sec, dest, user, install_parent, install_cmd, harvest_proc, cred_paths_touched, cred_paths | sort - install_time",
      "defender_kql": "let LookbackDays = 14d;\nlet CorrelationMin = 10m;\nlet CredFolderTokens = dynamic([@\".aws\\credentials\",@\"/.aws/credentials\",@\".ssh\\id_rsa\",@\"/.ssh/id_rsa\",@\".ssh\\id_ed25519\",@\"/.ssh/id_ed25519\",@\".npmrc\",@\".docker\\config.json\",@\"/.docker/config.json\",@\"/.config/gcloud\",@\"/.kube/config\",@\"/.azure/accessTokens.json\"]);\nlet InstallSpawn = DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where InitiatingProcessFileName in~ (\"npm.exe\",\"node.exe\",\"npm-cli.js\",\"yarn.exe\",\"pnpm.exe\",\"python.exe\",\"python3.exe\",\"npm\",\"node\",\"yarn\",\"pnpm\",\"python\",\"python3\")\n    | where FileName in~ (\"node.exe\",\"python.exe\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"sh.exe\",\"bash.exe\",\"node\",\"python\",\"python3\")\n    | where InitiatingProcessCommandLine has_any (\"install\",\" ci\",\"rebuild\",\"npx\") or ProcessCommandLine has_any (\"binding.gyp\",\"node-gyp\",\"preinstall\",\"postinstall\",\"index.js\",\"node_modules\")\n    | where AccountName !endswith \"$\"\n    | project InstallTime = Timestamp, DeviceId, DeviceName, AccountName, InstallProc = FileName, InstallCmd = ProcessCommandLine, InstallParent = InitiatingProcessFileName, InstallParentCmd = InitiatingProcessCommandLine;\nlet CredFanOut = DeviceFileEvents\n    | where Timestamp > ago(LookbackDays)\n    | where ActionType in (\"FileCreated\",\"FileModified\",\"FileRenamed\")\n         or InitiatingProcessFileName in~ (\"node.exe\",\"python.exe\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh.exe\",\"bash.exe\")\n    | where InitiatingProcessFileName in~ (\"node.exe\",\"python.exe\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh.exe\",\"bash.exe\",\"curl.exe\",\"wget.exe\",\"node\",\"python\",\"python3\")\n    | where FolderPath has_any (CredFolderTokens)\n         or FileName in~ (\".npmrc\",\"credentials\",\"id_rsa\",\"id_ed25519\",\".env\",\".envrc\",\"accessTokens.json\",\"github_token\")\n    | summarize FirstHarvest = min(Timestamp), CredPathsTouched = dcount(FolderPath), CredFiles = make_set(FolderPath, 12) by DeviceId, AccountName, bin(Timestamp, CorrelationMin)\n    | where CredPathsTouched >= 2;\nlet IMDSReach = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackDays)\n    | where RemoteIP in (\"169.254.169.254\",\"fd00:ec2::254\") or RemoteUrl has_any (\"metadata.google.internal\",\"metadata.azure.com\",\"169.254.169.254\")\n    | where InitiatingProcessFileName in~ (\"node.exe\",\"python.exe\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh.exe\",\"bash.exe\",\"curl.exe\",\"wget.exe\",\"node\",\"python\",\"python3\")\n    | project IMDSTime = Timestamp, DeviceId, IMDSProc = InitiatingProcessFileName, IMDSCmd = InitiatingProcessCommandLine, RemoteIP, RemoteUrl;\nlet HitsFromFanOut = InstallSpawn\n    | join kind=inner CredFanOut on DeviceId\n    | where FirstHarvest between (InstallTime .. InstallTime + CorrelationMin)\n    | project Trigger = \"cred-file-fan-out\", InstallTime, EvidenceTime = FirstHarvest, DelaySec = datetime_diff('second', FirstHarvest, InstallTime), DeviceName, AccountName, InstallParent, InstallParentCmd, InstallProc, InstallCmd, CredPathsTouched, CredFiles, RemoteIP = \"\", RemoteUrl = \"\";\nlet HitsFromIMDS = InstallSpawn\n    | join kind=inner IMDSReach on DeviceId\n    | where IMDSTime between (InstallTime .. InstallTime + CorrelationMin)\n    | project Trigger = \"imds-reach\", InstallTime, EvidenceTime = IMDSTime, DelaySec = datetime_diff('second', IMDSTime, InstallTime), DeviceName, AccountName, InstallParent, InstallParentCmd, InstallProc, InstallCmd, CredPathsTouched = 0, CredFiles = dynamic([]), RemoteIP, RemoteUrl;\nHitsFromFanOut\n| union HitsFromIMDS\n| order by InstallTime desc"
    },
    {
      "id": "UC_WEEKLY_NPM_INSTALL_TIME_LIFECYCLE_HOOK_TRIGGERS_OUTBOUND_",
      "title": "[WEEKLY] npm Install-Time Lifecycle Hook Triggers Outbound Egress to Newly-Seen Domain (Shai-Hulud/Miasma/IronWorm pattern)",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the joint pattern across the fortnight's npm supply-chain wave \u2014 Miasma/@redhat-cloud-services, IronWorm, codexui-android, vpmdhaj typosquats, and the binding.gyp self-propagating worm \u2014 where an npm/yarn/pnpm install spawns a node (or node-gyp/python build helper) child process that within ~5 minutes egresses to a domain or IP never seen before in the org. The chain spans delivery (malicious package landing on a dev/CI host) and C2/exfil (preinstall/binding.gyp payload beaconing harvested AWS, GCP, Azure, GitHub, npm, and CircleCI secrets). Kill-chain stages covered: install (lifecycle-hook execution) \u2192 c2/actions (credential exfil to attacker infrastructure). It will fire when a developer or CI runner installs a poisoned package and the payload reaches out to fresh attacker infra; it will NOT fire on normal npm installs that only contact already-baselined registries/CDNs (registry.npmjs.org, github.com, well-known mirrors), nor on lifecycle scripts that stay local. IOC slots (`<c2_domain_list>`, `<c2_ip_list>`, `<sha256_list>`) are referenced as substitution points so the rule survives weekly IOC rotation.\n\nRationale: Across all 11 articles the invariant is identical: an npm-family parent (or node-gyp triggered by binding.gyp) spawns a node/python/shell child during install that egresses to attacker infrastructure within minutes \u2014 exfiltrating GitHub/AWS/GCP/Azure/npm/Codex/Anthropic tokens. Correlating install-time lifecycle execution with a previously-unseen external destination in a 5-minute window catches Miasma, IronWorm, codexui-android, vpmdhaj typosquats, and the binding.gyp worm without depending on any single campaign's rotating hashes or domains.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1546.016",
          "name": "Event Triggered Execution: Installer Packages"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1552.005",
          "name": "Unsecured Credentials: Cloud Instance Metadata API"
        },
        {
          "id": "T1567",
          "name": "Exfiltration Over Web Service"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Resolution.DNS",
        "Network_Traffic.All_Traffic",
        "Web"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as install_time max(_time) as install_last values(Processes.process) as install_cmd values(Processes.process_path) as install_image values(Processes.parent_process_name) as install_parent from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"npm\",\"npm.exe\",\"npm-cli.js\",\"node\",\"node.exe\",\"yarn\",\"yarn.exe\",\"pnpm\",\"pnpm.exe\",\"npx\",\"npx.exe\") OR Processes.process_name IN (\"node-gyp\",\"node-gyp.exe\")) AND Processes.process_name IN (\"node\",\"node.exe\",\"python\",\"python.exe\",\"python3\",\"sh\",\"bash\",\"powershell.exe\",\"pwsh.exe\",\"curl.exe\",\"wget.exe\") by host Processes.user Processes.process_name Processes.parent_process_name | `drop_dm_object_name(\"Processes\")` | rename host as src | join type=inner src [| tstats `summariesonly` count min(_time) as egress_time values(All_Traffic.dest) as c2_dest values(All_Traffic.dest_ip) as c2_ip values(All_Traffic.dest_port) as c2_port from datamodel=Network_Traffic.All_Traffic where All_Traffic.action=\"allowed\" AND All_Traffic.dest_category!=\"internal\" by All_Traffic.src All_Traffic.dest | `drop_dm_object_name(\"All_Traffic\")` | rename src as src] | where egress_time>=install_time AND egress_time<=install_time+300 | eval delay_sec=egress_time-install_time | search NOT [| inputlookup npm_install_egress_baseline_30d.csv | fields dest] | search [| inputlookup known_npm_c2_iocs.csv | fields dest] OR c2_dest IN (\"<c2_domain_list>\") OR c2_ip IN (\"<c2_ip_list>\") OR NOT [| inputlookup npm_install_egress_baseline_30d.csv | fields dest] | table install_time, egress_time, delay_sec, src, user, install_parent, process_name, install_cmd, c2_dest, c2_ip, c2_port | sort 0 install_time",
      "defender_kql": "let LookbackDays = 7d;\nlet CorrelationWindow = 5m;\nlet BaselineWindow = 30d;\nlet KnownC2Domains = dynamic([\"aab.sportsontheweb.net\",\"oob.moika.tech\",\"moika.tech\",\"cloudplatform-single-spa.io\",\"t-in-one.io\",\"anyclaw.store\",\"sentry.anyclaw.store\",\"audit.checkmarx.cx\"]);\nlet KnownC2IPs = dynamic([\"94.154.172.43\"]);\nlet KnownBadHashes = dynamic([\"7069e28a5806db4ab0273639667d203f5e31b401d403af7e36d9f360c1f6d655\",\"b86c5ae9e95bd841a595440faa3eb6317441e746f241ae8fd641ab59ed1d1966\",\"ef641e956f91d501b748085996303c96a64d67f63bfeef0dda175e5aa19cca90\",\"5926b86b642e00672252953eb30d8f75cfb7797fe3118bd6fa2cfbee92905d61\",\"ceff7c51d70832c3ec8dd2744b606a23b3c924ef664ae23439b9b742ea154108\",\"da39146ef451d1b174a24d00b1e2a45cd38d54e849737f8f35333dcb22175707\",\"e3dbe63aded45278f49c4746ab938ed9472b36def79b43e2dd2d7eff014481d1\",\"82d83274680df928fdda296a348e01802f595e412308c399565c320df444052a\",\"288f26c2eadcb1a7923fe376d16f5404216cce15d9fc162a4a78574dc7df399a\"]);\nlet OrgDomainBaseline = DeviceNetworkEvents\n    | where Timestamp between (ago(BaselineWindow) .. ago(1h))\n    | where isnotempty(RemoteUrl)\n    | summarize BaselineHosts = dcount(DeviceName) by RemoteUrl\n    | where BaselineHosts > 2;\nlet InstallHookExec = DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where InitiatingProcessFileName in~ (\"npm.exe\",\"npm\",\"npm-cli.js\",\"node.exe\",\"node\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"npx.exe\",\"npx\",\"node-gyp\",\"node-gyp.exe\")\n    | where FileName in~ (\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"sh\",\"bash\",\"powershell.exe\",\"pwsh.exe\",\"curl.exe\",\"wget.exe\",\"cmd.exe\")\n    | where InitiatingProcessCommandLine has_any (\"install\",\"i \",\"ci\",\"add\",\"rebuild\",\"prepare\",\"preinstall\",\"postinstall\",\"binding.gyp\",\"node_modules\")\n        or ProcessCommandLine has_any (\"node_modules\",\"binding.gyp\",\"preinstall\",\"postinstall\",\"index.js\")\n    | where AccountName !endswith \"$\"\n    | project InstallTime = Timestamp, DeviceName, AccountName,\n              InstallParent = InitiatingProcessFileName,\n              InstallParentCmd = InitiatingProcessCommandLine,\n              ChildImage = FileName, ChildCmd = ProcessCommandLine, ChildSHA256 = SHA256;\nlet EgressEvents = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackDays)\n    | where RemoteIPType == \"Public\"\n    | where InitiatingProcessFileName in~ (\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"curl.exe\",\"wget.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh\",\"bash\",\"npm.exe\",\"yarn.exe\",\"pnpm.exe\")\n    | project EgressTime = Timestamp, DeviceName, RemoteUrl, RemoteIP, RemotePort,\n              EgressProcess = InitiatingProcessFileName, EgressCmd = InitiatingProcessCommandLine;\nInstallHookExec\n| join kind=inner EgressEvents on DeviceName\n| where EgressTime between (InstallTime .. InstallTime + CorrelationWindow)\n| extend DelaySec = datetime_diff('second', EgressTime, InstallTime)\n| join kind=leftanti OrgDomainBaseline on RemoteUrl\n| extend MatchKnownC2Domain = iff(RemoteUrl in~ (KnownC2Domains) or RemoteUrl has_any (KnownC2Domains), true, false)\n| extend MatchKnownC2IP = iff(RemoteIP in (KnownC2IPs), true, false)\n| extend MatchKnownHash = iff(ChildSHA256 in~ (KnownBadHashes), true, false)\n| project InstallTime, EgressTime, DelaySec, DeviceName, AccountName, InstallParent,\n          InstallParentCmd, ChildImage, ChildCmd, ChildSHA256, RemoteUrl, RemoteIP, RemotePort,\n          MatchKnownC2Domain, MatchKnownC2IP, MatchKnownHash\n| order by InstallTime desc"
    },
    {
      "id": "UC_WEEKLY_NPM_YARN_PNPM_INSTALL_HOOK_SPAWN_CREDENTIAL_STORE_",
      "title": "[WEEKLY] npm/yarn/pnpm Install-Hook Spawn \u2192 Credential-Store Read or Worm-Payload Drop in node_modules",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the canonical Mini Shai-Hulud / npm-supply-chain tradecraft: a package manager (npm, yarn, pnpm, bun, node) spawns a script interpreter (PowerShell, cmd, bash, sh, bun, node) during the preinstall/postinstall lifecycle, and within a short window that descendant process either (a) drops one of the worm-family payload filenames under node_modules (router_init.js, router_runtime.js, tanstack_runner.js, vite_setup.mjs, bw_setup.js, bw1.js, node-ipc.cjs) or (b) reads developer credential material (.ssh keys, ~/.aws, .npmrc, .gitconfig, browser Login Data/Cookies, .docker/config.json, .kube/config, .env). This fortnight is dominated by this pattern: @bitwarden/cli hijack (StepSecurity 2026-05-04), CanisterSprawl pgserve (StepSecurity 2026-05-04), intercom-client GHSA-54pg-9963-v8vg (2026-05-07), TanStack/Mistral Mini Shai-Hulud Wave 4 (Snyk 2026-05-11, Aikido 2026-05-12) across 160+ packages, and the node-ipc 9.1.6/9.2.3/12.0.1 maintainer-account compromise (StepSecurity/Snyk/BleepingComputer 2026-05-15) \u2014 every one of them executes through install hooks rather than IOC re-use. Kill-chain stages: install (lifecycle hook execution) \u2192 credential-access (file reads) and/or persistence (worm payload landing in node_modules to propagate). Fires when the parent-package-manager \u2192 interpreter-spawn \u2192 credential-or-payload-touch chain completes inside the window; does NOT fire on clean `npm ci` runs that don't read credential stores, nor on developer-initiated `node` REPL sessions, because the credential-touch / known-payload-name predicate is required on the second event.\n\nRationale: Every article in scope shares one TTP regardless of the IOC rotation: an npm-lifecycle hook spawns an interpreter that either drops the worm payload under node_modules or reads developer credential files. The two-event temporal correlation (package-manager parent \u2192 interpreter child \u2192 credential-store touch or known payload filename, within 5 minutes) catches Bitwarden CLI's Bun-stager (bw_setup.js/bw1.js), pgserve's preinstall harvester, intercom-client's Composer-plugin dropper, the TanStack/Mistral Wave-4 worm (router_init/tanstack_runner), and node-ipc's CJS-bundle stealer in a single rule \u2014 no IOC-list dependency.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1546.016",
          "name": "Event Triggered Execution: Installer Packages"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1552.004",
          "name": "Unsecured Credentials: Private Keys"
        },
        {
          "id": "T1555.003",
          "name": "Credentials from Password Stores: Credentials from Web Browsers"
        },
        {
          "id": "T1027",
          "name": "Obfuscated Files or Information"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats summariesonly=true count min(_time) as spawn_time from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"npm.exe\",\"npm-cli.js\",\"yarn.exe\",\"pnpm.exe\",\"bun.exe\",\"node.exe\",\"npm\",\"yarn\",\"pnpm\",\"bun\",\"node\") OR Processes.parent_process IN (\"*npm*install*\",\"*yarn*install*\",\"*pnpm*install*\",\"*preinstall*\",\"*postinstall*\",\"*node_modules*\")) AND Processes.process_name IN (\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"bash.exe\",\"sh.exe\",\"wsl.exe\",\"bun.exe\",\"node.exe\") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name Processes.process Processes.process_id | `drop_dm_object_name(Processes)` | rename process_id as spawn_pid | join type=inner dest [| tstats summariesonly=true count min(_time) as evt_time values(Filesystem.file_path) as file_paths values(Filesystem.file_name) as file_names from datamodel=Endpoint.Filesystem where (Filesystem.file_path IN (\"*\\\\.ssh\\\\*\",\"*\\\\.aws\\\\credentials*\",\"*\\\\.aws\\\\config*\",\"*\\\\.npmrc*\",\"*\\\\.gitconfig*\",\"*\\\\Login Data*\",\"*\\\\Cookies*\",\"*\\\\logins.json*\",\"*\\\\.docker\\\\config.json*\",\"*\\\\.kube\\\\config*\",\"*\\\\.env*\",\"*GITHUB_TOKEN*\") OR (Filesystem.file_name IN (\"router_init.js\",\"router_runtime.js\",\"tanstack_runner.js\",\"vite_setup.mjs\",\"bw_setup.js\",\"bw1.js\",\"node-ipc.cjs\") AND Filesystem.file_path=\"*node_modules*\")) by Filesystem.dest Filesystem.file_path Filesystem.file_name Filesystem.process_id | `drop_dm_object_name(Filesystem)`] | eval delay_sec = (evt_time - spawn_time) | where delay_sec >= 0 AND delay_sec <= 300 | table spawn_time evt_time delay_sec dest user process_name parent_process_name process file_paths file_names | sort - spawn_time",
      "defender_kql": "let LookbackHours = 24h;\nlet WindowSec = 300;\nlet _pkg_mgrs = dynamic([\"npm.exe\",\"npm-cli.js\",\"yarn.exe\",\"pnpm.exe\",\"bun.exe\",\"node.exe\"]);\nlet _shell_children = dynamic([\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"bash.exe\",\"sh.exe\",\"wsl.exe\",\"bun.exe\",\"node.exe\"]);\nlet _worm_names = dynamic([\"router_init.js\",\"router_runtime.js\",\"tanstack_runner.js\",\"vite_setup.mjs\",\"bw_setup.js\",\"bw1.js\",\"node-ipc.cjs\"]);\nlet _cred_path_fragments = dynamic([@\"\\.ssh\\\", @\"\\.aws\\\", @\"\\.npmrc\", @\"\\.gitconfig\", @\"\\Login Data\", @\"\\Cookies\", @\"\\logins.json\", @\"\\.docker\\config.json\", @\"\\.kube\\config\", @\"\\.env\"]);\nlet Spawns = DeviceProcessEvents\n    | where Timestamp > ago(LookbackHours)\n    | where InitiatingProcessFileName in~ (_pkg_mgrs)\n        or InitiatingProcessParentFileName in~ (_pkg_mgrs)\n        or InitiatingProcessCommandLine has_any (\"preinstall\",\"postinstall\",\"node_modules\",\"npm install\",\"yarn install\",\"pnpm install\")\n    | where FileName in~ (_shell_children)\n    | where AccountName !endswith \"$\"\n    | project SpawnTime = Timestamp, DeviceId, DeviceName, AccountName,\n              Parent = InitiatingProcessFileName,\n              ParentCmd = InitiatingProcessCommandLine,\n              Child = FileName, ChildCmd = ProcessCommandLine,\n              ChildPid = ProcessId;\nlet CredOrPayload = DeviceFileEvents\n    | where Timestamp > ago(LookbackHours)\n    | where (FolderPath has_any (_cred_path_fragments))\n         or (FileName in~ (_worm_names) and FolderPath has \"node_modules\")\n    | project EvtTime = Timestamp, DeviceId,\n              EvtPath = FolderPath, EvtFile = FileName,\n              EvtInitiator = InitiatingProcessFileName,\n              EvtInitiatorPid = InitiatingProcessId;\nSpawns\n| join kind=inner CredOrPayload on DeviceId\n| where EvtTime between (SpawnTime .. SpawnTime + WindowSec * 1s)\n| project SpawnTime, EvtTime,\n          DelaySec = datetime_diff('second', EvtTime, SpawnTime),\n          DeviceName, AccountName,\n          Parent, ParentCmd, Child, ChildCmd,\n          EvtPath, EvtFile, EvtInitiator\n| summarize arg_min(SpawnTime, *) by DeviceName, AccountName, Child, EvtFile\n| order by SpawnTime desc"
    },
    {
      "id": "UC_WEEKLY_OAUTH_DEVICE_CODE_CONSENT_PHISH_TO_CROSS_IP_CLOUD_",
      "title": "[WEEKLY] OAuth Device-Code Consent Phish to Cross-IP Cloud Token Replay",
      "kill_chain": "c2",
      "confidence": "High",
      "description": "Detects the EvilTokens / Storm-2949 consent-phishing chain: a successful Entra ID device-code authentication (or an OAuth consent grant to a non-Microsoft first-party app) is followed within 60 minutes by privileged cloud activity from a DIFFERENT source IP / ASN than the one that initiated the device-code request, where 'privileged activity' means mass mailbox/SharePoint reads, inbox-rule creation, app-credential or device-registration manipulation, or token use against Graph from a new ASN. This fortnight the pattern is driven by 'The New Phishing Click: How OAuth Consent Bypasses MFA' (EvilTokens PhaaS, 340+ M365 tenants in 5 weeks), 'How Storm-2949 turned a compromised identity into a cloud-wide breach' (token-theft-led cloud exfil to T1530/T1567.002), and the phishing-exposure pieces from THN and BleepingComputer. The chain spans delivery (phish link / pasted device code), exploit/persistence (T1528 token theft, T1098.001 credential add), and actions (T1550.001 token replay, cloud data theft). It fires when both stages match across IPs; it does NOT fire on legitimate device-code logons from kiosks / Teams Rooms / headless devices where the subsequent token use occurs from the same egress IP, nor on conditional-access-blocked attempts (those never produce a downstream success).\n\nRationale: Every article in scope converges on stolen-token cloud abuse \u2014 EvilTokens (device-code consent PhaaS), Storm-2949 (identity-to-cloud breach), and the THN/Bleeping pieces on phishing-driven session theft all rely on the SAME post-auth tell: a legitimate-looking Entra sign-in (device code / OAuth consent) is replayed from attacker infrastructure to take privileged actions within minutes. Joining the auth event to the downstream privileged action AND requiring a source-IP mismatch within 60 minutes catches the campaign pattern even when IOCs (apps, IPs, code-paste lures) rotate.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1528",
          "name": "Steal Application Access Token"
        },
        {
          "id": "T1550.001",
          "name": "Use Alternate Authentication Material: Application Access Token"
        },
        {
          "id": "T1078.004",
          "name": "Valid Accounts: Cloud Accounts"
        },
        {
          "id": "T1098.001",
          "name": "Account Manipulation: Additional Cloud Credentials"
        },
        {
          "id": "T1098.005",
          "name": "Account Manipulation: Device Registration"
        },
        {
          "id": "T1539",
          "name": "Steal Web Session Cookie"
        },
        {
          "id": "T1566.002",
          "name": "Phishing: Spearphishing Link"
        },
        {
          "id": "T1530",
          "name": "Data from Cloud Storage"
        }
      ],
      "data_models": [
        "Authentication",
        "Change",
        "Web",
        "Email.All_Email"
      ],
      "splunk_spl": "``` Stage 1: device-code or non-Microsoft OAuth consent sign-in success ```\n| tstats `summariesonly` earliest(_time) AS auth_time, values(Authentication.src) AS auth_src_ip, values(Authentication.app) AS auth_app\n    from datamodel=Authentication.Authentication\n    where Authentication.action=\"success\" AND (Authentication.signature_id=\"deviceCode\" OR Authentication.authentication_method=\"deviceCode\" OR Authentication.signature=\"Consent to application\")\n    by Authentication.user, Authentication.dest\n| `drop_dm_object_name(Authentication)`\n| rename user AS upn, dest AS tenant\n``` Stage 2: privileged cloud action from a DIFFERENT src IP within 60 min ```\n| join type=inner upn\n    [| tstats `summariesonly` earliest(_time) AS act_time, values(All_Changes.src) AS act_src_ip, values(All_Changes.action) AS act_name, values(All_Changes.object_id) AS act_obj\n        from datamodel=Change.All_Changes\n        where All_Changes.object_category IN (\"user\",\"mailbox\",\"application\",\"role\",\"device\")\n          AND All_Changes.action IN (\"created\",\"modified\",\"granted\",\"added\")\n          AND All_Changes.result=\"success\"\n        by All_Changes.user\n     | `drop_dm_object_name(All_Changes)`\n     | rename user AS upn]\n| where act_time >= auth_time AND act_time <= auth_time + 3600\n| where auth_src_ip != act_src_ip\n| iplocation auth_src_ip prefix=auth_\n| iplocation act_src_ip prefix=act_\n| where auth_Country != act_Country OR isnull(act_Country)\n| eval delay_min = round((act_time - auth_time)/60, 1)\n| table auth_time, act_time, delay_min, upn, tenant, auth_src_ip, auth_Country, act_src_ip, act_Country, act_name, act_obj, auth_app\n| sort 0 - act_time",
      "defender_kql": "// Stage 1 \u2014 EvilTokens-style: successful device-code or OAuth consent sign-in\nlet WindowMin = 60m;\nlet Lookback = 7d;\nlet ConsentAuth =\n    AADSignInEventsBeta\n    | where Timestamp > ago(Lookback)\n    | where ErrorCode == 0\n    | where AuthenticationProtocol =~ \"deviceCode\"\n         or AuthenticationRequirement =~ \"deviceCode\"\n         or ApplicationId !in~ (\"00000003-0000-0000-c000-000000000000\",\"1fec8e78-bce4-4aaf-ab1b-5451cc387264\")\n            and isnotempty(ResourceDisplayName)\n    | project AuthTime = Timestamp, AccountUpn, AccountObjectId, AuthIp = IPAddress,\n              AuthCountry = Country, AuthApp = ApplicationDisplayName, SessionId,\n              AuthAsn = tostring(NetworkLocationDetails);\n// Stage 2 \u2014 privileged cloud action from a different IP, \u226460 min later\nlet PrivAct =\n    CloudAppEvents\n    | where Timestamp > ago(Lookback)\n    | where ActionType in~ (\n        \"Add service principal credentials.\",\n        \"Add owner to application.\",\n        \"Add app role assignment to service principal.\",\n        \"Consent to application.\",\n        \"New-InboxRule\",\"Set-InboxRule\",\n        \"Add-MailboxPermission\",\n        \"FileDownloaded\",\"FileSyncDownloadedFull\",\n        \"MailItemsAccessed\",\n        \"Update user.\",\"Add member to role.\",\n        \"Register device.\")\n    | project ActTime = Timestamp, AccountUpn = AccountDisplayName, ActIp = IPAddress,\n              ActCountry = CountryCode, ActionType, ObjectName = ObjectId, RawEventData;\nConsentAuth\n| join kind=inner (PrivAct) on AccountUpn\n| where ActTime between (AuthTime .. AuthTime + WindowMin)\n| where tostring(AuthIp) != tostring(ActIp)\n| extend DelayMin = datetime_diff('minute', ActTime, AuthTime)\n| summarize ActionCount = count(),\n            Actions = make_set(ActionType, 25),\n            FirstAct = min(ActTime),\n            LastAct = max(ActTime),\n            DistinctActIps = dcount(ActIp),\n            ActIps = make_set(ActIp, 10),\n            ActCountries = make_set(ActCountry, 10)\n            by AuthTime, AccountUpn, AuthIp, AuthCountry, AuthApp, SessionId\n| where ActionCount >= 1   // any single sensitive action from a new IP within 60m is alert-worthy\n| order by FirstAct desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_INSTALL_LIFECYCLE_HOOK_SPAWNS_INTERPRETER_",
      "title": "[WEEKLY] Package install lifecycle hook spawns interpreter that reads developer credential stores",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the joint pattern across the June 2026 Miasma/Hades/Atomic-Arch/Laravel-Lang supply-chain wave: a package-manager process (npm, yarn, pnpm, composer, pip, makepkg/pacman, bun) directly spawning a generic interpreter (node, python, bash/sh, powershell, curl/wget, perl) whose command line references developer secret stores (~/.aws/credentials, .npmrc, .docker/config, .kube/config, GITHUB_TOKEN, NPM_TOKEN, VAULT_TOKEN, /proc/self/environ, id_rsa) or hands-off staging primitives (base64/atob/eval, outbound curl/wget, setup.pth bootstrap). The fortnight's articles \u2014 Snyk and StepSecurity on @redhat-cloud-services preinstall hooks, StepSecurity on the Laravel-Lang tag-rewrite, THN on Hades PyPI wheels auto-running a Bun stealer, and BleepingComputer/THN/StepSecurity on the 400+ AUR Atomic Arch packages \u2014 all share this exact preinstall/postinstall/post_install/makepkg \u2192 interpreter \u2192 credential-harvest chain regardless of registry. Kill-chain stages covered: delivery (registry pull), execution (interpreter spawn), credential access (secret-file read), and command-and-control (egress staging). Fires when the parent-child binary pair AND a credential-indicator token co-occur in one process record; does NOT fire on routine `npm install` that only runs native gyp builds, on pip wheels that don't shell out, or on package-manager-driven test suites that don't touch credential paths.\n\nRationale: Every article in the batch describes the same primitive \u2014 a package-manager install lifecycle hook directly spawning an interpreter that immediately enumerates developer secret stores and stages exfil \u2014 but each names a different registry (npm/PyPI/Composer/AUR) and different IOCs that will rotate within days. Anchoring on the parent-binary (package manager) \u2192 child-binary (interpreter) chain AND credential-token presence in the child cmdline keeps the detection IOC-free, schema-correct on every EDR we ship to, and catches future variants in the same Miasma/Hades/Atomic-Arch/Laravel-Lang family without re-tuning.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain: Compromise Software Dependencies and Development Tools"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1528",
          "name": "Steal Application Access Token"
        },
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        },
        {
          "id": "T1546",
          "name": "Event Triggered Execution"
        }
      ],
      "data_models": [
        "Endpoint.Processes"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as child_cmdline values(Processes.process_path) as child_image values(Processes.parent_process) as parent_cmdline values(Processes.parent_process_path) as parent_image values(Processes.user) as user from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"npm\",\"npm.exe\",\"npm-cli.js\",\"yarn\",\"yarn.exe\",\"pnpm\",\"pnpm.exe\",\"composer\",\"composer.exe\",\"composer.phar\",\"pip\",\"pip3\",\"pip.exe\",\"makepkg\",\"pacman\",\"yay\",\"paru\",\"bun\",\"bun.exe\",\"poetry\") AND Processes.process_name IN (\"node\",\"node.exe\",\"python\",\"python3\",\"python.exe\",\"bash\",\"sh\",\"zsh\",\"dash\",\"powershell.exe\",\"pwsh\",\"pwsh.exe\",\"cmd.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"perl\",\"ruby\") by host, Processes.user, Processes.parent_process_name, Processes.process_name | `drop_dm_object_name(Processes)` | where match(child_cmdline, \"(?i)(\\.aws/credentials|\\.npmrc|\\.docker/config|\\.kube/config|GITHUB_TOKEN|NPM_TOKEN|AWS_SECRET|AWS_ACCESS_KEY|/proc/self/environ|id_rsa|VAULT_TOKEN|/etc/shadow|CIRCLE_TOKEN|GH_TOKEN|setup\\.pth|atob\\(|base64\\s+-d|eval\\(|curl\\s+-[a-zA-Z]*\\s*https?://|wget\\s+https?://|fetch\\()\") OR match(parent_cmdline, \"(?i)(preinstall|postinstall|run-script\\s+(pre|post)|lifecycle|makepkg|post_install|prepare-package)\") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`",
      "defender_kql": "let LookbackHours = 24h;\nlet PkgManagers = dynamic([\"npm.exe\",\"npm\",\"npm-cli.js\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"composer.exe\",\"composer\",\"composer.phar\",\"pip.exe\",\"pip\",\"pip3\",\"makepkg\",\"pacman\",\"yay\",\"paru\",\"bun.exe\",\"bun\",\"poetry\"]);\nlet Interpreters = dynamic([\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"bash\",\"sh\",\"zsh\",\"dash\",\"powershell.exe\",\"pwsh.exe\",\"pwsh\",\"cmd.exe\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\",\"perl\",\"ruby\"]);\nlet CredIndicators = dynamic([\".aws/credentials\",\".aws/config\",\".npmrc\",\".docker/config\",\".kube/config\",\"GITHUB_TOKEN\",\"NPM_TOKEN\",\"AWS_SECRET\",\"AWS_ACCESS_KEY\",\"/proc/self/environ\",\"id_rsa\",\"VAULT_TOKEN\",\"/etc/shadow\",\"CIRCLE_TOKEN\",\"GH_TOKEN\",\"setup.pth\",\"atob(\",\"base64 -d\",\"eval(\"]);\nlet HookIndicators = dynamic([\"preinstall\",\"postinstall\",\"post_install\",\"run-script\",\"lifecycle\",\"makepkg\",\"prepare-package\"]);\nDeviceProcessEvents\n| where Timestamp > ago(LookbackHours)\n| where InitiatingProcessFileName in~ (PkgManagers)\n| where FileName in~ (Interpreters)\n| where AccountName !endswith \"$\"\n| where ProcessCommandLine has_any (CredIndicators)\n     or ProcessCommandLine matches regex @\"(?i)(curl|wget)\\s+-[a-zA-Z]*\\s*https?://\"\n     or InitiatingProcessCommandLine has_any (HookIndicators)\n| project Timestamp, DeviceName, AccountName,\n          ParentImage = InitiatingProcessFolderPath,\n          ParentCmd   = InitiatingProcessCommandLine,\n          ChildImage  = FolderPath,\n          ChildCmd    = ProcessCommandLine,\n          SHA256, IsInitiatingProcessRemoteSession\n| order by Timestamp desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_INSTALL_LIFECYCLE_SCRIPT_HARVESTS_LOCAL_CR",
      "title": "[WEEKLY] Package-install lifecycle script harvests local credentials and beacons to a non-baselined domain",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the cross-platform supply-chain pattern where a package manager or installer (npm/yarn/pnpm/npx, VS Code extension host, MSI/PKG installer) spawns a child during pre/postinstall that reads developer or cloud credential stores (.npmrc, ~/.aws/credentials, ~/.ssh/id_*, .env, /proc/<pid>/environ, browser Login Data, GitHub/NPM/Anthropic token env vars, macOS keychain) and within ~5 minutes the same host beacons to an external host that has not been seen in the 30-day org-wide baseline. This fortnight is dominated by exactly this chain: CISA KEV CVE-2026-48027 (Nx Console malicious extension), CVE-2026-8398 (Daemon Tools Lite embedded malicious code), Microsoft's 33 malicious npm packages abusing dependency confusion, Snyk's Miasma compromise of @redhat-cloud-services, The Hacker News' Claude Code GitHub Action prompt-injection reading /proc/<pid>/environ on self-hosted runners, FlutterShell macOS malvertising, and GHSA-jpvj-wpmj-h7rv (@cap-js/openapi). It spans install (lifecycle hook), credential-access (file/env scrape), and exfiltration/C2 (new-domain egress) stages, so it gives one alert per real chain rather than one per IOC. It fires when a node/npm/yarn/pnpm/installer-rooted process reads a credential artefact AND the host then talks to a fresh external host within the window; it does NOT fire on a credential read alone (CLI workflows), nor on package-manager network activity to known baselined registries (npmjs, github, vscode CDN, cloud provider endpoints).\n\nRationale: Every article in scope ends with the same chain \u2014 a freshly-installed package or extension's lifecycle hook (npm preinstall, VS Code extension postInstall, MSI custom action, GitHub Actions runner under prompt injection) reads a developer/cloud credential artefact, then beacons home. Correlating the credential-read process (gated on a package-install parent or lifecycle cue) with a new-domain outbound from the same device inside 5 minutes captures every variant \u2014 Nx Console, Daemon Tools Lite, the 33-package dependency-confusion set, @redhat-cloud-services, @cap-js/openapi, FlutterShell, and the Claude Code Action /proc/environ exfil \u2014 while ignoring legitimate npm installs that don't reach for secrets and routine secret access that doesn't egress.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1546.016",
          "name": "Event Triggered Execution: Installer Packages"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1552.004",
          "name": "Unsecured Credentials: Private Keys"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        },
        {
          "id": "T1027",
          "name": "Obfuscated Files or Information"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem",
        "Network_Traffic.All_Traffic",
        "Network_Resolution.DNS"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as credTime from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"node.exe\",\"node\",\"npm.cmd\",\"npm\",\"yarn.cmd\",\"yarn\",\"pnpm.cmd\",\"pnpm\",\"npx.cmd\",\"npx\",\"Code.exe\",\"code\",\"msiexec.exe\",\"setup.exe\",\"installer\",\"Installer\") OR Processes.parent_process IN (\"*preinstall*\",\"*postinstall*\",\"*npm-cli.js*\",\"*yarn.js*\",\"*pnpm.cjs*\",\"*npm install*\",\"*yarn install*\",\"*pnpm install*\")) AND (Processes.process IN (\"*.npmrc*\",\"*.aws/credentials*\",\"*.aws\\\\credentials*\",\"*.ssh/id_*\",\"*.ssh\\\\id_*\",\"*.env*\",\"*/proc/*/environ*\",\"*Login Data*\",\"*Cookies*\",\"*GITHUB_TOKEN*\",\"*NPM_TOKEN*\",\"*ANTHROPIC_API_KEY*\",\"*aws_access_key*\",\"*aws_secret*\",\"*.netrc*\",\"*.docker/config*\",\"*libsecret*\",\"*find-generic-password*\") OR Processes.process_name IN (\"env\",\"printenv\",\"security\",\"keychain\",\"strings\",\"xxd\")) by host Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.parent_process | `drop_dm_object_name(Processes)` | rename host AS src | join type=inner src [ search `cim_Network_Traffic_indexes` earliest=-7d | tstats `summariesonly` count as egressCount min(_time) as netTime values(All_Traffic.dest) as dest_hostname values(All_Traffic.dest) as dest values(All_Traffic.dest_port) as dest_port from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_port IN (80,443,8080,8443) NOT (All_Traffic.dest IN (\"registry.npmjs.org\",\"*.npmjs.com\",\"*.yarnpkg.com\",\"*.pnpm.io\",\"*.github.com\",\"*.githubusercontent.com\",\"*.vscode-cdn.net\",\"*.microsoft.com\",\"*.windows.net\",\"*.googleapis.com\",\"*.amazonaws.com\",\"*.azure.com\",\"*.docker.io\",\"*.docker.com\")) by All_Traffic.src All_Traffic.dest All_Traffic.dest_port | `drop_dm_object_name(All_Traffic)` ] | eval delaySec = abs(netTime - credTime) | where delaySec <= 300 | search NOT user IN (\"SYSTEM\",\"LOCAL SERVICE\",\"NETWORK SERVICE\") AND NOT match(user,\"\\$$\") | table credTime netTime delaySec src user parent_process_name process_name process dest_hostname dest dest_port | sort -credTime",
      "defender_kql": "let Lookback = 7d;\nlet Window = 5m;\nlet PkgInstallParents = dynamic([\"node.exe\",\"npm.cmd\",\"yarn.cmd\",\"pnpm.cmd\",\"npx.cmd\",\"code.exe\",\"msiexec.exe\",\"setup.exe\",\"installer.exe\"]);\nlet LifecycleCues = dynamic([\"preinstall\",\"postinstall\",\"npm install\",\"yarn install\",\"pnpm install\",\"npm-cli.js\",\"yarn.js\",\"pnpm.cjs\"]);\nlet CredArtefacts = dynamic([\".npmrc\",\".aws\\\\credentials\",\".aws/credentials\",\".ssh\\\\id_\",\".ssh/id_\",\".env\",\"/proc/\",\"environ\",\"Login Data\",\"Cookies\",\"GITHUB_TOKEN\",\"NPM_TOKEN\",\"ANTHROPIC_API_KEY\",\"HF_TOKEN\",\"aws_access_key\",\"aws_secret\",\".netrc\",\".docker\\\\config\",\".docker/config\",\"libsecret\",\"find-generic-password\"]);\nlet KnownRegistries = dynamic([\"registry.npmjs.org\",\"npmjs.com\",\"yarnpkg.com\",\"pnpm.io\",\"github.com\",\"githubusercontent.com\",\"vscode-cdn.net\",\"microsoft.com\",\"windows.net\",\"googleapis.com\",\"amazonaws.com\",\"azure.com\",\"docker.io\",\"docker.com\"]);\nlet CredReads = DeviceProcessEvents\n    | where Timestamp > ago(Lookback)\n    | where InitiatingProcessFileName in~ (PkgInstallParents)\n        or InitiatingProcessCommandLine has_any (LifecycleCues)\n    | where ProcessCommandLine has_any (CredArtefacts)\n        or FolderPath has_any (CredArtefacts)\n        or FileName in~ (\"env\",\"printenv\",\"security\",\"keychain\",\"strings\",\"xxd\")\n    | where AccountName !endswith \"$\"\n        and AccountName !in~ (\"system\",\"local service\",\"network service\")\n    | project CredTime = Timestamp, DeviceId, DeviceName, AccountName,\n              ParentImage = InitiatingProcessFileName,\n              ParentCmd = InitiatingProcessCommandLine,\n              ChildImage = FolderPath,\n              ChildCmd = ProcessCommandLine;\nlet BaselineDomains = DeviceNetworkEvents\n    | where Timestamp between (ago(30d) .. ago(Lookback))\n    | where isnotempty(RemoteUrl)\n    | summarize HostCount = dcount(DeviceId) by RemoteUrl\n    | where HostCount > 3;\nlet Egress = DeviceNetworkEvents\n    | where Timestamp > ago(Lookback)\n    | where RemoteIPType == \"Public\"\n    | where isnotempty(RemoteUrl)\n    | where not(RemoteUrl has_any (KnownRegistries))\n    | join kind=leftanti BaselineDomains on RemoteUrl\n    | project NetTime = Timestamp, DeviceId, EgressImage = InitiatingProcessFileName,\n              EgressCmd = InitiatingProcessCommandLine,\n              RemoteUrl, RemoteIP, RemotePort;\nCredReads\n| join kind=inner Egress on DeviceId\n| where NetTime between (CredTime .. CredTime + Window)\n| extend DelaySec = datetime_diff('second', NetTime, CredTime)\n| project CredTime, NetTime, DelaySec,\n          DeviceName, AccountName,\n          ParentImage, ParentCmd, ChildImage, ChildCmd,\n          EgressImage, EgressCmd, RemoteUrl, RemoteIP, RemotePort\n| order by CredTime desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_MANAGER_CHILD_PROCESS_CREDENTIAL_FAN_OUT_W",
      "title": "[WEEKLY] Package-manager child process credential fan-out with public egress (Mini Shai-Hulud / TeamPCP worm chain)",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the canonical post-install supply-chain worm chain: a Node/Python/Ruby package-manager process (or any child it spawns) reads three or more distinct developer-secret files (.npmrc, .pypirc, .aws/credentials, .ssh/id_*, .kube/config, .docker/config.json, .git-credentials, .env, .vault-token, NPM_TOKEN/GITHUB_TOKEN env caches) AND establishes outbound egress to a public host, all inside a 5-minute correlation window rooted at the install. This is the joint pattern driving this fortnight's storm: Mini Shai-Hulud Wave 4 against @tanstack (Snyk, May 11), the follow-on OpenAI device compromise via TanStack (THN, May 15), TeamPCP's Checkmarx Jenkins AST plugin trojan (THN, May 11), malicious node-ipc versions exfiltrating to sh.azurestaticprovider.net (StepSecurity, May 15), QLNX Linux RAT (THN, May 8), and ESET's ScarCruft sqgame supply-chain DLL side-load (May 5). The detection spans install (compromised package execution) \u2192 actions (credential collection) \u2192 c2 (exfil), and is specifically engineered to ignore IOC rotation \u2014 the same TanStack worm has cycled IPs/domains weekly. It fires when a worm post-install script harvests then beacons; it does NOT fire on benign `npm install` (single .npmrc read, no egress), nor on standalone dev-tooling that reads credentials without spawning network traffic, nor on routine outbound from VS Code / language servers that never touch secret files.\n\nRationale: Every article in scope shares the same hinge: attacker code lands as a legitimate package install (npm/pypi/Jenkins plugin/game installer DLL side-load) and the very first post-install action is to enumerate developer credential stores then ship them outbound. Single-event detections in the catalogue catch one slice (QLNX's fan-out, node-ipc's beacon) \u2014 none correlates install + fan-out + egress in a 5-minute window, which is what raises this from noisy hunt to alerting-grade.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1552.004",
          "name": "Unsecured Credentials: Private Keys"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        },
        {
          "id": "T1567",
          "name": "Exfiltration Over Web Service"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats summariesonly=t count min(_time) as pkg_time from datamodel=Endpoint.Processes where (Processes.process_name IN (\"node.exe\",\"node\",\"npm.exe\",\"npm\",\"npx.exe\",\"npx\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"pip.exe\",\"pip\",\"pip3\",\"python.exe\",\"python\",\"python3\",\"ruby\",\"gem\",\"bundle\") OR Processes.parent_process_name IN (\"node.exe\",\"node\",\"npm.exe\",\"npm\",\"npx.exe\",\"npx\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"pip.exe\",\"pip\",\"pip3\",\"python.exe\",\"python\",\"python3\")) by Processes.dest, Processes.user, Processes.process_name, Processes.parent_process_name, Processes.process | `drop_dm_object_name(Processes)` | join type=inner dest [ | tstats summariesonly=t dc(Filesystem.file_path) as secret_file_count values(Filesystem.file_path) as secret_files min(_time) as first_secret_time max(_time) as last_secret_time from datamodel=Endpoint.Filesystem where (Filesystem.file_path=\"*/.npmrc*\" OR Filesystem.file_path=\"*/.pypirc*\" OR Filesystem.file_path=\"*/.aws/credentials*\" OR Filesystem.file_path=\"*/.aws/config*\" OR Filesystem.file_path=\"*/.ssh/id_*\" OR Filesystem.file_path=\"*/.ssh/known_hosts*\" OR Filesystem.file_path=\"*/.kube/config*\" OR Filesystem.file_path=\"*/.docker/config.json*\" OR Filesystem.file_path=\"*/.git-credentials*\" OR Filesystem.file_path=\"*/.gitconfig*\" OR Filesystem.file_path=\"*/.env*\" OR Filesystem.file_path=\"*/.netrc*\" OR Filesystem.file_path=\"*/.vault-token*\" OR Filesystem.file_path=\"*GITHUB_TOKEN*\" OR Filesystem.file_path=\"*NPM_TOKEN*\" OR Filesystem.file_path=\"*pypi-token*\") AND Filesystem.process_name IN (\"node.exe\",\"node\",\"npm.exe\",\"npm\",\"npx.exe\",\"npx\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"python.exe\",\"python\",\"python3\",\"pip.exe\",\"pip\",\"pip3\",\"ruby\",\"sh\",\"bash\",\"zsh\",\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\") by Filesystem.dest, Filesystem.process_name | `drop_dm_object_name(Filesystem)` | where secret_file_count >= 3 ] | join type=inner dest [ | tstats summariesonly=t values(All_Traffic.dest_ip) as c2_ips values(All_Traffic.dest) as c2_hosts dc(All_Traffic.dest_ip) as c2_count min(_time) as first_net_time from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_category!=\"internal\" AND All_Traffic.app IN (\"node.exe\",\"node\",\"npm.exe\",\"npm\",\"npx.exe\",\"npx\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"python.exe\",\"python\",\"python3\",\"pip.exe\",\"pip\",\"pip3\",\"ruby\",\"curl\",\"wget\",\"sh\",\"bash\") by All_Traffic.src | `drop_dm_object_name(All_Traffic)` | rename src as dest ] | eval install_to_secret_sec=last_secret_time-pkg_time, install_to_net_sec=first_net_time-pkg_time | where install_to_secret_sec>=0 AND install_to_secret_sec<=300 AND install_to_net_sec>=0 AND install_to_net_sec<=300 | table pkg_time, dest, user, process_name, parent_process_name, process, secret_file_count, secret_files, c2_ips, c2_hosts, install_to_secret_sec, install_to_net_sec | sort - pkg_time",
      "defender_kql": "let WindowSec = 300;\nlet SecretFilePatterns = dynamic([@\"\\.npmrc\", @\"\\.pypirc\", @\"\\.aws\\credentials\", @\"\\.aws\\config\", @\"\\.ssh\\id_\", @\"\\.kube\\config\", @\"\\.docker\\config.json\", @\"\\.git-credentials\", @\"\\.gitconfig\", @\"\\.env\", @\"\\.netrc\", @\"\\.vault-token\", \"NPM_TOKEN\", \"GITHUB_TOKEN\", \"pypi-token\"]);\nlet PkgMgrs = dynamic([\"node.exe\",\"npm.exe\",\"npx.exe\",\"yarn.exe\",\"pnpm.exe\",\"pip.exe\",\"pip3.exe\",\"python.exe\",\"python3.exe\",\"ruby.exe\",\"gem.exe\",\"bundle.exe\"]);\nlet PkgProcs = DeviceProcessEvents\n    | where Timestamp > ago(7d)\n    | where FileName in~ (PkgMgrs) or InitiatingProcessFileName in~ (PkgMgrs)\n    | where AccountName !endswith \"$\"\n    | project PkgTime = Timestamp, DeviceId, DeviceName, AccountName,\n              PkgFile = FileName, PkgCmd = ProcessCommandLine,\n              PkgParent = InitiatingProcessFileName;\nlet SecretReads = DeviceFileEvents\n    | where Timestamp > ago(7d)\n    | where ActionType in (\"FileCreated\",\"FileModified\",\"FileRenamed\")\n    | where InitiatingProcessFileName in~ (PkgMgrs)\n       or InitiatingProcessParentFileName in~ (PkgMgrs)\n       or InitiatingProcessFileName in~ (\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"bash.exe\",\"sh.exe\")\n    | extend FullPath = strcat(FolderPath, @\"\\\", FileName)\n    | where FullPath has_any (SecretFilePatterns)\n    | project SecretTime = Timestamp, DeviceId, ReaderName = InitiatingProcessFileName,\n              SecretPath = FullPath;\nlet Beacons = DeviceNetworkEvents\n    | where Timestamp > ago(7d)\n    | where RemoteIPType == \"Public\"\n    | where InitiatingProcessFileName in~ (PkgMgrs)\n       or InitiatingProcessParentFileName in~ (PkgMgrs)\n       or InitiatingProcessFileName in~ (\"curl.exe\",\"wget.exe\",\"powershell.exe\",\"pwsh.exe\",\"bitsadmin.exe\")\n    | project NetTime = Timestamp, DeviceId, BeaconName = InitiatingProcessFileName,\n              RemoteIP, RemoteUrl, RemotePort;\nPkgProcs\n| join kind=inner SecretReads on DeviceId\n| where SecretTime between (PkgTime .. PkgTime + WindowSec * 1s)\n| summarize SecretCount = dcount(SecretPath),\n            Secrets = make_set(SecretPath, 20),\n            FirstSecretTime = min(SecretTime)\n            by DeviceId, DeviceName, AccountName, PkgTime, PkgFile, PkgParent, PkgCmd\n| where SecretCount >= 3\n| join kind=inner Beacons on DeviceId\n| where NetTime between (PkgTime .. PkgTime + WindowSec * 1s)\n| summarize C2_IPs = make_set(RemoteIP, 20),\n            C2_Urls = make_set(RemoteUrl, 20),\n            FirstNetTime = min(NetTime)\n            by DeviceId, DeviceName, AccountName, PkgTime, PkgFile, PkgParent, PkgCmd, SecretCount, Secrets\n| project Timestamp = PkgTime, DeviceName, AccountName,\n          PkgFile, PkgParent, PkgCmd,\n          SecretCount, Secrets,\n          InstallToSecretSec = datetime_diff('second', FirstNetTime, PkgTime),\n          C2_IPs, C2_Urls\n| order by Timestamp desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_MANAGER_DEV_TOOL_AUTO_EXECUTION_TRIGGERS_N",
      "title": "[WEEKLY] Package Manager / Dev-Tool Auto-Execution Triggers Non-Registry Egress or Credential-Store Access",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the supply-chain TTP that dominated this fortnight: a package manager or build tool (npm, node, cargo, pip, yarn, pnpm, ruby/gem) \u2014 or a freshly-dropped trojanized installer \u2014 spawns a child process or initiates outbound traffic to a non-registry public destination within a short window of install/test execution, often paired with reads of browser credential stores, SSH keys, AWS/npm/git config, or cloud token files. Driven by the node-ipc 9.1.6/9.2.3/12.0.1 compromise (StepSecurity + Snyk, 2026-05-15) exfiltrating to sh.azurestaticprovider.net/37.16.75.69, the pgserve 1.1.11\u20131.1.13 postinstall script harvesting creds to an ICP canister (StepSecurity 2026-05-04), DeepSeek-TUI CVE-2026-45311 auto-approved cargo test execution (GHSA 2026-05-14), and the JDownloader site compromise distributing trojanized installers signed as 'Zipline LLC'/'The Water Team' (CyberSecurityNews 2026-05-16). It spans Delivery (compromised package/installer pulled) \u2192 Execution (postinstall/require/cargo-test/installer run) \u2192 Credential Access (T1555.003/T1552.001) \u2192 C2/Exfil (T1041/T1071) and fires only when both the dev-tool process start and the off-registry public connection (or credential-store read) occur on the same host within \u22645 minutes. It will NOT fire for routine `npm install` traffic to npmjs.org/PyPI/crates.io/github.com (allowlisted) or for dev-tools talking only to internal mirrors.\n\nRationale: All four headline campaigns this fortnight (node-ipc 9.1.6/9.2.3/12.0.1 \u2192 37.16.75.69; pgserve 1.1.11\u20131.1.13 \u2192 ICP canister; DeepSeek-TUI CVE-2026-45311 cargo-test auto-exec; JDownloader trojanized installer) share the same observable joint pattern: a trusted local dev-tool/package-manager process spawns a script interpreter or directly initiates an unexpected outbound connection that bypasses the known package-registry allowlist, within seconds of install/test execution. Correlating dev-tool process start with non-registry public egress (and optionally credential-store reads) inside a 5-minute window catches the technique without depending on any single rotating IOC, and the registry-domain allowlist suppresses the legitimate `npm install` baseline that would otherwise drown the signal.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1204.002",
          "name": "User Execution: Malicious File"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1555.003",
          "name": "Credentials from Web Browsers"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic",
        "Endpoint.Filesystem"
      ],
      "splunk_spl": "| tstats summariesonly=true count min(_time) as install_time max(_time) as install_last values(Processes.process) as install_cmd values(Processes.process_name) as child_proc values(Processes.parent_process_name) as parent_proc from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"npm\",\"npm.exe\",\"node\",\"node.exe\",\"npm-cli.js\",\"cargo\",\"cargo.exe\",\"yarn\",\"yarn.exe\",\"pnpm\",\"pnpm.exe\",\"pip\",\"pip.exe\",\"pip3\",\"python\",\"python.exe\",\"python3\",\"ruby\",\"ruby.exe\",\"gem\",\"gem.exe\",\"go\",\"go.exe\") AND (Processes.process_name IN (\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"sh\",\"bash\",\"zsh\",\"dash\",\"wscript.exe\",\"cscript.exe\",\"mshta.exe\",\"curl.exe\",\"wget.exe\",\"certutil.exe\",\"bitsadmin.exe\") OR Processes.process IN (\"*Login Data*\",\"*cookies.sqlite*\",\"*logins.json*\",\"*\\\\.aws\\\\credentials*\",\"*/.aws/credentials*\",\"*\\\\.ssh\\\\id_*\",\"*/.ssh/id_*\",\"*\\\\.npmrc*\",\"*/.npmrc*\",\"*\\\\.docker\\\\config.json*\")) by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process_guid | `drop_dm_object_name(Processes)` | join type=inner dest [| tstats summariesonly=true count min(_time) as net_time values(All_Traffic.dest) as remote_dest values(All_Traffic.dest_ip) as remote_ip values(All_Traffic.dest_port) as remote_port values(All_Traffic.app) as net_app from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_category!=\"internal\" NOT (All_Traffic.dest IN (\"*registry.npmjs.org\",\"*registry.yarnpkg.com\",\"*pypi.org\",\"*files.pythonhosted.org\",\"*crates.io\",\"*static.crates.io\",\"*rubygems.org\",\"*proxy.golang.org\",\"*github.com\",\"*githubusercontent.com\",\"*codeload.github.com\",\"*objects.githubusercontent.com\",\"*nodejs.org\",\"*npmmirror.com\")) by All_Traffic.src All_Traffic.dest All_Traffic.dest_ip All_Traffic.dest_port All_Traffic.app | rename All_Traffic.src as dest | `drop_dm_object_name(All_Traffic)`] | where net_time >= install_time AND net_time <= (install_time + 300) | eval delay_seconds = net_time - install_time | eval risk_message=\"Dev-tool (\".parent_proc.\") spawned \".child_proc.\" then non-registry public egress to \".remote_dest.\" within \".delay_seconds.\"s \u2014 supply-chain payload pattern (node-ipc/pgserve/DeepSeek-TUI/JDownloader fortnight)\" | table install_time, net_time, delay_seconds, dest, user, parent_proc, child_proc, install_cmd, remote_dest, remote_ip, remote_port, net_app | sort - install_time",
      "defender_kql": "let LookbackHours = 24h;\nlet CorrelationWindow = 5m;\nlet DevTools = dynamic([\"npm.exe\",\"npm\",\"node.exe\",\"node\",\"cargo.exe\",\"cargo\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"pip.exe\",\"pip\",\"pip3\",\"python.exe\",\"python\",\"python3\",\"ruby.exe\",\"ruby\",\"gem.exe\",\"gem\",\"go.exe\",\"go\"]);\nlet Interpreters = dynamic([\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"sh\",\"bash\",\"zsh\",\"dash\",\"wscript.exe\",\"cscript.exe\",\"mshta.exe\",\"curl.exe\",\"wget.exe\",\"certutil.exe\",\"bitsadmin.exe\"]);\nlet RegistryHosts = dynamic([\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"pypi.org\",\"files.pythonhosted.org\",\"crates.io\",\"static.crates.io\",\"rubygems.org\",\"proxy.golang.org\",\"github.com\",\"raw.githubusercontent.com\",\"codeload.github.com\",\"objects.githubusercontent.com\",\"nodejs.org\",\"npmmirror.com\"]);\nlet CredPaths = dynamic([@\"\\Google\\Chrome\\User Data\",@\"\\Microsoft\\Edge\\User Data\",@\"\\Mozilla\\Firefox\\Profiles\",@\"\\.aws\\credentials\",@\"\\.ssh\\id_\",@\"\\.npmrc\",@\"\\.docker\\config.json\",@\"\\.gitconfig\",@\".config/gh/hosts.yml\"]);\nlet InstallSpawns = DeviceProcessEvents\n    | where Timestamp > ago(LookbackHours)\n    | where InitiatingProcessFileName in~ (DevTools)\n    | where FileName in~ (Interpreters) or ProcessCommandLine has_any (\"Login Data\",\"cookies.sqlite\",\"logins.json\",\".aws/credentials\",\".ssh/id_\",\".npmrc\")\n    | where AccountName !endswith \"$\"\n    | project DeviceId, DeviceName, AccountName,\n              InstallTime = Timestamp,\n              ParentFile = InitiatingProcessFileName,\n              ParentCmd = InitiatingProcessCommandLine,\n              ChildFile = FileName,\n              ChildCmd = ProcessCommandLine,\n              ChildPid = ProcessId;\nlet NetEgress = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackHours)\n    | where RemoteIPType == \"Public\"\n    | where isempty(RemoteUrl) or not(RemoteUrl has_any (RegistryHosts))\n    | where InitiatingProcessAccountName !endswith \"$\"\n    | project DeviceId, NetTime = Timestamp,\n              EgressProcess = InitiatingProcessFileName,\n              EgressCmd = InitiatingProcessCommandLine,\n              RemoteIP, RemoteUrl, RemotePort;\nlet CredAccess = DeviceFileEvents\n    | where Timestamp > ago(LookbackHours)\n    | where FolderPath has_any (CredPaths)\n    | where InitiatingProcessFileName !in~ (\"chrome.exe\",\"msedge.exe\",\"firefox.exe\",\"brave.exe\",\"opera.exe\")\n    | project DeviceId, FileTime = Timestamp,\n              AccessProcess = InitiatingProcessFileName,\n              AccessFile = FolderPath;\nInstallSpawns\n| join kind=inner NetEgress on DeviceId\n| where NetTime between (InstallTime .. InstallTime + CorrelationWindow)\n| join kind=leftouter CredAccess on DeviceId\n| extend CredHit = iif(isnotempty(AccessFile) and FileTime between (InstallTime .. InstallTime + CorrelationWindow), AccessFile, \"\")\n| extend DelaySec = datetime_diff('second', NetTime, InstallTime)\n| project InstallTime, DeviceName, AccountName,\n          ParentFile, ChildFile, ChildCmd,\n          NetTime, DelaySec, RemoteIP, RemoteUrl, EgressProcess,\n          CredHit\n| summarize arg_min(InstallTime, *) by DeviceName, AccountName, ParentFile, RemoteUrl, RemoteIP\n| order by InstallTime desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_MANAGER_INSTALL_HOOK_SPAWNS_INTERPRETER_TH",
      "title": "[WEEKLY] Package-manager install hook spawns interpreter that beacons to non-registry host within 120s",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the joint pattern across the May 2026 supply-chain wave (Mini Shai-Hulud / TeamPCP @antv compromise, Laravel-Lang Composer tag-rewrite, Sicoob.Sdk NuGet, vpmdhaj typosquat npm packages, dependency-confusion npm scopes, and Microsoft durabletask PyPI compromise): a developer-host or CI runner invokes a package manager (npm/yarn/pnpm/bun/composer/dotnet/pip), the install-time hook (preinstall/postinstall, composer scripts, nuget install.ps1, pip setup.py) spawns an interpreter (node/bun/python/php/powershell/bash), and that interpreter opens an outbound connection to a host that is NOT the official registry within ~120 seconds. The chain spans Delivery (T1195.002 supply-chain), Execution (T1059.001/004/006/007, T1546.016 installer-triggered), and C2/Exfil (T1071.001, T1041, T1567). Fires when a freshly-installed dependency reaches out to an attacker-controlled domain such as those rotated weekly by these actors (m-kosche.com, git-service.com, flipboxstudio.info, sportsontheweb.net, moika.tech, cloudplatform-single-spa.io, t-in-one.io). Does NOT fire when the same interpreters are launched outside an install context, or when the post-install network egress goes to a known registry / CDN allowlist (registry.npmjs.org, packagist.org, api.nuget.org, files.pythonhosted.org, github.com release CDN).\n\nRationale: Every article in scope describes the same chain: a compromised package on a public registry, install-time scripts that hand control to a language runtime, and that runtime contacting an attacker-controlled host within seconds. Correlating package-manager \u2192 interpreter child \u2192 non-registry public egress on the same PID inside a 120s window catches all six campaigns (Mini Shai-Hulud npm, Laravel-Lang Composer, Sicoob NuGet, vpmdhaj typosquat, dependency-confusion scopes, durabletask PyPI) without depending on any single IOC, while excluding the legitimate registry fetches that dominate normal install traffic.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1546.016",
          "name": "Event Triggered Execution: Installer Packages"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic",
        "Network_Resolution.DNS"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as proc_time values(Processes.process) as cmd values(Processes.process_path) as image values(Processes.parent_process) as parent_cmd from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"npm.exe\",\"npm-cli.js\",\"node.exe\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"bun.exe\",\"bun\",\"composer.phar\",\"composer\",\"php.exe\",\"php\",\"dotnet.exe\",\"dotnet\",\"nuget.exe\",\"pip\",\"pip3\",\"pip.exe\",\"python.exe\",\"python\",\"python3\") Processes.process_name IN (\"node.exe\",\"node\",\"bun.exe\",\"bun\",\"python.exe\",\"python\",\"python3\",\"php.exe\",\"php\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"bash\",\"sh\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name Processes.process_id\n| `drop_dm_object_name(\"Processes\")`\n| rename process_id as install_pid\n| join type=inner dest [\n    | tstats `summariesonly` count min(_time) as net_time values(All_Traffic.dest) as c2_dest values(All_Traffic.dest_ip) as c2_ip values(All_Traffic.app) as net_app from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_port IN (80,443,53,8080,8443) NOT (All_Traffic.dest IN (\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"packagist.org\",\"repo.packagist.org\",\"api.nuget.org\",\"www.nuget.org\",\"files.pythonhosted.org\",\"pypi.org\",\"objects.githubusercontent.com\",\"codeload.github.com\",\"github.com\",\"raw.githubusercontent.com\")) by All_Traffic.src All_Traffic.dest All_Traffic.dest_ip All_Traffic.process_id host\n    | `drop_dm_object_name(\"All_Traffic\")`\n    | rename src as dest process_id as install_pid\n]\n| eval delay_sec = round((net_time - proc_time),0)\n| where delay_sec >= 0 AND delay_sec <= 120\n| where NOT match(c2_dest, \"(?i)(npmjs|yarnpkg|packagist|nuget|pythonhosted|githubusercontent|github\\.com|microsoft\\.com|google\\.com|amazonaws\\.com)$\")\n| stats min(proc_time) as install_time min(net_time) as c2_time values(image) as binary values(parent_cmd) as install_cmd values(c2_dest) as c2_domain values(c2_ip) as c2_ip dc(c2_dest) as c2_count by dest user process_name parent_process_name\n| where c2_count >= 1\n| convert ctime(install_time) ctime(c2_time)\n| `security_content_ctime(install_time)`",
      "defender_kql": "let Window = 120s;\nlet PkgMgrs = dynamic([\"npm.exe\",\"npm\",\"npm-cli.js\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"bun.exe\",\"bun\",\"composer\",\"composer.phar\",\"dotnet.exe\",\"dotnet\",\"nuget.exe\",\"pip\",\"pip3\",\"pip.exe\",\"python.exe\",\"python\",\"python3\"]);\nlet Interpreters = dynamic([\"node.exe\",\"node\",\"bun.exe\",\"bun\",\"python.exe\",\"python\",\"python3\",\"php.exe\",\"php\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"bash\",\"sh\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\"]);\nlet RegistryAllow = dynamic([\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"packagist.org\",\"repo.packagist.org\",\"api.nuget.org\",\"www.nuget.org\",\"files.pythonhosted.org\",\"pypi.org\",\"objects.githubusercontent.com\",\"codeload.github.com\",\"github.com\",\"raw.githubusercontent.com\",\"deb.debian.org\",\"security.ubuntu.com\"]);\nlet KnownBadIOC = dynamic([\"m-kosche.com\",\"t.m-kosche.com\",\"git-service.com\",\"check.git-service.com\",\"flipboxstudio.info\",\"sportsontheweb.net\",\"aab.sportsontheweb.net\",\"moika.tech\",\"oob.moika.tech\",\"cloudplatform-single-spa.io\",\"t-in-one.io\",\"filev2.getsession.org\"]);\nlet InstallSpawn = DeviceProcessEvents\n    | where Timestamp > ago(7d)\n    | where InitiatingProcessFileName in~ (PkgMgrs)\n    | where FileName in~ (Interpreters)\n    | where AccountName !endswith \"$\"\n    | project SpawnTime = Timestamp, DeviceId, DeviceName, AccountName,\n              InstallParentCmd = InitiatingProcessCommandLine,\n              InstallParent    = InitiatingProcessFileName,\n              InterpreterImage = FolderPath,\n              InterpreterCmd   = ProcessCommandLine,\n              InterpreterPid   = ProcessId,\n              InterpreterName  = FileName;\nlet NetEgress = DeviceNetworkEvents\n    | where Timestamp > ago(7d)\n    | where RemoteIPType == \"Public\"\n    | where isnotempty(RemoteUrl) or isnotempty(RemoteIP)\n    | extend Domain = tolower(tostring(RemoteUrl))\n    | where Domain !in~ (RegistryAllow)\n    | where not(Domain has_any (RegistryAllow))\n    | project NetTime = Timestamp, DeviceId, DeviceName,\n              EgressPid = InitiatingProcessId,\n              EgressImage = InitiatingProcessFileName,\n              RemoteUrl, RemoteIP, RemotePort;\nInstallSpawn\n| join kind=inner NetEgress on DeviceId, $left.InterpreterPid == $right.EgressPid\n| where NetTime between (SpawnTime .. SpawnTime + Window)\n| extend DelaySec = datetime_diff('second', NetTime, SpawnTime)\n| extend IsKnownIOC = RemoteUrl has_any (KnownBadIOC)\n| summarize SpawnTime = min(SpawnTime), FirstBeacon = min(NetTime),\n            Domains = make_set(RemoteUrl, 25),\n            IPs = make_set(RemoteIP, 25),\n            Ports = make_set(RemotePort, 10),\n            InstallParent = any(InstallParent),\n            InstallCmd = any(InstallParentCmd),\n            InterpCmd = any(InterpreterCmd),\n            MinDelaySec = min(DelaySec),\n            IocHit = max(toint(IsKnownIOC))\n            by DeviceName, AccountName, InterpreterName, InterpreterPid\n| extend Severity = case(IocHit == 1, \"High\", \"Medium\")\n| order by SpawnTime desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_MANAGER_INSTALL_HOOK_SPAWNS_SCRIPTING_INTE",
      "title": "[WEEKLY] Package Manager Install Hook Spawns Scripting Interpreter Then Touches Credential Files or Egresses Off-Registry",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the joint pattern shared across the May 2026 npm/PyPI/Packagist compromise wave: a package-manager process (npm/yarn/pnpm/pip/uv/poetry/composer/php) spawns a generic scripting interpreter (node/python/sh/bash/powershell/curl/wget) as part of a postinstall/setup.py hook, and that child either reads developer credential material (.npmrc, .pypirc, ~/.aws/credentials, ~/.ssh/id_rsa, .env, GH_TOKEN-bearing files) or beacons to a non-registry public destination within a 5-minute window on the same host. This matters this fortnight because TeamPCP's self-spreading 'Mini Shai-Hulud' worm hit @tanstack ('TanStack Npm Packages Compromised Inside The Mini Shai Hulud Supply Chain Attack', 'TeamPCP's Mini Shai-Hulud Is Back') and @beproduct/nestjs-auth, Microsoft's durabletask PyPI was trojanised ('The AntV Supply Chain Campaign Expands'), node-ipc, guardrails-ai==0.10.1 (CVE-2026-45758), @cap-js/sqlite|postgres|db-service (CVE-2026-46421), and the Laravel-Lang Composer namespace ('Laravel-Lang Supply Chain Advisory', 'Every Tag\u2026Rewritten', 'Supply Chain Attack Targets Laravel-Lang') were all compromised in a 14-day window \u2014 and the '5 Supply Chain Attacks in 48 Hours' write-up plus the npm 2FA-gating response confirm this is the dominant fortnightly TTP. The detection spans install (package hook drops/runs payload), credential-access (T1552.001 / T1555 / T1539), and exfil/C2 (T1041 / T1071) \u2014 the IOCs (getsession.org, masscan.cloud, azurestaticprovider.net, flipboxstudio.info, git-tanstack.com, m-kosche.com) rotate package-by-package but the chain is invariant. It fires when a developer or CI runner installs a package and the install lifecycle script reaches outside the registry, including first-install on dev laptops and `npm ci`/`pip install -r`/`composer update` in pipelines. It deliberately does NOT fire on package managers contacting only registry/CDN destinations (registry.npmjs.org, pypi.org, files.pythonhosted.org, packagist.org, codeload.github.com, ghcr.io), on machine-account contexts, or on interpreter spawns that lack the package-manager parent ancestry.\n\nRationale: Every article in scope describes the same chain \u2014 a package-ecosystem install runs a lifecycle hook that drops to a generic interpreter, scrapes credential material from well-known dotfile paths, and beacons to attacker-controlled infrastructure that is by definition not the language's package registry. By correlating package-manager-parent \u2192 interpreter-child against (credential file touch OR non-registry public egress) within 5 minutes on the same host, the rule catches Mini Shai-Hulud (npm), durabletask/guardrails-ai (PyPI), @cap-js (npm), node-ipc (npm), and Laravel-Lang (Composer/Packagist) without depending on any of their rotating domains, hashes, or package names.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1027",
          "name": "Obfuscated Files or Information"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem",
        "Network_Traffic.All_Traffic",
        "Network_Resolution.DNS"
      ],
      "splunk_spl": "| tstats summariesonly=t count min(_time) as install_time from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"npm\",\"npm.exe\",\"npm-cli.js\",\"yarn\",\"yarn.exe\",\"pnpm\",\"pnpm.exe\",\"pip\",\"pip.exe\",\"pip3\",\"pip3.exe\",\"poetry\",\"poetry.exe\",\"uv\",\"uv.exe\",\"composer\",\"composer.phar\",\"php\",\"php.exe\",\"node\",\"node.exe\",\"python\",\"python.exe\",\"python3\",\"python3.exe\")) AND (Processes.process_name IN (\"node\",\"node.exe\",\"python\",\"python.exe\",\"python3\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"sh\",\"bash\",\"dash\",\"zsh\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\")) AND NOT (Processes.user IN (\"SYSTEM\",\"LOCAL SERVICE\",\"NETWORK SERVICE\")) by host Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_id\n| `drop_dm_object_name(Processes)`\n| rename process_id as child_pid, process_name as child_name, process as child_cmd, parent_process_name as pkg_mgr\n| join type=inner host [\n    | tstats summariesonly=t count min(_time) as net_time values(All_Traffic.dest) as dest values(All_Traffic.dest_ip) as dest_ip values(All_Traffic.dest_port) as dest_port from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_port IN (53,80,443,8080,8443) AND NOT (All_Traffic.dest IN (\"registry.npmjs.org\",\"pypi.org\",\"files.pythonhosted.org\",\"packagist.org\",\"repo.packagist.org\",\"codeload.github.com\",\"objects.githubusercontent.com\",\"ghcr.io\",\"raw.githubusercontent.com\")) AND NOT (All_Traffic.dest=\"*.npmjs.org\" OR All_Traffic.dest=\"*.pythonhosted.org\" OR All_Traffic.dest=\"*.packagist.org\") by host\n    | `drop_dm_object_name(All_Traffic)`\n  ]\n| eval delta = net_time - install_time\n| where delta >= 0 AND delta <= 300\n| append [\n    | tstats summariesonly=t count min(_time) as install_time from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"npm\",\"npm.exe\",\"pip\",\"pip3\",\"poetry\",\"uv\",\"composer\",\"composer.phar\",\"php\",\"node\",\"python\",\"python3\",\"yarn\",\"pnpm\")) AND (Processes.process_name IN (\"node\",\"python\",\"python3\",\"sh\",\"bash\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"curl\",\"wget\")) by host Processes.user Processes.parent_process_name Processes.process_name Processes.process_id\n    | `drop_dm_object_name(Processes)`\n    | join type=inner host [\n        | tstats summariesonly=t count min(_time) as file_time values(Filesystem.file_path) as cred_paths from datamodel=Endpoint.Filesystem where (Filesystem.file_name IN (\".npmrc\",\".pypirc\",\".netrc\",\".env\",\"credentials\",\"credentials.json\",\"id_rsa\",\"id_ed25519\",\".gitconfig\",\"gh_token\",\"config.json\") OR Filesystem.file_path=\"*\\\\.aws\\\\credentials\" OR Filesystem.file_path=\"*/.aws/credentials\" OR Filesystem.file_path=\"*\\\\.ssh\\\\*\" OR Filesystem.file_path=\"*/.ssh/*\" OR Filesystem.file_path=\"*\\\\.docker\\\\config.json\" OR Filesystem.file_path=\"*/.docker/config.json\") by host\n        | `drop_dm_object_name(Filesystem)`\n      ]\n    | eval delta = file_time - install_time\n    | where delta >= 0 AND delta <= 300\n  ]\n| stats min(install_time) as first_install max(coalesce(net_time,file_time)) as last_followup values(pkg_mgr) as pkg_mgr values(child_name) as child values(child_cmd) as child_cmds values(dest) as off_registry_dest values(dest_ip) as off_registry_ip values(cred_paths) as cred_paths by host user\n| eval first_install=strftime(first_install,\"%Y-%m-%d %H:%M:%S\"), last_followup=strftime(last_followup,\"%Y-%m-%d %H:%M:%S\")\n| where isnotnull(off_registry_dest) OR isnotnull(cred_paths)",
      "defender_kql": "let LookbackDays = 14d;\nlet WindowSeconds = 300;\nlet PkgMgrs = dynamic([\"npm.exe\",\"npm-cli.js\",\"yarn.exe\",\"pnpm.exe\",\"pip.exe\",\"pip3.exe\",\"poetry.exe\",\"uv.exe\",\"composer.phar\",\"composer\",\"php.exe\",\"node.exe\",\"python.exe\",\"python3.exe\"]);\nlet Interpreters = dynamic([\"node.exe\",\"python.exe\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"sh\",\"bash\",\"dash\",\"zsh\",\"curl.exe\",\"wget.exe\"]);\nlet TrustedRegistries = dynamic([\"registry.npmjs.org\",\"pypi.org\",\"files.pythonhosted.org\",\"packagist.org\",\"repo.packagist.org\",\"codeload.github.com\",\"objects.githubusercontent.com\",\"ghcr.io\",\"raw.githubusercontent.com\",\"api.github.com\",\"npmjs.com\"]);\nlet CredFileNames = dynamic([\".npmrc\",\".pypirc\",\".netrc\",\".env\",\"credentials\",\"credentials.json\",\"id_rsa\",\"id_ed25519\",\".gitconfig\",\"config.json\"]);\nlet CredPathFragments = dynamic([@\"\\.aws\\credentials\",\"/.aws/credentials\",@\"\\.ssh\\\",\"/.ssh/\",@\"\\.docker\\config.json\",\"/.docker/config.json\",@\"\\.config\\gh\\\",\"/.config/gh/\"]);\nlet PkgInstallChildren = DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where AccountName !endswith \"$\"\n    | where AccountName !in~ (\"system\",\"local service\",\"network service\")\n    | where InitiatingProcessFileName in~ (PkgMgrs)\n    | where FileName in~ (Interpreters)\n    | project ChildTime=Timestamp, DeviceId, DeviceName, AccountName,\n              ParentName=InitiatingProcessFileName,\n              ParentCmd=InitiatingProcessCommandLine,\n              ChildName=FileName,\n              ChildCmd=ProcessCommandLine,\n              ChildPid=ProcessId,\n              ChildSha256=SHA256;\nlet CredAccess = DeviceFileEvents\n    | where Timestamp > ago(LookbackDays)\n    | where ActionType in (\"FileCreated\",\"FileModified\",\"FileRenamed\")\n         or ActionType startswith \"FileAccessed\"\n    | where FileName in~ (CredFileNames)\n         or FolderPath has_any (CredPathFragments)\n    | project EventTime=Timestamp, DeviceId, Pid=InitiatingProcessId,\n              Kind=\"cred\",\n              Detail=strcat(FolderPath, \"\\\\\", FileName);\nlet OffRegistryEgress = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackDays)\n    | where RemoteIPType == \"Public\"\n    | where isnotempty(RemoteUrl) or isnotempty(RemoteIP)\n    | extend Host = tolower(RemoteUrl)\n    | where not(Host has_any (TrustedRegistries))\n    | project EventTime=Timestamp, DeviceId, Pid=InitiatingProcessId,\n              Kind=\"net\",\n              Detail=strcat(RemoteUrl, \" \", RemoteIP, \":\", tostring(RemotePort));\nlet FollowUps = union CredAccess, OffRegistryEgress;\nPkgInstallChildren\n| join kind=inner FollowUps on DeviceId\n| where EventTime between (ChildTime .. ChildTime + WindowSeconds * 1s)\n| where Pid == ChildPid or Kind == \"net\"  // file events are pid-tight; net broadens to host-window\n| summarize FollowUpEvents=make_set(strcat(Kind,\":\",Detail), 25),\n            Kinds=make_set(Kind),\n            FirstChild=min(ChildTime),\n            LastFollowUp=max(EventTime)\n            by DeviceId, DeviceName, AccountName, ParentName, ParentCmd, ChildName, ChildCmd, ChildSha256\n| where array_length(Kinds) >= 1\n| extend HasCred = Kinds has \"cred\", HasNet = Kinds has \"net\"\n| order by FirstChild desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_MANAGER_INSTALL_INTERPRETER_CHILD_NON_REGI",
      "title": "[WEEKLY] Package-Manager Install -> Interpreter Child -> Non-Registry Egress Within 5 Minutes",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the cross-stage skeleton common to every npm/PyPI/Composer/Cargo supply-chain compromise in the corpus: a package manager (npm/npx/yarn/pnpm/pip/uv/poetry/composer/cargo) spawns a script interpreter or downloader (node, python, sh/bash, powershell, curl/wget, certutil), and the same host then makes outbound HTTP/S to a destination NOT on the package-registry allowlist within ~5 minutes, optionally corroborated by reads of `.npmrc`, `.aws/credentials`, `.ssh/id_*`, `.env`, GitHub PATs and similar credential files. This pattern drove the May 2026 cluster including Mini Shai-Hulud (durabletask PyPI, @beproduct/nestjs-auth, @cap-js/*, node-ipc), GlassWorm VS Code/npm worm, TrapDoor (npm+PyPI+Crates), Laravel-Lang composer tag-rewrite (flipboxstudio.info), guardrails-ai 0.10.1 (CVE-2026-45758), and was the through-line of StepSecurity's '5 Supply Chain Attacks in 48 Hours' write-up and The Hacker News weekly recap. Kill-chain coverage: install (registry pull) -> execution (postinstall/setup.py/composer-script/build.rs) -> credential access (T1552.001) -> exfiltration over C2 (T1041/T1567). Fires when all three stages chain on the same host within the window; does NOT fire when an install completes without spawning an interpreter, when all egress stays inside the explicit registry/mirror allowlist, or when the interpreter is spawned by an IDE/CI orchestrator rather than the package-manager process itself.\n\nRationale: Every campaign in the corpus (Mini Shai-Hulud npm worm, GlassWorm, TrapDoor, Laravel-Lang Composer tag-rewrite, durabletask PyPI, @cap-js/* GHSA, node-ipc, guardrails-ai 0.10.1) follows the same skeleton: a registry install triggers an interpreter/downloader child that exfiltrates secrets to a non-registry destination within seconds. Correlating install -> interpreter-child -> non-registry-egress on the same host within a 5-minute window catches all of them without depending on the per-campaign C2 domains or hashes, which rotate weekly.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        },
        {
          "id": "T1567",
          "name": "Exfiltration Over Web Service"
        },
        {
          "id": "T1204.002",
          "name": "User Execution: Malicious File"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem",
        "Network_Traffic.All_Traffic",
        "Network_Resolution.DNS"
      ],
      "splunk_spl": "| tstats summariesonly=true count min(_time) as install_time values(Processes.process) as install_cmd values(Processes.parent_process_name) as pkg_mgr values(Processes.process_name) as child_proc from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"npm\",\"npm.exe\",\"npm.cmd\",\"npx\",\"npx.exe\",\"yarn\",\"yarn.exe\",\"pnpm\",\"pnpm.exe\",\"pip\",\"pip.exe\",\"pip3\",\"pip3.exe\",\"uv\",\"uv.exe\",\"poetry\",\"poetry.exe\",\"composer\",\"composer.exe\",\"composer.phar\",\"cargo\",\"cargo.exe\") AND (Processes.process_name IN (\"sh\",\"bash\",\"zsh\",\"dash\",\"node\",\"node.exe\",\"python\",\"python.exe\",\"python3\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"certutil.exe\",\"bitsadmin.exe\") OR Processes.process IN (\"*curl *\",\"*wget *\",\"*Invoke-WebRequest*\",\"*DownloadString*\",\"*base64 -d*\",\"*atob(*\",\"*Buffer.from*\",\"*child_process*\",\"*os.system*\",\"*subprocess.*\",\"*/dev/tcp/*\",\"*exec(*\",\"*eval(*\")) by Processes.dest Processes.user | `drop_dm_object_name(Processes)` | join type=inner dest [ | tstats summariesonly=true min(_time) as exfil_time values(All_Traffic.dest) as exfil_dest values(All_Traffic.dest_ip) as exfil_ip values(All_Traffic.dest_port) as exfil_port from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_port IN (80,443,8080,8443,4444,9001) AND NOT (All_Traffic.dest IN (\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"registry.npmmirror.com\",\"pypi.org\",\"files.pythonhosted.org\",\"pypi.python.org\",\"crates.io\",\"static.crates.io\",\"index.crates.io\",\"packagist.org\",\"repo.packagist.org\",\"github.com\",\"codeload.github.com\",\"raw.githubusercontent.com\",\"objects.githubusercontent.com\",\"api.github.com\") OR All_Traffic.dest LIKE \"%.npmjs.com\" OR All_Traffic.dest LIKE \"%.pythonhosted.org\" OR All_Traffic.dest LIKE \"%.crates.io\" OR All_Traffic.dest LIKE \"%.packagist.org\") by All_Traffic.src | rename All_Traffic.src as dest | `drop_dm_object_name(All_Traffic)` ] | where exfil_time >= install_time AND (exfil_time - install_time) <= 300 | eval delay_seconds = exfil_time - install_time | join type=left dest [ | tstats summariesonly=true values(Filesystem.file_path) as cred_files from datamodel=Endpoint.Filesystem where (Filesystem.file_path IN (\"*\\\\.npmrc\",\"*/.npmrc\",\"*\\\\.pypirc\",\"*/.pypirc\",\"*\\\\.aws\\\\credentials\",\"*/.aws/credentials\",\"*\\\\.ssh\\\\id_*\",\"*/.ssh/id_*\",\"*\\\\.gitconfig\",\"*/.gitconfig\",\"*\\\\.docker\\\\config.json\",\"*/.docker/config.json\",\"*\\\\.kube\\\\config\",\"*/.kube/config\",\"*\\\\.env\",\"*/.env\",\"*auth.json\",\"*credentials.json\")) by Filesystem.dest | rename Filesystem.dest as dest | `drop_dm_object_name(Filesystem)` ] | table install_time, exfil_time, delay_seconds, dest, user, pkg_mgr, child_proc, install_cmd, exfil_dest, exfil_ip, exfil_port, cred_files | sort - install_time",
      "defender_kql": "let LookbackHours = 24h;\nlet WindowSec = 300;\nlet PkgMgrs = dynamic([\"npm.exe\",\"npx.exe\",\"yarn.exe\",\"pnpm.exe\",\"pip.exe\",\"pip3.exe\",\"uv.exe\",\"poetry.exe\",\"composer.exe\",\"composer.phar\",\"cargo.exe\",\"npm\",\"npx\",\"yarn\",\"pnpm\",\"pip\",\"pip3\",\"uv\",\"poetry\",\"composer\",\"cargo\"]);\nlet ScriptHosts = dynamic([\"sh\",\"bash\",\"zsh\",\"dash\",\"node\",\"node.exe\",\"python\",\"python.exe\",\"python3\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"certutil.exe\",\"bitsadmin.exe\"]);\nlet RegistryHosts = dynamic([\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"registry.npmmirror.com\",\"pypi.org\",\"files.pythonhosted.org\",\"pypi.python.org\",\"crates.io\",\"static.crates.io\",\"index.crates.io\",\"packagist.org\",\"repo.packagist.org\",\"github.com\",\"codeload.github.com\",\"raw.githubusercontent.com\",\"objects.githubusercontent.com\",\"api.github.com\"]);\nlet CredFileTokens = dynamic([\".npmrc\",\".pypirc\",\".aws/credentials\",\".aws\\\\credentials\",\".aws/config\",\".ssh/id_\",\".ssh\\\\id_\",\".gitconfig\",\".docker/config.json\",\".docker\\\\config.json\",\".kube/config\",\".kube\\\\config\",\".env\",\".envrc\",\"credentials.json\",\"auth.json\",\"gh_token\",\"github_token\"]);\nlet InstallExec = DeviceProcessEvents\n    | where Timestamp > ago(LookbackHours)\n    | where AccountName !endswith \"$\" and AccountName !in~ (\"system\",\"local service\",\"network service\")\n    | where InitiatingProcessFileName in~ PkgMgrs\n    | where FileName in~ ScriptHosts\n         or ProcessCommandLine has_any (\"curl \",\"wget \",\"Invoke-WebRequest\",\"DownloadString\",\"base64 -d\",\"atob(\",\"Buffer.from\",\"child_process\",\"os.system\",\"subprocess.\",\"/dev/tcp/\",\" eval(\",\" exec(\")\n    | project InstallTime = Timestamp, DeviceId, DeviceName, AccountName,\n              PkgMgr = InitiatingProcessFileName,\n              PkgMgrCmd = InitiatingProcessCommandLine,\n              ChildImage = FileName,\n              ChildCmd = ProcessCommandLine;\nlet Exfil = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackHours)\n    | where RemoteIPType == \"Public\"\n    | where isnotempty(RemoteUrl) or isnotempty(RemoteIP)\n    | where not(RemoteUrl in~ (RegistryHosts))\n    | where not(RemoteUrl endswith \".npmjs.com\" or RemoteUrl endswith \".pythonhosted.org\" or RemoteUrl endswith \".crates.io\" or RemoteUrl endswith \".packagist.org\")\n    | project ExfilTime = Timestamp, DeviceId, RemoteUrl, RemoteIP, RemotePort,\n              ExfilProcess = InitiatingProcessFileName;\nlet CredReads = DeviceFileEvents\n    | where Timestamp > ago(LookbackHours)\n    | where ActionType in (\"FileOpened\",\"FileCreated\",\"FileModified\")\n    | where FolderPath has_any (CredFileTokens) or FileName in~ (\".npmrc\",\".pypirc\",\".env\",\"credentials\",\"auth.json\")\n    | project CredTime = Timestamp, DeviceId,\n              CredFile = strcat(FolderPath, FileName),\n              CredReader = InitiatingProcessFileName;\nInstallExec\n| join kind=inner Exfil on DeviceId\n| where ExfilTime between (InstallTime .. InstallTime + WindowSec * 1s)\n| extend DelaySec = datetime_diff('second', ExfilTime, InstallTime)\n| join kind=leftouter (CredReads) on DeviceId\n| where isempty(CredTime) or CredTime between (InstallTime - 30s .. ExfilTime + 5s)\n| summarize InstallTime = min(InstallTime),\n            ExfilTime = min(ExfilTime),\n            DelaySec = min(DelaySec),\n            CredFiles = make_set(CredFile, 25),\n            RemoteUrls = make_set(RemoteUrl, 10),\n            RemoteIPs = make_set(RemoteIP, 10),\n            SampleChildCmd = any(ChildCmd),\n            SamplePkgMgrCmd = any(PkgMgrCmd)\n            by DeviceName, AccountName, PkgMgr, ChildImage\n| order by InstallTime desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_MANAGER_INSTALL_SPAWNING_OUTBOUND_EGRESS_T",
      "title": "[WEEKLY] Package Manager Install Spawning Outbound Egress to Non-Registry Infrastructure Within 5 Minutes",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the joint pattern across this fortnight's wave of dev-toolchain supply-chain compromises: a package-install command (npm/yarn/pnpm/bun/pip/uv/poetry/cargo or a node/python interpreter invoked by them) is followed within 5 minutes by an outbound public network connection from the same host that does NOT land on a known package registry CDN (npmjs.org, pypi.org / pythonhosted.org, crates.io, github.com/raw.githubusercontent.com, jsdelivr/unpkg/ghcr). This is the post-install detonation step shared by node-ipc 9.1.6/9.2.3/12.0.1 (azurestaticprovider[.]net), Mini Shai-Hulud / TeamPCP across the @antv ecosystem (t.m-kosche[.]com), mistralai 2.4.6 (83.142.209[.]194 / transformers.pyz), guardrails-ai 0.10.1 (git-tanstack[.]com, CVE-2026-45758), @cap-js/sqlite|postgres|db-service (zero.masscan.cloud / 94.154.172[.]43, CVE-2026-46421), the Nx Console VS Code extension breach of GitHub internal repos, Megalodon's 5,561-repo malicious GitHub Actions push (polymarketbot.polymarketdev.workers.dev), and DeepSeek TUI's auto-approved `run_tests` \u2192 cargo test RCE (CVE-2026-45311). Kill-chain coverage: install (the package-manager invocation) into c2/actions (the unsanctioned beacon). FIRES when an npm/pip/yarn-style install (incl. postinstall/preinstall hooks and `cargo test`) is followed by an HTTP/HTTPS/DNS-resolved beacon to a public destination outside the registry allowlist within the temporal window; does NOT fire on installs that only fetch from registry CDNs, on offline `npm ci` against a local mirror, or on installs occurring on a build agent whose registry mirror is allowlisted at the egress proxy.\n\nRationale: Every campaign in scope \u2014 node-ipc, Mini Shai-Hulud/AntV, mistralai, guardrails-ai, @cap-js/*, Nx Console, Megalodon, DeepSeek TUI run_tests \u2014 shares the same observable: a developer-tool install/build/test invocation immediately precedes an outbound connection to attacker infrastructure that is NOT a package registry. Filtering descendants of npm/pip/yarn/cargo/uv/poetry against a registry-CDN allowlist within a 5-minute window catches the chain regardless of which specific IOC, hash, or domain the next compromised package uses.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        },
        {
          "id": "T1204.002",
          "name": "User Execution: Malicious File"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats summariesonly=true min(_time) as install_time values(Processes.process) as install_cmd values(Processes.user) as install_user values(Processes.process_name) as install_proc from datamodel=Endpoint.Processes where Processes.process_name IN (\"npm.exe\",\"npm\",\"npx.exe\",\"npx\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"bun.exe\",\"bun\",\"pip.exe\",\"pip\",\"pip3.exe\",\"pip3\",\"uv.exe\",\"uv\",\"poetry.exe\",\"poetry\",\"cargo.exe\",\"cargo\") AND (Processes.process=\"*install*\" OR Processes.process=\"*add *\" OR Processes.process=\"* test*\" OR Processes.process=\"*preinstall*\" OR Processes.process=\"*postinstall*\" OR Processes.process=\"*ci *\") by Processes.dest Processes.process_guid | `drop_dm_object_name(Processes)` | rename dest as src | join type=inner src [ | tstats summariesonly=true min(_time) as egress_time values(All_Traffic.dest) as c2_dest values(All_Traffic.dest_ip) as c2_ip values(All_Traffic.dest_port) as c2_port values(All_Traffic.app) as egress_app from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_category=\"external\" AND NOT (All_Traffic.dest IN (\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"npmjs.com\",\"pypi.org\",\"files.pythonhosted.org\",\"pypi.python.org\",\"crates.io\",\"static.crates.io\",\"github.com\",\"raw.githubusercontent.com\",\"objects.githubusercontent.com\",\"codeload.github.com\",\"jsdelivr.net\",\"unpkg.com\",\"ghcr.io\",\"yarnpkg.com\") OR All_Traffic.dest IN (\"<registry_allowlist>\")) by All_Traffic.src ] | where egress_time >= install_time AND egress_time <= install_time + 300 | eval delay_sec=egress_time-install_time | stats min(install_time) as firstInstall max(egress_time) as lastEgress dc(c2_dest) as distinct_dests values(c2_dest) as c2_dests values(c2_ip) as c2_ips values(c2_port) as c2_ports values(egress_app) as egress_apps values(install_cmd) as install_cmds values(install_user) as users values(install_proc) as install_procs min(delay_sec) as min_delay by src | where distinct_dests >= 1 | convert ctime(firstInstall) ctime(lastEgress) | sort 0 -firstInstall",
      "defender_kql": "let Lookback = 7d;\nlet WindowSec = 300;\nlet PkgMgrs = dynamic([\"npm.exe\",\"npm\",\"npx.exe\",\"npx\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"bun.exe\",\"bun\",\"pip.exe\",\"pip\",\"pip3.exe\",\"pip3\",\"uv.exe\",\"uv\",\"poetry.exe\",\"poetry\",\"cargo.exe\",\"cargo\"]);\nlet RegistryHosts = dynamic([\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"npmjs.com\",\"yarnpkg.com\",\"pypi.org\",\"files.pythonhosted.org\",\"pypi.python.org\",\"crates.io\",\"static.crates.io\",\"github.com\",\"raw.githubusercontent.com\",\"objects.githubusercontent.com\",\"codeload.github.com\",\"jsdelivr.net\",\"unpkg.com\",\"ghcr.io\"]);\nlet Installs = DeviceProcessEvents\n    | where Timestamp > ago(Lookback)\n    | where FileName in~ (PkgMgrs)\n    | where ProcessCommandLine has_any (\"install\",\"add \",\" test\",\" ci \",\"preinstall\",\"postinstall\")\n    | where AccountName !endswith \"$\"\n    | where AccountName !in~ (\"system\",\"local service\",\"network service\")\n    | project InstallTime = Timestamp, DeviceId, DeviceName, AccountName,\n              InstallProc = FileName, InstallCmd = ProcessCommandLine,\n              InstallPGuid = ProcessUniqueId;\nlet Egress = DeviceNetworkEvents\n    | where Timestamp > ago(Lookback)\n    | where RemoteIPType == \"Public\"\n    | where isnotempty(RemoteUrl) or isnotempty(RemoteIP)\n    | extend RemoteHost = tolower(coalesce(RemoteUrl, RemoteIP))\n    | where not (RemoteHost has_any (RegistryHosts))\n    | where not (RemoteHost endswith \".github.io\")\n    | project EgressTime = Timestamp, DeviceId,\n              EgressInitiator = InitiatingProcessFileName,\n              EgressCmd = InitiatingProcessCommandLine,\n              RemoteIP, RemotePort, RemoteUrl, RemoteHost;\nInstalls\n| join kind=inner Egress on DeviceId\n| where EgressTime between (InstallTime .. InstallTime + WindowSec * 1s)\n| extend DelaySec = datetime_diff('second', EgressTime, InstallTime)\n| summarize FirstInstall = min(InstallTime), LastEgress = max(EgressTime),\n            MinDelay = min(DelaySec), DistinctDests = dcount(RemoteHost),\n            Dests = make_set(RemoteHost, 20),\n            EgressInitiators = make_set(EgressInitiator, 10),\n            SampleInstallCmd = any(InstallCmd), SampleEgressCmd = any(EgressCmd)\n            by DeviceName, AccountName, InstallProc\n| where DistinctDests >= 1\n| order by FirstInstall desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_MANAGER_INSTALL_TIME_INTERPRETER_SPAWN_WIT",
      "title": "[WEEKLY] Package-manager install-time interpreter spawn with credential-file read and outbound egress within 120s",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the joint behaviour pattern behind the current wave of npm and PyPI supply-chain worms (Miasma / Mini Shai-Hulud / IronWorm / binding.gyp / Phantom Gyp): a package-manager process (npm, npm-cli.js, yarn, pnpm, pip, python setup.py, node-gyp) spawns a scripting interpreter (powershell, pwsh, node, python, bash, sh) during install which then reads developer / cloud credential material (.npmrc, ~/.aws/credentials, ~/.ssh/id_*, ~/.docker/config.json, GH_TOKEN/AWS_*/AZURE_* env files) AND opens an outbound connection to a public IP within ~120 seconds on the same host. This fortnight saw multiple linked campaigns drive this chain: 'Miasma Worm Hits 73 Microsoft GitHub Repositories' (THN), 'IronWorm and New Miasma Worm Variant Hit npm' (THN), 'Node-gyp Supply Chain Compromise: A Self-Propagating npm Worm That Hides in binding.gyp' (Snyk), 'Miasma supply chain attack ... @redhat-cloud-services' (Snyk), 'Microsoft's durabletask PyPI Package Compromised' (StepSecurity), GHSA-jpvj-wpmj-h7rv (@cap-js/openapi), and Aikido's two essays on why EDR/proxy miss this class. Kill-chain spans delivery (registry pull) -> install (lifecycle / binding.gyp exec) -> credential-access (T1552.001) -> exfil / C2 (T1567 / T1041). Fires when an install session pulls together all three signals (interp spawn + cred read + egress) within the window; does NOT fire on a plain `npm install` with no shell child, on a developer running `node` outside an install tree, or on package managers contacting the registry alone (registry CIDR allowlisted).\n\nRationale: Every article in scope converges on install-time code execution from a registry package: Miasma/Mini Shai-Hulud via lifecycle scripts and AI-agent hook configs, the node-gyp / binding.gyp worm via gyp-triggered compile, durabletask PyPI via setup.py preinstall, and @cap-js/openapi via preinstall. The constant is package-manager parent -> interpreter child -> credential file read -> egress to non-registry host, all within seconds. Correlating the three signals on the same host inside a 2-minute window catches the joint pattern without depending on any single campaign's rotating IOC list (sfrclak.com / git-service.com / m-kosche.com / masscan.cloud / git-tanstack.com / 160.119.64.3 etc. become substitution slots, not gating filters).",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1546.016",
          "name": "Event Triggered Execution: Installer Packages"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1528",
          "name": "Steal Application Access Token"
        },
        {
          "id": "T1567",
          "name": "Exfiltration Over Web Service"
        },
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats `summariesonly` min(_time) as install_time values(Processes.process) as install_cmd values(Processes.parent_process_name) as parent values(Processes.process_name) as child_proc values(Processes.process_guid) as install_guid from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"npm\",\"npm.exe\",\"node.exe\",\"node\",\"yarn\",\"yarn.exe\",\"pnpm\",\"pnpm.exe\",\"pip\",\"pip.exe\",\"pip3\",\"pip3.exe\",\"python.exe\",\"python\",\"python3\",\"node-gyp\",\"node-gyp.exe\") OR Processes.parent_process IN (\"*npm-cli.js*\",\"*node-gyp*\",\"*setup.py install*\",\"*pip install*\")) AND Processes.process_name IN (\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"wscript.exe\",\"cscript.exe\",\"bash\",\"sh\",\"zsh\",\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"curl.exe\",\"wget.exe\") by host, Processes.user, Processes.process_guid, Processes.parent_process_guid | `drop_dm_object_name(Processes)` | rename process_guid as install_guid | join type=inner host [| tstats `summariesonly` min(_time) as cred_time values(Filesystem.file_path) as cred_paths from datamodel=Endpoint.Filesystem where (Filesystem.file_path IN (\"*\\\\.npmrc\",\"*/.npmrc\",\"*\\\\.aws\\\\credentials\",\"*/.aws/credentials\",\"*\\\\.ssh\\\\id_*\",\"*/.ssh/id_*\",\"*\\\\.docker\\\\config.json\",\"*/.docker/config.json\",\"*\\\\.netrc\",\"*/.netrc\",\"*\\\\.kube\\\\config\",\"*/.kube/config\",\"*\\\\.gitconfig\",\"*/.gitconfig\",\"*Login Data*\",\"*/Cookies\",\"*Local State*\")) by host, Filesystem.process_guid] | join type=inner host [| tstats `summariesonly` min(_time) as net_time values(All_Traffic.dest) as dest values(All_Traffic.dest_port) as dport from datamodel=Network_Traffic.All_Traffic where All_Traffic.action=\"allowed\" AND All_Traffic.dest_category!=\"internal\" AND All_Traffic.dest_port IN (80,443,8080,8443) AND NOT (All_Traffic.dest IN (\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"pypi.org\",\"files.pythonhosted.org\",\"github.com\",\"objects.githubusercontent.com\")) by host] | eval cred_delta=abs(cred_time-install_time), net_delta=abs(net_time-install_time) | where cred_delta<=120 AND net_delta<=120 | table install_time, host, user, parent, child_proc, install_cmd, cred_paths, dest, dport, cred_delta, net_delta | sort - install_time",
      "defender_kql": "let Window = 2m;\nlet PkgMgrParents = dynamic([\"npm\",\"npm.exe\",\"node.exe\",\"yarn\",\"yarn.exe\",\"pnpm\",\"pnpm.exe\",\"pip.exe\",\"pip3.exe\",\"python.exe\",\"python3\",\"node-gyp\",\"node-gyp.exe\"]);\nlet Interpreters = dynamic([\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"wscript.exe\",\"cscript.exe\",\"bash\",\"sh\",\"zsh\",\"node.exe\",\"python.exe\",\"python3\",\"curl.exe\",\"wget.exe\"]);\nlet CredPaths = dynamic([\".npmrc\",\".aws\\\\credentials\",\".aws/credentials\",\"\\\\.ssh\\\\id_\",\"/.ssh/id_\",\".docker\\\\config.json\",\".docker/config.json\",\".netrc\",\"\\\\.kube\\\\config\",\"/.kube/config\",\".gitconfig\",\"Login Data\",\"Local State\"]);\nlet TrustedRegistries = dynamic([\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"pypi.org\",\"files.pythonhosted.org\",\"github.com\",\"objects.githubusercontent.com\",\"raw.githubusercontent.com\",\"codeload.github.com\"]);\nlet Installs = DeviceProcessEvents\n  | where Timestamp > ago(7d)\n  | where InitiatingProcessFileName in~ (PkgMgrParents)\n     or InitiatingProcessCommandLine has_any (\"npm-cli.js\",\"node-gyp\",\"setup.py install\",\"pip install\",\"yarn install\",\"pnpm install\")\n  | where FileName in~ (Interpreters)\n  | where AccountName !endswith \"$\"\n  | project InstallTime = Timestamp, DeviceId, DeviceName, AccountName,\n            Parent = InitiatingProcessFileName, ParentCmd = InitiatingProcessCommandLine,\n            Child = FileName, ChildCmd = ProcessCommandLine, InstallPid = ProcessId;\nlet CredReads = DeviceFileEvents\n  | where Timestamp > ago(7d)\n  | where ActionType in (\"FileCreated\",\"FileModified\",\"FileRenamed\",\"FileAccessed\") or isempty(ActionType)\n  | where FolderPath has_any (CredPaths) or FileName has_any (CredPaths)\n  | project CredTime = Timestamp, DeviceId, CredPath = FolderPath, CredFile = FileName;\nlet Egress = DeviceNetworkEvents\n  | where Timestamp > ago(7d)\n  | where RemoteIPType == \"Public\"\n  | where RemotePort in (80, 443, 8080, 8443)\n  | where not(RemoteUrl has_any (TrustedRegistries))\n  | project NetTime = Timestamp, DeviceId, RemoteIP, RemotePort, RemoteUrl, NetProc = InitiatingProcessFileName;\nInstalls\n| join kind=inner CredReads on DeviceId\n| where CredTime between (InstallTime .. InstallTime + Window)\n| join kind=inner Egress on DeviceId\n| where NetTime between (InstallTime .. InstallTime + Window)\n| summarize CredPaths = make_set(CredPath, 10), RemoteUrls = make_set(RemoteUrl, 10),\n            RemoteIPs = make_set(RemoteIP, 10), CredDeltaSec = min(datetime_diff('second', CredTime, InstallTime)),\n            NetDeltaSec = min(datetime_diff('second', NetTime, InstallTime))\n  by InstallTime, DeviceName, AccountName, Parent, Child, ParentCmd, ChildCmd\n| order by InstallTime desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_MANAGER_LIFECYCLE_HOOK_SPAWNS_NETWORK_FETC",
      "title": "[WEEKLY] Package manager lifecycle hook spawns network-fetching shell or runtime",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the joint TTP behind every npm/PyPI/Composer supply-chain compromise of the last fortnight: a package manager process (npm/npx/yarn/pnpm/node/pip/python/composer) \u2014 invoked through an install or lifecycle hook (preinstall/postinstall) \u2014 spawns a script interpreter or downloader (powershell/pwsh/bash/sh/cmd/curl/wget/bun/bitsadmin/certutil) which then reaches a non-registry public destination within a 5-minute window. This fortnight the pattern drove the Mini Shai-Hulud Wave 4 worm in @tanstack and @antv packages (Snyk, Aikido, StepSecurity, Microsoft), the node-ipc maintainer compromise (Snyk, StepSecurity), the art-template watering-hole backdoor (Cyber Security News), the durabletask PyPI compromise (Microsoft), and the Packagist composer worm (The Hacker News) \u2014 all of which used lifecycle scripts to pull a secondary payload (typically the Bun runtime or a Linux ELF from GitHub Releases) before exfil'ing CI/CD secrets to git-tanstack.com, azurestaticprovider.net, m-kosche.com, git-service.com, or getsession.org. Kill-chain spans install (T1195.002, T1546.016, T1204.002) into c2 (T1071.001, T1105). Fires when a package install's lifecycle script spawns a fetcher AND that host beacons to a public non-registry host within 5 minutes; does NOT fire on interactive developer shells started outside an install context, nor on CI pipelines whose only egress is registry.npmjs.org / pypi.org / files.pythonhosted.org / repo.packagist.org / github.com/oven-sh release URLs that the org has explicitly whitelisted.\n\nRationale: Every article describes the same install-time chain: a compromised package's lifecycle script spawns an interpreter / downloader from inside an npm-pip-composer install context, then beacons to an attacker-controlled (non-registry) host within minutes. Joining the spawn event to a 5-minute non-registry egress window catches Mini Shai-Hulud (Bun fetched from GitHub Releases \u2192 m-kosche / git-tanstack / getsession C2), node-ipc (azurestaticprovider), durabletask (git-service.com), art-template (cfww.shop), and Packagist (parikhpreyash4 release) without depending on specific IOC values that rotate weekly.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1546.016",
          "name": "Event Triggered Execution: Installer Packages"
        },
        {
          "id": "T1204.002",
          "name": "User Execution: Malicious File"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic",
        "Network_Resolution.DNS"
      ],
      "splunk_spl": "| tstats summariesonly=t count min(_time) as spawnTime values(Processes.process) as childCmd values(Processes.parent_process) as parentCmd values(Processes.process_path) as childPath from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"npm.exe\",\"npm-cli.js\",\"node.exe\",\"node\",\"npx.exe\",\"yarn.exe\",\"pnpm.exe\",\"python.exe\",\"python\",\"python3\",\"pip.exe\",\"pip\",\"pip3\",\"composer\",\"composer.phar\") OR Processes.parent_process IN (\"*npm install*\",\"*npm ci*\",\"*npx *\",\"*yarn install*\",\"*pnpm install*\",\"*pip install*\",\"*composer install*\",\"*composer require*\",\"*lifecycle*preinstall*\",\"*lifecycle*postinstall*\")) AND (Processes.process_name IN (\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"bash\",\"bash.exe\",\"sh\",\"sh.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"bun\",\"bun.exe\",\"bitsadmin.exe\",\"certutil.exe\") OR Processes.process=\"*oven-sh/bun*\" OR Processes.process=\"*releases/download*\" OR Processes.process=\"*raw.githubusercontent.com*\") by host Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_path\n| `drop_dm_object_name(Processes)`\n| rename host as src\n| join type=inner src [| tstats summariesonly=t count min(_time) as egressTime values(All_Traffic.dest) as destHost values(All_Traffic.dest) as destIp values(All_Traffic.dest_port) as destPort from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_category!=\"internal\" AND NOT (All_Traffic.dest IN (\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"pypi.org\",\"files.pythonhosted.org\",\"repo.packagist.org\",\"packagist.org\") OR All_Traffic.dest=\"*.npmjs.org\" OR All_Traffic.dest=\"*.pypi.org\") by All_Traffic.src _time | rename All_Traffic.src as src | fields src egressTime destHost destIp destPort]\n| where egressTime>=spawnTime AND egressTime<=relative_time(spawnTime,\"+5m\")\n| stats min(spawnTime) as firstSpawn dc(destHost) as uniqueDests values(destHost) as destHosts values(destIp) as destIps values(parent_process_name) as parents values(process_name) as children values(process) as childCmds by src user\n| where uniqueDests>=1\n| convert ctime(firstSpawn) as firstSpawn\n| sort - firstSpawn",
      "defender_kql": "let LookbackDays = 14d;\nlet WindowMinutes = 5m;\nlet PkgMgrParents = dynamic([\"npm.exe\",\"npm-cli.js\",\"node.exe\",\"npx.exe\",\"yarn.exe\",\"pnpm.exe\",\"python.exe\",\"python3\",\"pip.exe\",\"pip3\",\"composer\",\"composer.phar\"]);\nlet PkgMgrCmdHints = dynamic([\"npm install\",\"npm ci\",\"npx \",\"yarn install\",\"pnpm install\",\"pip install\",\"composer install\",\"composer require\",\"preinstall\",\"postinstall\",\"lifecycle\"]);\nlet SuspChildren = dynamic([\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"bash\",\"bash.exe\",\"sh\",\"sh.exe\",\"curl.exe\",\"wget.exe\",\"bun.exe\",\"bun\",\"bitsadmin.exe\",\"certutil.exe\"]);\nlet RegistryHosts = dynamic([\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"pypi.org\",\"files.pythonhosted.org\",\"repo.packagist.org\",\"packagist.org\"]);\nlet InstallSpawn = DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where (InitiatingProcessFileName in~ (PkgMgrParents))\n         or (InitiatingProcessCommandLine has_any (PkgMgrCmdHints))\n    | where FileName in~ (SuspChildren)\n         or ProcessCommandLine has_any (\"oven-sh/bun\",\"releases/download\",\"raw.githubusercontent.com\",\"curl -s\",\"wget -q\",\"Invoke-WebRequest\",\"DownloadString\")\n    | where AccountName !endswith \"$\"\n    | project SpawnTime=Timestamp, DeviceId, DeviceName, AccountName,\n              ParentImage=InitiatingProcessFolderPath,\n              ParentCmd=InitiatingProcessCommandLine,\n              ChildImage=FolderPath,\n              ChildCmd=ProcessCommandLine,\n              ChildProc=FileName;\nlet Egress = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackDays)\n    | where RemoteIPType == \"Public\"\n    | where isnotempty(RemoteUrl)\n    | where RemoteUrl !in~ (RegistryHosts)\n    | where not(RemoteUrl endswith \".npmjs.org\" or RemoteUrl endswith \".pypi.org\" or RemoteUrl endswith \"pythonhosted.org\" or RemoteUrl endswith \"packagist.org\")\n    | project EgressTime=Timestamp, DeviceId, DeviceName,\n              EgressUrl=RemoteUrl, EgressIP=RemoteIP, EgressPort=RemotePort,\n              EgressInitiator=InitiatingProcessFileName,\n              EgressInitiatorCmd=InitiatingProcessCommandLine;\nInstallSpawn\n| join kind=inner Egress on DeviceId\n| where EgressTime between (SpawnTime .. SpawnTime + WindowMinutes)\n| summarize FirstSpawn=min(SpawnTime),\n            FirstEgress=min(EgressTime),\n            UniqueDestHosts=dcount(EgressUrl),\n            DestHosts=make_set(EgressUrl, 25),\n            DestIPs=make_set(EgressIP, 25),\n            EgressInitiators=make_set(EgressInitiator, 10)\n        by DeviceName, AccountName, ParentCmd, ChildProc, ChildCmd\n| where UniqueDestHosts >= 1     // any non-registry public destination inside the install window\n| extend DelaySec = datetime_diff('second', FirstEgress, FirstSpawn)\n| order by FirstSpawn desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_MANAGER_LIFECYCLE_HOOK_SPAWNS_RUNTIME_WITH",
      "title": "[WEEKLY] Package manager lifecycle hook spawns runtime with outbound egress to non-registry host within 5 minutes",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the joint TTP that every May 2026 supply-chain campaign shares: a package manager (npm/yarn/pnpm, pip, cargo, nuget/dotnet, composer, gem) invokes a preinstall/postinstall lifecycle script that spawns a script-host or downloader child (node, python, sh/bash, powershell, bun, curl/wget, certutil/bitsadmin), and that child \u2014 or an immediate sibling \u2014 initiates an outbound connection to a host that is NOT a known package registry or VCS within ~5 minutes. This fortnight that chain is the engine of Mini Shai-Hulud's @antv burst, the node-ipc beaconing to sh.azurestaticprovider.net, the durabletask PyPI dropper calling git-service.com, TrapDoor's cross-ecosystem (npm/PyPI/Crates) credential theft, vpmdhaj typosquats exfiltrating to aab.sportsontheweb.net, and Microsoft's dependency-confusion recon to moika.tech / cloudplatform-single-spa.io / t-in-one.io (per Snyk 2026-05-18, StepSecurity 2026-05-19, Aikido 2026-05-19, Microsoft 2026-05-20, Unit 42 2026-05-21, THN 2026-05-23/25/27/29, Microsoft 2026-05-29/30). Kill-chain coverage spans delivery (compromised package pulled), install (lifecycle hook fires), and the first C2/exfil hop \u2014 the early-warning window before credential dumping happens. FIRES on developer / CI hosts when an `npm install`-class command produces a script-host child that immediately egresses to a non-registry public host. DOES NOT fire on plain `npm install` traffic to npmjs/yarnpkg/pypi/crates/nuget/rubygems/packagist registries or to github/gitlab/bitbucket VCS, nor on script-host child processes that produce no outbound public connection within the window.\n\nRationale: Every article in this fortnight's set describes the same execution shape \u2014 a tainted package on npm/PyPI/NuGet/Crates/Packagist whose preinstall/postinstall hook spawns a script-host or downloader that immediately phones home (Mini Shai-Hulud \u2192 t.m-kosche.com, node-ipc \u2192 sh.azurestaticprovider.net, durabletask \u2192 git-service.com, TrapDoor \u2192 ddjidd564.github.io, vpmdhaj \u2192 aab.sportsontheweb.net, dependency-confusion \u2192 moika.tech / cloudplatform-single-spa.io / t-in-one.io). Detecting the parent-child-egress triple within a 5-minute window catches the technique regardless of which registry, which package name, or which C2 host this week \u2014 and registry/VCS allow-listing prevents the obvious benign baseline (npm install pulling tarballs from registry.npmjs.org or codeload.github.com) from drowning the signal.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain: Compromise Software Dependencies and Development Tools"
        },
        {
          "id": "T1546.016",
          "name": "Event Triggered Execution: Installer Packages"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1204.002",
          "name": "User Execution: Malicious File"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as proc_time from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"npm.exe\",\"npm\",\"npm-cli.js\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"pip.exe\",\"pip\",\"pip3\",\"pip3.exe\",\"cargo.exe\",\"cargo\",\"nuget.exe\",\"nuget\",\"dotnet.exe\",\"dotnet\",\"composer\",\"composer.phar\",\"gem\",\"bundle\") OR Processes.parent_process IN (\"*npm install*\",\"*npm i *\",\"*yarn install*\",\"*yarn add*\",\"*pnpm install*\",\"*pnpm add*\",\"*pip install*\",\"*cargo install*\",\"*nuget install*\",\"*dotnet add package*\",\"*dotnet restore*\",\"*composer install*\",\"*composer require*\",\"*gem install*\",\"*bundle install*\")) AND Processes.process_name IN (\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"sh\",\"bash\",\"dash\",\"bun.exe\",\"bun\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\",\"certutil.exe\",\"bitsadmin.exe\") AND Processes.user!=\"*$\" AND Processes.user!=\"SYSTEM\" by host Processes.user Processes.parent_process_name Processes.parent_process Processes.process_name Processes.process Processes.process_path Processes.process_id | `drop_dm_object_name(\"Processes\")` | rename host as src, process_id as src_pid | join type=inner src [| tstats `summariesonly` count as conns min(_time) as net_time values(All_Traffic.dest) as dest values(All_Traffic.dest_port) as dest_port values(All_Traffic.app) as net_proc from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_category!=\"internal\" AND All_Traffic.action=\"allowed\" AND NOT (All_Traffic.dest IN (\"*registry.npmjs.org\",\"*registry.yarnpkg.com\",\"*pypi.org\",\"*files.pythonhosted.org\",\"*crates.io\",\"*static.crates.io\",\"*api.nuget.org\",\"*nuget.org\",\"*rubygems.org\",\"*packagist.org\",\"*repo.packagist.org\",\"*github.com\",\"*githubusercontent.com\",\"*gitlab.com\",\"*bitbucket.org\",\"*objects.githubusercontent.com\")) by All_Traffic.src | rename All_Traffic.src as src | `drop_dm_object_name(\"All_Traffic\")`] | eval delta_sec=net_time-proc_time | where delta_sec>=0 AND delta_sec<=300 | stats min(proc_time) as firstTime max(net_time) as lastTime values(parent_process) as parent_cmd values(process) as child_cmd values(process_name) as child_proc values(dest) as remote_dests values(dest_port) as remote_ports dc(dest) as distinct_dests by src, user, parent_process_name | where distinct_dests>=1 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`",
      "defender_kql": "let LookbackHours = 24h;\nlet CorrelationWindowSec = 300;\nlet PkgMgrParents = dynamic([\"npm.exe\",\"npm-cli.js\",\"npm\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"pip.exe\",\"pip\",\"pip3\",\"pip3.exe\",\"cargo.exe\",\"cargo\",\"nuget.exe\",\"nuget\",\"dotnet.exe\",\"dotnet\",\"composer\",\"composer.phar\",\"gem\",\"bundle\",\"node.exe\",\"node\"]);\nlet PkgMgrCmdHints = dynamic([\"npm install\",\"npm i \",\"npm ci\",\"yarn install\",\"yarn add\",\"pnpm install\",\"pnpm add\",\"pip install\",\"pip3 install\",\"cargo install\",\"nuget install\",\"dotnet add package\",\"dotnet restore\",\"composer install\",\"composer require\",\"gem install\",\"bundle install\"]);\nlet HookRuntimes = dynamic([\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"python3.exe\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"sh\",\"bash\",\"dash\",\"bun.exe\",\"bun\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\",\"certutil.exe\",\"bitsadmin.exe\"]);\nlet RegistryAllow = dynamic([\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"pypi.org\",\"files.pythonhosted.org\",\"crates.io\",\"static.crates.io\",\"api.nuget.org\",\"nuget.org\",\"www.nuget.org\",\"rubygems.org\",\"packagist.org\",\"repo.packagist.org\",\"github.com\",\"raw.githubusercontent.com\",\"objects.githubusercontent.com\",\"codeload.github.com\",\"gitlab.com\",\"bitbucket.org\"]);\nlet HookSpawns =\n    DeviceProcessEvents\n    | where Timestamp > ago(LookbackHours)\n    | where (InitiatingProcessFileName in~ (PkgMgrParents))\n         or (InitiatingProcessCommandLine has_any (PkgMgrCmdHints))\n    | where FileName in~ (HookRuntimes)\n    | where AccountName !endswith \"$\"\n    | where AccountName !in~ (\"system\",\"local service\",\"network service\")\n    | project ProcTime = Timestamp, DeviceId, DeviceName, AccountName,\n              ParentImage = InitiatingProcessFolderPath,\n              ParentCmd   = InitiatingProcessCommandLine,\n              ChildImage  = FolderPath,\n              ChildCmd    = ProcessCommandLine,\n              ChildProc   = FileName,\n              ChildPid    = ProcessId;\nlet Egress =\n    DeviceNetworkEvents\n    | where Timestamp > ago(LookbackHours)\n    | where RemoteIPType == \"Public\"\n    | where ActionType in~ (\"ConnectionSuccess\",\"ConnectionAttempted\",\"ConnectionFound\",\"DnsConnectionInspected\")\n    | extend Host = tolower(coalesce(RemoteUrl, \"\"))\n    | where Host == \"\" or not(RegistryAllow has_any (split(Host, \".\")))\n    | where not(Host has_any (RegistryAllow))\n    | project NetTime = Timestamp, DeviceId, RemoteIP, RemoteUrl, RemotePort,\n              NetInitProc = InitiatingProcessFileName,\n              NetInitCmd  = InitiatingProcessCommandLine,\n              NetInitPid  = InitiatingProcessId;\nHookSpawns\n| join kind=inner Egress on DeviceId\n| where NetTime between (ProcTime .. ProcTime + CorrelationWindowSec * 1s)\n| extend DelaySec = datetime_diff('second', NetTime, ProcTime)\n| where DelaySec >= 0 and DelaySec <= CorrelationWindowSec\n| where (NetInitProc =~ ChildProc) or (NetInitPid == ChildPid) or (NetInitProc in~ (HookRuntimes))\n| summarize FirstSeen = min(ProcTime), LastEgress = max(NetTime),\n            EgressCount = count(),\n            DistinctDests = dcount(coalesce(RemoteUrl, tostring(RemoteIP))),\n            Destinations = make_set(coalesce(RemoteUrl, tostring(RemoteIP)), 25),\n            Ports = make_set(RemotePort, 10),\n            SampleParentCmd = any(ParentCmd),\n            SampleChildCmd  = any(ChildCmd),\n            MinDelaySec = min(DelaySec)\n          by DeviceName, AccountName, ParentImage, ChildImage, ChildProc\n| where DistinctDests >= 1\n| order by FirstSeen desc"
    },
    {
      "id": "UC_WEEKLY_PACKAGE_MANAGER_SPAWNS_NETWORK_FETCHING_CHILD_TO_P",
      "title": "[WEEKLY] Package manager spawns network-fetching child to public code-hosting within minutes of install",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the joint pattern across this fortnight's wave of supply-chain compromises: a package manager process (npm, yarn, pnpm, pip, composer, or a `node`/`python` interpreter executing a freshly-installed module) is followed within ~5 minutes on the same host by a child or sibling fetcher process (curl, wget, node, python, bash, powershell) reaching out to a public code-hosting endpoint (github.com release/raw, gist.github.com, objects.githubusercontent.com) or a non-internal first-seen domain. This is the unifying tradecraft behind the mistralai 2.4.6 PyPI dropper, Microsoft's `durabletask` PyPI compromise (Mini Shai-Hulud), the AntV/@beproduct/nestjs-auth npm worm expansion, the `@cap-js/sqlite|postgres|db-service` credential-harvester (CVE-2026-46421), the Packagist GitHub-Releases binary fetcher, and the `mouse5212-super-formatter` Claude-AI exfil package. Kill-chain coverage: delivery (compromised registry version) \u2192 install/exploit (post/pre-install or import-time hook) \u2192 C2/actions (payload pull or token exfil to attacker-controlled GitHub asset or staging domain). It will fire when an `npm install`/`pip install`/`composer require` is immediately followed by a fetcher hitting a public code-hosting URL or a domain unseen in the 30d org baseline; it will NOT fire on routine `git clone`/`git fetch` (no package-manager parent) or on `npm install` against an internal mirror with no outbound fetcher spawn.\n\nRationale: Every article in scope chains the same three primitives \u2014 (1) a package-manager / interpreter process performs an install or import, (2) within seconds a fetcher process makes outbound, and (3) the outbound lands on either a public code-hosting endpoint (GitHub releases/raw/gists, the Packagist+mouse5212+Mini Shai-Hulud staging pattern) or a fresh attacker domain (mistralai 83.142.209.194 / durabletask check.git-service.com / @cap-js zero.masscan.cloud / @beproduct filev2.getsession.org). Binding the pkg-mgr parent to the fetcher child within a 5-minute window catches every variant while leaving routine git clones, mirror installs, and CDN browser traffic out of scope.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain: Compromise Software Dependencies and Development Tools"
        },
        {
          "id": "T1204.002",
          "name": "User Execution: Malicious File"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1567.001",
          "name": "Exfiltration Over Web Service: Exfiltration to Code Repository"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as installTime values(Processes.process) as install_cmd values(Processes.user) as install_user values(Processes.process_name) as install_proc from datamodel=Endpoint.Processes where (Processes.process_name IN (\"npm\",\"npm.exe\",\"npm-cli.js\",\"yarn\",\"yarn.exe\",\"pnpm\",\"pnpm.exe\",\"pip\",\"pip3\",\"pip.exe\",\"python\",\"python3\",\"python.exe\",\"composer\",\"composer.phar\",\"node\",\"node.exe\") OR Processes.parent_process_name IN (\"npm\",\"npm.exe\",\"yarn\",\"yarn.exe\",\"pnpm\",\"pip\",\"pip3\",\"python\",\"python3\",\"composer\",\"node\",\"node.exe\")) AND (Processes.process=\"*install*\" OR Processes.process=\"*postinstall*\" OR Processes.process=\"*preinstall*\" OR Processes.process=\"* add *\" OR Processes.process=\"* ci *\" OR Processes.process=\"* require *\" OR Processes.process=\"*-r requirements*\" OR Processes.process=\"*-c \\\"import *\") by Processes.dest | `drop_dm_object_name(Processes)` | join type=inner dest [ | tstats `summariesonly` count min(_time) as fetchTime values(All_Traffic.dest) as remote_dest values(All_Traffic.dest_ip) as remote_ip values(All_Traffic.app) as fetcher_app values(All_Traffic.dest_port) as remote_port from datamodel=Network_Traffic.All_Traffic where (All_Traffic.app IN (\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"node\",\"node.exe\",\"python\",\"python.exe\",\"python3\",\"powershell.exe\",\"pwsh.exe\",\"sh\",\"bash\") OR All_Traffic.app IN (\"curl\",\"wget\",\"node\",\"python\",\"python3\",\"bash\",\"sh\")) AND (All_Traffic.dest IN (\"github.com\",\"raw.githubusercontent.com\",\"gist.github.com\",\"objects.githubusercontent.com\",\"codeload.github.com\") OR All_Traffic.dest=\"*githubusercontent.com\" OR All_Traffic.dest_category!=\"internal\") AND All_Traffic.action=\"allowed\" by All_Traffic.src | `drop_dm_object_name(All_Traffic)` | rename src as dest ] | eval delay_sec = fetchTime - installTime | where delay_sec >= 0 AND delay_sec <= 300 | eval iocs_hash=\"<sha256_list>\", iocs_dns=\"<c2_domain_list>\" | table installTime, fetchTime, delay_sec, dest, install_user, install_proc, install_cmd, fetcher_app, remote_dest, remote_ip, remote_port",
      "defender_kql": "let LookbackHours = 24h;\nlet WindowSeconds = 300;\nlet PkgMgrs = dynamic([\"npm.exe\",\"npm\",\"npm-cli.js\",\"yarn.exe\",\"yarn\",\"pnpm.exe\",\"pnpm\",\"pip.exe\",\"pip\",\"pip3\",\"python.exe\",\"python\",\"python3\",\"composer\",\"composer.phar\",\"node.exe\",\"node\"]);\nlet Fetchers = dynamic([\"curl.exe\",\"curl\",\"wget.exe\",\"wget\",\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"powershell.exe\",\"pwsh.exe\",\"sh\",\"bash\"]);\nlet CodeHostHosts = dynamic([\"github.com\",\"raw.githubusercontent.com\",\"gist.github.com\",\"objects.githubusercontent.com\",\"codeload.github.com\"]);\nlet Installs = DeviceProcessEvents\n    | where Timestamp > ago(LookbackHours)\n    | where AccountName !endswith \"$\"\n    | where (FileName in~ (PkgMgrs)) or (InitiatingProcessFileName in~ (PkgMgrs))\n    | where ProcessCommandLine has_any (\" install\",\" add \",\" ci \",\" require \",\"postinstall\",\"preinstall\",\"pip install\",\"-r requirements\",\"-c \\\"import\",\"composer require\",\"npm i \")\n    | project InstallTime = Timestamp, DeviceId, DeviceName, AccountName,\n              InstallProc = FileName, InstallParent = InitiatingProcessFileName,\n              InstallCmd = ProcessCommandLine;\nlet Fetches = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackHours)\n    | where InitiatingProcessFileName in~ (Fetchers)\n    | where RemoteIPType == \"Public\"\n    | where RemoteUrl has_any (CodeHostHosts)\n         or RemoteUrl matches regex @\"(?i)/releases/download/|/raw/|/archive/refs/\"\n         or isnotempty(RemoteUrl)\n    | project FetchTime = Timestamp, DeviceId, DeviceName,\n              FetcherProc = InitiatingProcessFileName,\n              FetcherCmd = InitiatingProcessCommandLine,\n              RemoteUrl, RemoteIP, RemotePort;\nlet BaselineDomains = DeviceNetworkEvents\n    | where Timestamp between (ago(30d) .. ago(LookbackHours))\n    | where isnotempty(RemoteUrl)\n    | summarize OrgHosts = dcount(DeviceId) by RemoteUrl\n    | where OrgHosts > 5;\nInstalls\n| join kind=inner Fetches on DeviceId\n| where FetchTime between (InstallTime .. InstallTime + WindowSeconds * 1s)\n| extend DelaySec = datetime_diff('second', FetchTime, InstallTime)\n| join kind=leftanti BaselineDomains on RemoteUrl   // rare-by-org OR codehost (codehost retained below)\n| extend IsCodeHost = RemoteUrl has_any (CodeHostHosts)\n                  or RemoteUrl matches regex @\"(?i)/releases/download/|/raw/|/archive/refs/\"\n| where IsCodeHost or DelaySec <= 60   // codehost = high signal at any delay; first-seen domain = tight window\n| project InstallTime, FetchTime, DelaySec, DeviceName, AccountName,\n          InstallParent, InstallProc, InstallCmd,\n          FetcherProc, RemoteUrl, RemoteIP, RemotePort, IsCodeHost\n| order by InstallTime desc"
    },
    {
      "id": "UC_WEEKLY_POST_AUTH_PRIVILEGE_BOUNDARY_CROSSING_ON_EDGE_MANA",
      "title": "[WEEKLY] Post-Auth Privilege Boundary Crossing on Edge/Management Appliances (low-priv -> admin within 10m)",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects a single source IP + user-agent that, within a ~10 minute window, transitions from hitting a low-privilege authenticated endpoint (registration, onboard-customer, login, peering, auth API) to executing admin-only HTTP verbs (POST/PUT/PATCH/DELETE) against admin paths (/admin*, /api/admin*, /configuration*, /vmanage*, authorized_keys, or the Grav-style data[access][admin]/data[groups] injection markers) on the same management plane. This is the joint TTP across this fortnight's wave of post-auth privilege-escalation flaws: CVE-2026-42613 (Grav Login plugin missing server-side validation of groups/access), CVE-2026-6973 (Ivanti EPMM remotely-authenticated admin RCE), CVE-2026-20127/20182 (Cisco Catalyst SD-WAN Controller auth bypass \u2014 added to CISA KEV with confirmed in-the-wild exploitation), and GHSA-9h64-2846-7x7f (Axonflow multi-tenant isolation / access-control bypass). It spans the exploit phase between initial low-privilege authentication and the install phase (web shell drop, account creation, SSH key write); firing here catches the attacker before the persistence handoff. Will fire on the rapid low-priv->admin pivot pattern characteristic of these CVEs; will NOT fire when an admin session is preceded by an admin-tier authentication event from an allowlisted jump host, when only one stage is present, or on background management traffic that lacks the admin verb on a sensitive path.\n\nRationale: All four post-auth privilege-escalation vulns this fortnight (Grav data[groups] injection, Ivanti EPMM auth'd-to-admin, Cisco SD-WAN auth bypass, Axonflow tenant-isolation) share the same wire signature: a single client transitions from a low-privilege authenticated request to an admin-tier write within minutes. Detecting the temporal pivot \u2014 not any specific CVE's URL \u2014 survives IOC rotation and catches the un-patched variants likely to follow.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1068",
          "name": "Exploitation for Privilege Escalation"
        },
        {
          "id": "T1078",
          "name": "Valid Accounts"
        },
        {
          "id": "T1136.001",
          "name": "Create Account: Local Account"
        },
        {
          "id": "T1098.004",
          "name": "Account Manipulation: SSH Authorized Keys"
        },
        {
          "id": "T1505.003",
          "name": "Server Software Component: Web Shell"
        }
      ],
      "data_models": [
        "Web",
        "Network_Traffic.All_Traffic",
        "Authentication"
      ],
      "splunk_spl": "| tstats summariesonly=true count from datamodel=Web where Web.http_method IN (\"POST\",\"PUT\",\"PATCH\",\"DELETE\") AND Web.status<400 by Web.src Web.dest Web.http_user_agent Web.uri_path Web.url _time | `drop_dm_object_name(Web)` | eval req=coalesce(url,uri_path) | eval stage=case(match(req,\"(?i)(user_register|/register|onboard-customer|/peering|/auth/login|/login\\.action|/api/v1/auth|/sessions/new)\"),\"low_priv\", match(req,\"(?i)(/admin|/api/admin|data(\\[|%5B)access(\\]|%5D)(\\[|%5B)admin|data(\\[|%5B)groups|/configuration|authorized_keys|/installer|/vmanage|/system/setting|/users/[0-9]+/roles)\"),\"admin\", 1=1,\"other\") | where stage IN (\"low_priv\",\"admin\") | sort 0 src dest http_user_agent _time | streamstats current=t window=500 time_window=10m latest(eval(if(stage=\"low_priv\",req,null))) as lowpriv_req latest(eval(if(stage=\"low_priv\",_time,null))) as lowpriv_ts by src dest http_user_agent | where stage=\"admin\" AND isnotnull(lowpriv_ts) AND _time>lowpriv_ts | eval delta_sec=_time-lowpriv_ts | where delta_sec<=600 | stats min(lowpriv_ts) as firstLowPriv max(_time) as adminEventTime values(lowpriv_req) as lowpriv_paths values(req) as admin_paths max(delta_sec) as maxDeltaSec count by src dest http_user_agent | convert ctime(firstLowPriv) ctime(adminEventTime) | search NOT [| inputlookup mgmt_admin_jumphosts.csv | fields src]",
      "defender_kql": "// Best-effort hunt \u2014 DeviceNetworkEvents only sees URLs on inspected HTTP traffic; HTTPS surfaces SNI only. Pair with web-access logs in Sentinel for full coverage.\nlet WindowMin = 10m;\nlet LowPrivPatterns = dynamic([\"user_register\",\"/register\",\"onboard-customer\",\"/peering\",\"/auth/login\",\"/login.action\",\"/api/v1/auth\",\"/sessions/new\"]);\nlet AdminPatterns   = dynamic([\"/admin\",\"/api/admin\",\"data[access][admin]\",\"data%5Baccess%5D%5Badmin%5D\",\"data[groups]\",\"data%5Bgroups%5D\",\"/configuration\",\"authorized_keys\",\"/installer\",\"/vmanage\",\"/system/setting\"]);\nDeviceNetworkEvents\n| where Timestamp > ago(7d)\n| where ActionType in (\"ConnectionSuccess\",\"HttpConnectionInspected\")\n| where isnotempty(RemoteUrl)\n| extend stage = case(\n    RemoteUrl has_any (LowPrivPatterns), \"low_priv\",\n    RemoteUrl has_any (AdminPatterns),   \"admin\",\n    \"other\")\n| where stage in (\"low_priv\",\"admin\")\n| project Timestamp, DeviceName, DeviceId, RemoteIP, RemotePort, RemoteUrl, InitiatingProcessFileName, stage\n| order by RemoteIP asc, Timestamp asc\n| serialize\n| extend PrevStage=prev(stage), PrevTime=prev(Timestamp), PrevUrl=prev(RemoteUrl), PrevIP=prev(RemoteIP)\n| where stage == \"admin\" and PrevStage == \"low_priv\" and RemoteIP == PrevIP\n| where datetime_diff('minute', Timestamp, PrevTime) <= 10\n| project AdminEventTime=Timestamp, LowPrivEventTime=PrevTime,\n          DeltaMinutes=datetime_diff('minute', Timestamp, PrevTime),\n          DeviceName, RemoteIP, RemotePort, LowPrivUrl=PrevUrl, AdminUrl=RemoteUrl, InitiatingProcessFileName\n| order by AdminEventTime desc"
    },
    {
      "id": "UC_WEEKLY_PUBLIC_FACING_APP_RUNTIME_SPAWNS_SHELL_LOLBIN_OR_C",
      "title": "[WEEKLY] Public-Facing App Runtime Spawns Shell, LOLBin, or Container-Control Tool",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects an internet-facing application runtime \u2014 Node.js, Python/Jupyter, PHP-FPM, IIS w3wp, nginx, httpd, caddy, ruby, or java \u2014 spawning a shell, LOLBin, or container-control binary, optionally with a recon/privilege-escalation keyword in the child command line. This is the common post-condition shared across the fortnight's CRITICAL/KEV catch: PAN-OS GlobalProtect auth bypass under active exploitation (CISA KEV CVE-2026-0257; The Hacker News follow-up), three independent vm2 sandbox escapes (CVE-2026-47137 patch bypass, CVE-2026-47140 NodeVM denylist bypass, CVE-2026-47210 JSPI Promise species bypass), the Jupyter Enterprise Gateway KERNEL_UID/GID enforcement bypass (CVE-2026-44180), and the Shopper RBAC authorization bypass (CVE-2026-47744). Every one of these CVEs lets an unauthenticated/low-privileged HTTP touch defeat an auth/authz/sandbox check; the only host-visible signal the bypasses share is the privilege-boundary crossing that follows \u2014 a web/app process suddenly executing a shell, container-control tool, or LOLBin. The query spans the exploit and install stages by joining a parent in the app-runtime set to a child in the shell/LOLBin/container-control set on the same host. It fires when the bypass is converted to host action and does NOT fire on benign in-runtime behaviour (a Node API calling git, a PHP composer install, a Python wheel build invoking gcc) because none of those children are in the suspicious-child set; CI build agents and DevOps tooling running as app runtimes are the main FP source and should be allowlisted by hostname.\n\nRationale: Across the seven articles the only shared host-visible signal is the privilege-boundary crossing that follows the auth/authz/sandbox bypass: a web/app runtime suddenly running a shell, LOLBin, or container-control tool. Pivoting on the parent\u21c4child pair (instead of any single vm2/PAN-OS/Jupyter/Shopper IOC) catches the joint pattern and survives IOC rotation.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        },
        {
          "id": "T1611",
          "name": "Escape to Host"
        },
        {
          "id": "T1068",
          "name": "Exploitation for Privilege Escalation"
        },
        {
          "id": "T1610",
          "name": "Deploy Container"
        }
      ],
      "data_models": [
        "Endpoint.Processes"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdlines values(Processes.process_path) as child_paths values(Processes.user) as users values(Processes.parent_process) as parent_cmd from datamodel=Endpoint.Processes where (Processes.parent_process_name IN (\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"jupyter\",\"jupyter-kernel-launcher\",\"php-fpm.exe\",\"php-fpm\",\"php.exe\",\"php\",\"w3wp.exe\",\"nginx.exe\",\"nginx\",\"httpd.exe\",\"httpd\",\"caddy.exe\",\"caddy\",\"ruby.exe\",\"ruby\",\"java.exe\",\"java\")) AND (Processes.process_name IN (\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh\",\"bash\",\"dash\",\"zsh\",\"ash\",\"ksh\",\"wmic.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"wscript.exe\",\"cscript.exe\",\"certutil.exe\",\"bitsadmin.exe\",\"curl.exe\",\"wget.exe\",\"kubectl\",\"kubectl.exe\",\"docker\",\"docker.exe\",\"crictl\",\"crictl.exe\",\"nsenter\",\"unshare\")) AND Processes.user!=\"*$\" by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process | `drop_dm_object_name(Processes)` | eval has_recon=if(match(process,\"(?i)(whoami|\\bid\\b|uname|hostname|ipconfig|ifconfig|/etc/passwd|/etc/shadow|net user|net group|child_process|execSync)\"),1,0), has_priv=if(match(process,\"(?i)(--privileged|runAsUser=0|runAsGroup=0|KERNEL_UID=0|KERNEL_GID=0|\\b-uid\\s+0\\b|\\b-gid\\s+0\\b|setuid\\(0\\)|chmod\\s+\\+s)\"),1,0), node_or_jup=if(match(parent_process_name,\"(?i)(node|jupyter|python)\"),1,0) | where has_recon=1 OR has_priv=1 OR node_or_jup=1 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | table firstTime lastTime dest user parent_process_name process_name process has_recon has_priv",
      "defender_kql": "let AppRuntimes = dynamic([\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"jupyter\",\"jupyter-kernel-launcher\",\"php-fpm.exe\",\"php-fpm\",\"php.exe\",\"php\",\"w3wp.exe\",\"nginx.exe\",\"nginx\",\"httpd.exe\",\"httpd\",\"caddy.exe\",\"caddy\",\"ruby.exe\",\"ruby\",\"java.exe\",\"java\"]);\nlet SuspChildren = dynamic([\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh\",\"bash\",\"dash\",\"zsh\",\"ash\",\"ksh\",\"wmic.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"wscript.exe\",\"cscript.exe\",\"certutil.exe\",\"bitsadmin.exe\",\"curl.exe\",\"wget.exe\",\"kubectl\",\"kubectl.exe\",\"docker\",\"docker.exe\",\"crictl\",\"crictl.exe\",\"nsenter\",\"unshare\"]);\nlet ReconRegex = @\"(?i)(whoami|\\bid\\b|uname|hostname|ipconfig|ifconfig|/etc/passwd|/etc/shadow|\\bnet user\\b|\\bnet group\\b|child_process|execSync)\";\nlet PrivRegex  = @\"(?i)(--privileged|runAsUser=0|runAsGroup=0|KERNEL_UID=0|KERNEL_GID=0|\\b-uid\\s+0\\b|\\b-gid\\s+0\\b|setuid\\(0\\)|chmod\\s+\\+s)\";\nDeviceProcessEvents\n| where Timestamp > ago(7d)\n| where InitiatingProcessFileName in~ (AppRuntimes)\n| where FileName in~ (SuspChildren)\n| where AccountName !endswith \"$\"\n| extend HasRecon = ProcessCommandLine matches regex ReconRegex\n| extend HasPrivEscalation = ProcessCommandLine matches regex PrivRegex\n| extend ScriptingRuntimeParent = InitiatingProcessFileName in~ (\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"jupyter\",\"jupyter-kernel-launcher\")\n| where HasRecon or HasPrivEscalation or ScriptingRuntimeParent\n| summarize EventCount=count(), FirstSeen=min(Timestamp), LastSeen=max(Timestamp), ParentCmd=any(InitiatingProcessCommandLine), ChildCmds=make_set(ProcessCommandLine, 10), Users=make_set(AccountName, 5), IsRemoteSession=any(IsInitiatingProcessRemoteSession) by DeviceName, InitiatingProcessFileName, FileName, HasRecon, HasPrivEscalation\n| order by LastSeen desc"
    },
    {
      "id": "UC_WEEKLY_SCRIPT_INTERPRETER_OR_PACKAGE_INSTALL_HOOK_EGRESS_",
      "title": "[WEEKLY] Script Interpreter or Package-Install Hook Egress to Free-Tier Edge SaaS Within 5 Minutes of Process Start",
      "kill_chain": "c2",
      "confidence": "High",
      "description": "Detects a script-host or package-manager process (node, npm/npx/yarn, python, ruby, mshta, wscript, cscript, powershell/pwsh, bash) opening an outbound connection to free-tier edge SaaS that adversaries are increasingly using as living-off-trusted-services (LOTS) staging / C2 / exfiltration channels \u2014 *.workers.dev, *.trycloudflare.com, *.supabase.co, *.pages.dev, *.web.app, *.r2.dev, *.vercel.app, raw.githubusercontent.com, gist.githubusercontent.com \u2014 within five minutes of the process spawning. This fortnight's driver is the convergence of three campaigns onto this same delivery topology: Gamaredon's WinRAR-delivered GammaPhish HTA payloads call back to bold.zsjtn41091.workers.dev, efficiency-planes-emotions-fascinating.trycloudflare.com and iiwdsxwamylbwwsoyrmj.supabase.co (\"Gamaredon Exploits WinRAR to Deliver GammaWorm and GammaSteel Against Ukraine\"); the @redhat-cloud-services npm supply-chain compromise exfiltrates credentials from preinstall hooks to attacker-controlled code repositories (\"Preinstall to persistence: Inside the Red Hat npm Miasma credential-stealing campaign\"); and TA4922's ValleyRAT/Atlas RAT downloaders use the same disposable CDN frontends to stage second-stage payloads across the U.K., Germany, Italy and South Africa (\"China-Linked TA4922 Expands Phishing Attacks to U.K., Germany, Italy, and South Africa\"). Adjacent CVE-driven RCEs that drop into a Node interpreter \u2014 e.g. the LiquidJS template-injection chain (\"GHSA CVE-2026-45618: LiquidJS is Vulnerable to Remote Code Execution\") \u2014 also land squarely in this detection because the post-exploitation interpreter is the egress source. Spans the install \u2192 C2 \u2192 exfiltration kill-chain phases. It will fire when an interpreter or package manager makes its first egress to one of these tenant-style domains within the correlation window; it will NOT fire on browsers / curl / wget hitting these CDNs (intentionally excluded \u2014 those are dominant legitimate consumers) nor on cached/baseline domains the org already routinely calls.\n\nRationale: All three live campaigns this fortnight (Gamaredon GammaPhish HTA, Red Hat npm Miasma preinstall credential exfil, TA4922 ValleyRAT/Atlas RAT staging) and any post-exploitation drop from interpreter RCE chains like LiquidJS converge on the same delivery topology: a script host or package-install hook reaches a disposable free-tier edge SaaS tenant within seconds of spawning. Joining DeviceProcessEvents to DeviceNetworkEvents on (DeviceId, ProcessId) within a 5-minute window catches the joint TTP without depending on any one campaign's rotating IOC set, while the install-context flag separates supply-chain abuse from interactive runtime abuse for triage.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1102.002",
          "name": "Web Service: Bidirectional Communication"
        },
        {
          "id": "T1567.001",
          "name": "Exfiltration Over Web Service: Exfiltration to Code Repository"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        },
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic",
        "Web.Web"
      ],
      "splunk_spl": "| tstats summariesonly=true count min(_time) as proc_time values(Processes.process) as cmd values(Processes.parent_process_name) as parent_name values(Processes.parent_process) as parent_cmd values(Processes.user) as user from datamodel=Endpoint.Processes where Processes.process_name IN (\"node.exe\",\"npm.cmd\",\"npx.cmd\",\"yarn.cmd\",\"python.exe\",\"python3.exe\",\"ruby.exe\",\"mshta.exe\",\"wscript.exe\",\"cscript.exe\",\"powershell.exe\",\"pwsh.exe\",\"bash.exe\",\"wsl.exe\") by host Processes.process_name Processes.process_id | `drop_dm_object_name(\"Processes\")` | rename process_id as proc_id | join type=inner host proc_id [| tstats summariesonly=true min(_time) as net_time values(All_Traffic.dest) as dest values(All_Traffic.dest_port) as dest_port from datamodel=Network_Traffic.All_Traffic where (All_Traffic.dest=\"*.workers.dev\" OR All_Traffic.dest=\"*.trycloudflare.com\" OR All_Traffic.dest=\"*.supabase.co\" OR All_Traffic.dest=\"*.pages.dev\" OR All_Traffic.dest=\"*.web.app\" OR All_Traffic.dest=\"*.r2.dev\" OR All_Traffic.dest=\"*.vercel.app\" OR All_Traffic.dest=\"raw.githubusercontent.com\" OR All_Traffic.dest=\"gist.githubusercontent.com\" OR All_Traffic.dest=\"*.replit.app\" OR All_Traffic.dest=\"*.glitch.me\") by host All_Traffic.process_id | `drop_dm_object_name(\"All_Traffic\")` | rename process_id as proc_id] | eval delay_sec = net_time - proc_time | where delay_sec >= 0 AND delay_sec <= 300 | eval install_context = if(match(cmd,\"(?i)preinstall|postinstall|npm\\s+install|yarn\\s+add|pip\\s+install|gem\\s+install|cargo\\s+install|setup\\.py\"),1,0) | table proc_time delay_sec host user process_name cmd parent_name parent_cmd dest dest_port install_context | sort - proc_time",
      "defender_kql": "let LookbackHours = 24h;\nlet CorrelationWindow = 5m;     // empirical: legitimate post-install fetches usually complete in <5m, beyond that the parent/child PID linkage gets noisy\nlet ScriptHosts = dynamic([\"node.exe\",\"npm.cmd\",\"npx.cmd\",\"yarn.cmd\",\"python.exe\",\"python3.exe\",\"ruby.exe\",\"mshta.exe\",\"wscript.exe\",\"cscript.exe\",\"powershell.exe\",\"pwsh.exe\",\"cmd.exe\",\"bash.exe\",\"wsl.exe\"]);\nlet EdgeDomains = dynamic([\".workers.dev\",\".trycloudflare.com\",\".supabase.co\",\".pages.dev\",\".web.app\",\".r2.dev\",\".vercel.app\",\".replit.app\",\".glitch.me\",\".onrender.com\",\"raw.githubusercontent.com\",\"gist.githubusercontent.com\",\"bitbucket.org/raw\"]);\nlet InstallHints = dynamic([\"preinstall\",\"postinstall\",\"install.js\",\"setup.py\",\"pip install\",\"npm install\",\"yarn add\",\"gem install\",\"cargo install\"]);\nlet ProcStart = DeviceProcessEvents\n    | where Timestamp > ago(LookbackHours)\n    | where FileName in~ (ScriptHosts)\n    | where AccountName !endswith \"$\"\n    | where AccountName !in~ (\"system\",\"local service\",\"network service\")\n    | project ProcTime = Timestamp, DeviceId, DeviceName, AccountName,\n              ParentImage = InitiatingProcessFolderPath,\n              ParentCmd   = InitiatingProcessCommandLine,\n              ScriptHost  = FileName,\n              ScriptCmd   = ProcessCommandLine,\n              ProcSHA256  = SHA256,\n              ProcId      = ProcessId,\n              IsRemoteSession = IsInitiatingProcessRemoteSession;\nlet NetEgress = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackHours)\n    | where RemoteIPType == \"Public\"\n    | where isnotempty(RemoteUrl)\n    | where RemoteUrl has_any (EdgeDomains)\n    | project NetTime = Timestamp, DeviceId, RemoteUrl, RemoteIP, RemotePort,\n              EgressProc = InitiatingProcessFileName,\n              EgressProcId = InitiatingProcessId;\nProcStart\n| join kind=inner NetEgress on DeviceId, $left.ProcId == $right.EgressProcId\n| where NetTime between (ProcTime .. ProcTime + CorrelationWindow)\n| extend DelaySec = datetime_diff('second', NetTime, ProcTime)\n| extend InstallContext = iff(ParentCmd has_any (InstallHints) or ScriptCmd has_any (InstallHints), \"install-hook\", \"runtime\")\n| summarize FirstSeen = min(ProcTime), LastSeen = max(NetTime), HitCount = count(),\n            DistinctDomains = make_set(RemoteUrl, 25),\n            SampleScriptCmd = any(ScriptCmd),\n            SampleParentCmd = any(ParentCmd),\n            arg_min(DelaySec, *)\n            by DeviceName, AccountName, ScriptHost, InstallContext\n| order by FirstSeen desc"
    },
    {
      "id": "UC_WEEKLY_SELF_HOSTED_APPLICATION_SERVICE_SPAWNS_SHELL_OR_SS",
      "title": "[WEEKLY] Self-hosted application service spawns shell or SSH within seconds of inbound unauthenticated API write",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects the recurring pattern across this fortnight's GHSA-Critical drop where a self-hosted Go/Node/Python/PHP service receives an unauthenticated (or low-privileged) POST/PATCH/PUT to an internal API path and within ~120s the same service process spawns a shell, ssh, or scripting interpreter child \u2014 the universal smoking-gun of in-process RCE primitives. This catches the chain in Nezha cron-RCE (CVE-2026-46716), Kopia SSH ProxyCommand injection (CVE-2026-45695), Algernon handler.lua escape (CVE-2026-45721), Fission router unauthenticated function invoke (CVE-2026-46614), Twig {% use %} PHP injection (CVE-2026-46633), FileBrowser Quantum path-traversal PATCH (GHSA-qqqm-5547-774x), Boxlite OCI symlink escape (CVE-2026-46703), YesWiki Bazar SQLi follow-on (CVE-2026-46670), zrok ProxyShare SSRF (CVE-2026-45568), and the @cap-js / guardrails-ai supply-chain stage-2 payloads (CVE-2026-46421, CVE-2026-45758). It spans Delivery (the HTTP request) into Exploit / Execute (the unexpected interpreter child). It fires when one of the listed service binaries is the immediate parent of /bin/sh, ssh, powershell, python, etc., correlated to an inbound listener connection in the prior 120s \u2014 it does NOT fire on the service's normal supervisor/health-check child processes, on container-runtime forks (containerd-shim, runc), or when the parent is a CI/dev image (`*-dev`, `*-test`).\n\nRationale: Every CVE in this fortnight's drop ultimately turns an unauthenticated/low-priv HTTP write into either a process spawn under the service or a shell-fork primitive \u2014 joining inbound listener traffic to an unexpected interpreter child under the same parent service binary, within a tight window, fires on the joint behaviour and stays useful as IOCs rotate.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1505.003",
          "name": "Server Software Component: Web Shell"
        },
        {
          "id": "T1133",
          "name": "External Remote Services"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic",
        "Web"
      ],
      "splunk_spl": "| tstats summariesonly=t count min(_time) as proc_time values(Processes.process) as child_cmd values(Processes.process_path) as child_path values(Processes.user) as proc_user from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"nezha\",\"nezha-dashboard\",\"kopia\",\"kopia.exe\",\"kopia-ui\",\"kopia-ui.exe\",\"algernon\",\"filebrowser\",\"fission-router\",\"fission-bundle\",\"boxlite\",\"zrok\",\"yeswiki\",\"php-fpm\",\"php-fpm8.1\",\"php-fpm8.2\",\"php-fpm8.3\",\"httpd\",\"apache2\",\"node\",\"node.exe\",\"python\",\"python3\",\"python.exe\") AND Processes.process_name IN (\"sh\",\"bash\",\"dash\",\"zsh\",\"ash\",\"ssh\",\"ssh.exe\",\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"perl\",\"python\",\"python3\",\"ruby\",\"node\",\"curl\",\"wget\",\"nc\",\"ncat\",\"socat\") AND Processes.parent_process_name!=\"*-dev\" AND Processes.parent_process_name!=\"*-test\" by host, Processes.parent_process_name, Processes.process_name, Processes.process, Processes.user, Processes.process_id, Processes.parent_process_id\n| `drop_dm_object_name(Processes)`\n| eval window_start = proc_time - 120, window_end = proc_time + 5\n| join type=inner host [ | tstats summariesonly=t count as inbound_hits values(All_Traffic.src) as src_ip values(All_Traffic.dest_port) as listener_port values(All_Traffic.transport) as transport from datamodel=Network_Traffic.All_Traffic where All_Traffic.direction=\"inbound\" AND All_Traffic.dest_port IN (80,443,2222,3000,4242,5000,8000,8080,8081,8443,8888,9000,9090) by All_Traffic.dest, All_Traffic.dest_port, All_Traffic.src, _time | `drop_dm_object_name(All_Traffic)` | rename dest as host, _time as inbound_time ]\n| where inbound_time >= window_start AND inbound_time <= window_end\n| eval delta_seconds = proc_time - inbound_time\n| stats min(inbound_time) as first_inbound, max(proc_time) as child_spawn_time, values(src_ip) as remote_src_ips, values(listener_port) as listener_ports, values(parent_process_name) as vulnerable_service, values(process_name) as child_binary, values(process) as child_cmdline, values(user) as run_as, dc(src_ip) as distinct_src_ips, count as event_count by host, parent_process_id, process_id\n| eval delay_sec = round(child_spawn_time - first_inbound, 1)\n| where delay_sec <= 120\n| convert ctime(first_inbound) ctime(child_spawn_time)\n| sort 0 - child_spawn_time\n| table child_spawn_time, host, vulnerable_service, child_binary, child_cmdline, run_as, listener_ports, remote_src_ips, distinct_src_ips, delay_sec",
      "defender_kql": "// Self-hosted niche service spawns shell/ssh/interpreter within 120s of inbound API listener hit\nlet Lookback = 7d;\nlet WindowSec = 120;\nlet VulnerableServiceParents = dynamic([\n  \"nezha\",\"nezha-dashboard\",\"kopia\",\"kopia.exe\",\"kopia-ui\",\"kopia-ui.exe\",\n  \"algernon\",\"filebrowser\",\"fission-router\",\"fission-bundle\",\"boxlite\",\n  \"zrok\",\"yeswiki\",\"php-fpm\",\"php-fpm8.1\",\"php-fpm8.2\",\"php-fpm8.3\",\n  \"httpd\",\"apache2\",\"node\",\"node.exe\",\"python\",\"python3\",\"python.exe\"\n]);\nlet SuspiciousChildren = dynamic([\n  \"sh\",\"bash\",\"dash\",\"zsh\",\"ash\",\"ssh\",\"ssh.exe\",\n  \"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\n  \"perl\",\"ruby\",\"curl\",\"wget\",\"nc\",\"ncat\",\"socat\"\n]);\nlet ListenerPorts = dynamic([80,443,2222,3000,4242,5000,8000,8080,8081,8443,8888,9000,9090]);\nlet WebShellChildren = DeviceProcessEvents\n  | where Timestamp > ago(Lookback)\n  | where tolower(InitiatingProcessFileName) in (VulnerableServiceParents)\n  | where tolower(FileName) in (SuspiciousChildren)\n  | where InitiatingProcessFileName !endswith \"-dev\" and InitiatingProcessFileName !endswith \"-test\"\n  | where AccountName !endswith \"$\"\n  | where not(InitiatingProcessCommandLine has_any (\"healthcheck\",\"--version\",\"/healthz\"))\n  | extend ChildTime = Timestamp,\n           VulnService = InitiatingProcessFileName,\n           ChildBin    = FileName,\n           ChildCmd    = ProcessCommandLine,\n           ParentPid   = InitiatingProcessId\n  | project DeviceId, DeviceName, AccountName, ChildTime, VulnService, ChildBin, ChildCmd, ParentPid,\n           InitiatingProcessFolderPath, FolderPath, SHA256;\nlet InboundHits = DeviceNetworkEvents\n  | where Timestamp > ago(Lookback)\n  | where ActionType in (\"InboundConnectionAccepted\",\"ConnectionInbound\",\"ListeningConnectionCreated\")\n  | where LocalPort in (ListenerPorts) or RemotePort in (ListenerPorts)\n  | extend InboundTime = Timestamp,\n           RemoteSrc   = tostring(RemoteIP),\n           ListenerPort = coalesce(LocalPort, RemotePort)\n  | project DeviceId, InboundTime, RemoteSrc, ListenerPort, InitiatingProcessFileName;\nWebShellChildren\n| join kind=inner InboundHits on DeviceId\n| where InboundTime between (ChildTime - WindowSec * 1s .. ChildTime + 5s)\n| extend DelaySec = datetime_diff('second', ChildTime, InboundTime)\n| summarize FirstInbound = min(InboundTime),\n            DistinctSrcIps = dcount(RemoteSrc),\n            SrcIps = make_set(RemoteSrc, 25),\n            ListenerPorts = make_set(ListenerPort, 25)\n          by DeviceName, AccountName, VulnService, ChildBin, ChildCmd, ChildTime, ParentPid, SHA256\n| extend DelaySec = datetime_diff('second', ChildTime, FirstInbound)\n| where DelaySec between (0 .. WindowSec)\n| project ChildTime, DeviceName, AccountName, VulnService, ChildBin, ChildCmd,\n          DelaySec, ListenerPorts, SrcIps, DistinctSrcIps, ParentPid, SHA256\n| order by ChildTime desc"
    },
    {
      "id": "UC_WEEKLY_SERVER_AI_AGENT_PROCESS_SPAWNS_SHELL_OR_LOLBIN_WIT",
      "title": "[WEEKLY] Server / AI-agent process spawns shell or LOLBIN with public egress \u2014 post-RCE behavioural chain",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects long-running server or agent processes (node, java, python, dotnet, nginx workers, xrdp, portainer, deepseek-tui, n8n, flowise, ruby, perl, php-fpm) spawning interactive shells (cmd/powershell/sh/bash) or network LOLBINs (curl/wget/certutil/bitsadmin/nc/ncat/socat), correlated with public-internet egress from the same host within 5 minutes \u2014 or with shell-decode / download-cradle markers in the child command line. This fortnight produced 12 disparate RCE disclosures whose post-exploit fingerprint collapses to the same behaviour: n8n HTTP-Request prototype pollution (CVE-2026-44789), NGINX rewrite heap overflow exploited in the wild (CVE-2026-42945 / 42946 / 40701 / 42934), Portainer plugin auth bypass to host RCE (CVE-2026-44848), FlowiseAI NodeVM sandbox escape (CVE-2026-46442), DeepSeek-TUI run_tests + task_create insecure defaults (CVE-2026-45311 / 45374), Mapfish Print dynamic-table injection (CVE-2026-44672), esm.sh path traversal (CVE-2026-44593), xrdp pre-auth RCE (CVE-2025-68670), Ivanti EPMM (CVE-2026-6973), and Microsoft's AI-agent frameworks advisory (CVE-2026-26030 / 25592). The kill chain spans exploit through install / c2 \u2014 it fires when an RCE primitive lands and the operator pivots from in-process code execution to a real shell or a second-stage downloader. Fires when a watched server parent spawns a shell/LOLBIN AND the child either reaches a public IP within 5 minutes OR carries decode/download cradles in its command line; does NOT fire on routine CI/build activity (npm/cargo/gradle/mvn/pip/go-test/jest/pytest/eslint/prettier/webpack) suppressed via parent and child command-line regex, nor on benign shell spawns that never egress and lack cradle markers.\n\nRationale: Every one of the 12 articles ends the same way regardless of the exploit primitive: a trusted long-running server / AI-agent / workflow process gains the ability to run code and the operator immediately reaches for a shell or a network-staging LOLBIN. Anchoring on the parent-child pair (server-host \u2192 shell/LOLBIN) plus a 5-minute time-windowed correlation to public egress on the same PID promotes a hunch into an alert without depending on the rotating CVE-, hash-, or IP-level IOCs in any individual article.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1059.003",
          "name": "Command and Scripting Interpreter: Windows Command Shell"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1059.007",
          "name": "Command and Scripting Interpreter: JavaScript"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        },
        {
          "id": "T1218",
          "name": "System Binary Proxy Execution"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats `summariesonly` count min(_time) as spawnTime values(Processes.process) as child_cmd values(Processes.parent_process) as parent_cmd values(Processes.user) as user from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"node\",\"node.exe\",\"java\",\"java.exe\",\"javaw.exe\",\"python\",\"python.exe\",\"python3\",\"pythonw.exe\",\"dotnet\",\"dotnet.exe\",\"nginx\",\"nginx.exe\",\"xrdp\",\"xrdp-sesman\",\"portainer\",\"portainer.exe\",\"deepseek-tui\",\"deepseek-tui.exe\",\"n8n\",\"n8n.exe\",\"flowise\",\"ruby\",\"ruby.exe\",\"perl\",\"go\",\"php-fpm\") AND Processes.process_name IN (\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh\",\"bash\",\"zsh\",\"dash\",\"ksh\",\"mshta.exe\",\"wscript.exe\",\"cscript.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"bitsadmin.exe\",\"certutil.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"nc\",\"ncat\",\"socat\") by Processes.dest Processes.parent_process_name Processes.process_name Processes.process_id _time span=1s | `drop_dm_object_name(Processes)` | search NOT user=\"*$\" | where NOT match(parent_cmd, \"(?i)(npm\\s+(test|install|run|ci)|cargo\\s+(test|build|run)|gradle|mvn\\s+(install|test|package)|pip\\s+install|go\\s+test|jest|pytest|eslint|prettier|webpack)\") | eval suspicious_cmd=if(match(child_cmd, \"(?i)(base64\\s+-d|/dev/tcp/|bash\\s+-i|powershell\\s+-(e|enc)|FromBase64String|IEX\\s|Invoke-Expression|DownloadString|certutil\\s+-urlcache|wget\\s+http|curl\\s+-s\\s+http)\"), \"yes\", \"no\") | join type=left dest [ | tstats `summariesonly` min(_time) as netTime values(All_Traffic.dest_ip) as remote_ip values(All_Traffic.dest_port) as remote_port values(All_Traffic.app) as netproc from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_category!=\"internal\" AND (All_Traffic.app IN (\"node\",\"java\",\"python\",\"python3\",\"dotnet\",\"nginx\",\"xrdp\",\"portainer\",\"deepseek-tui\",\"n8n\",\"flowise\",\"ruby\",\"perl\",\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh\",\"bash\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"bitsadmin.exe\",\"certutil.exe\",\"curl\",\"wget\",\"nc\",\"ncat\",\"socat\")) by All_Traffic.src as dest | `drop_dm_object_name(All_Traffic)` ] | eval egress_window=if(isnotnull(netTime) AND (netTime-spawnTime)>=0 AND (netTime-spawnTime)<=300, \"yes\", \"no\") | where egress_window=\"yes\" OR suspicious_cmd=\"yes\" | convert ctime(spawnTime) ctime(netTime) | table spawnTime dest user parent_process_name parent_cmd process_name child_cmd process_id remote_ip remote_port netproc netTime egress_window suspicious_cmd | sort - spawnTime",
      "defender_kql": "let _server_parents = dynamic([\"node.exe\",\"node\",\"java.exe\",\"java\",\"javaw.exe\",\"python.exe\",\"python\",\"python3\",\"pythonw.exe\",\"dotnet.exe\",\"dotnet\",\"nginx.exe\",\"nginx\",\"xrdp\",\"xrdp-sesman\",\"portainer.exe\",\"portainer\",\"deepseek-tui.exe\",\"deepseek-tui\",\"n8n.exe\",\"n8n\",\"flowise\",\"ruby.exe\",\"ruby\",\"perl\",\"go\",\"php-fpm\"]);\nlet _shells_lolbins = dynamic([\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"sh\",\"bash\",\"zsh\",\"dash\",\"ksh\",\"mshta.exe\",\"wscript.exe\",\"cscript.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"bitsadmin.exe\",\"certutil.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"nc\",\"ncat\",\"socat\"]);\nlet _build_noise = @\"(?i)(npm\\s+(test|install|run|ci)|cargo\\s+(test|build|run)|gradle\\s|mvn\\s+(install|test|package)|pip\\s+install|go\\s+test|jest|pytest|eslint|prettier|webpack)\";\nlet _cradle = @\"(?i)(base64\\s+-d|/dev/tcp/|bash\\s+-i|powershell\\s+-(e|enc)|FromBase64String|IEX |Invoke-Expression|DownloadString|certutil\\s+-urlcache|wget\\s+http|curl\\s+-s\\s+http)\";\nlet _spawn = DeviceProcessEvents\n    | where Timestamp > ago(1h)\n    | where InitiatingProcessFileName in~ (_server_parents)\n    | where FileName in~ (_shells_lolbins)\n    | where AccountName !endswith \"$\"\n    | where not(ProcessCommandLine matches regex _build_noise)\n    | where not(InitiatingProcessCommandLine matches regex _build_noise)\n    | project SpawnTime = Timestamp, DeviceId, DeviceName, AccountName,\n              ParentImage = InitiatingProcessFileName,\n              ParentCmd = InitiatingProcessCommandLine,\n              ChildImage = FileName,\n              ChildCmd = ProcessCommandLine,\n              ChildPid = ProcessId,\n              SHA256;\nlet _egress = DeviceNetworkEvents\n    | where Timestamp > ago(1h)\n    | where ActionType in (\"ConnectionSuccess\",\"HttpConnectionInspected\",\"ConnectionAttempt\")\n    | where RemoteIPType == \"Public\"\n    | project NetTime = Timestamp, DeviceId,\n              NetProc = InitiatingProcessFileName,\n              NetPid = InitiatingProcessId,\n              RemoteIP, RemotePort, RemoteUrl;\n_spawn\n| join kind=leftouter _egress on DeviceId, $left.ChildPid == $right.NetPid\n| extend EgressWithinWindow = iif(isnotnull(NetTime) and datetime_diff('second', NetTime, SpawnTime) between (0 .. 300), true, false)\n| extend SuspiciousCmd = ChildCmd matches regex _cradle\n| where EgressWithinWindow or SuspiciousCmd\n| project SpawnTime, DeviceName, AccountName, ParentImage, ParentCmd, ChildImage, ChildCmd, ChildPid, RemoteIP, RemotePort, RemoteUrl, NetTime, EgressWithinWindow, SuspiciousCmd\n| order by SpawnTime desc"
    },
    {
      "id": "UC_WEEKLY_SERVICE_PROCESS_PARENT_SPAWNS_SUBPROCESS_CONTAININ",
      "title": "[WEEKLY] Service-process parent spawns subprocess containing CLI-argument-injection tokens",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects a unifying tradecraft across this fortnight's web-app and CLI-tool CVEs: a long-running service or non-interactive tool (web server, language runtime, backup CLI, AI/dev TUI) spawns a child process (ssh, git, curl, wget, bash/sh, powershell, python, perl) whose command line carries high-risk argv-injection tokens (`-oProxyCommand=`, `--upload-pack=`, `--receive-pack=`, `-c core.sshCommand=`, `--exec=`, `-EncodedCommand`, curl `--config`/`-K`/`-T`, shell-metachar substitution `$(...)`, backticks, `|sh`, `&&curl`, `/dev/tcp/`, `bash -i`, `base64 -d`). This pattern is the joint signature of CVE-2026-45695 (Kopia SSH ProxyCommand), CVE-2026-44790 (n8n Git node `--upload-pack`/`-c core.sshCommand`), CVE-2026-45369 (utcp-cli `$(...)` substitution), CVE-2026-45374 (DeepSeek TUI shell sub-agent), and the post-exploit RCE chains following CVE-2026-9082 (Drupal SQLi), CVE-2026-26980 (Ghost CMS SQLi\u2192ClickFix), CVE-2026-46670 (YesWiki SQLi), CVE-2026-46633 (Twig PHP code injection), CVE-2026-45697 (Formie SSTI), CVE-2026-45288 (Marten regConfig SQLi), and CVE-2026-22599 (Strapi SQLi). It spans the exploit \u2192 install / execution kill-chain pivot \u2014 where the vulnerable parent has accepted untrusted input and now hands it down a process tree. It fires when a service-tier parent (php-fpm, w3wp, node, n8n, kopia, deepseek-tui, etc.) launches an interpreter/tool child carrying one of the injection-token families. It does NOT fire on user-shell-rooted activity (bash launched from sshd/login), nor on service parents launching child processes whose argv is clean of metachars/injection flags, suppressing routine maintenance and admin scripts.\n\nRationale: Every article in scope ends in the same physical artefact: a trusted service / CLI parent emits a child process whose argv carries either argument-injection switches (ssh -oProxyCommand, git --upload-pack/-c core.sshCommand, curl --config) or interpreter-injection metachars ($(...), backticks, |sh, &&curl, /dev/tcp/, -EncodedCommand, base64 -d). Pinning on the parent-class + child-class + token-class triad turns 12 different CVEs into one stable behavioural detection that survives IOC rotation.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        },
        {
          "id": "T1059.001",
          "name": "Command and Scripting Interpreter: PowerShell"
        },
        {
          "id": "T1059.003",
          "name": "Command and Scripting Interpreter: Windows Command Shell"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1059.006",
          "name": "Command and Scripting Interpreter: Python"
        },
        {
          "id": "T1203",
          "name": "Exploitation for Client Execution"
        },
        {
          "id": "T1505.003",
          "name": "Server Software Component: Web Shell"
        }
      ],
      "data_models": [
        "Endpoint.Processes"
      ],
      "splunk_spl": "| tstats summariesonly=true count, min(_time) as firstTime, max(_time) as lastTime, values(Processes.process_path) as child_paths, values(Processes.parent_process) as parent_cmds from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"w3wp.exe\",\"php-fpm\",\"php-cgi\",\"php-cgi.exe\",\"php.exe\",\"php\",\"httpd\",\"httpd.exe\",\"apache2\",\"nginx\",\"nginx.exe\",\"node\",\"node.exe\",\"n8n\",\"n8n.exe\",\"python\",\"python3\",\"python.exe\",\"ruby\",\"ruby.exe\",\"java\",\"java.exe\",\"kopia\",\"kopia.exe\",\"kopia-ui\",\"kopia-ui.exe\",\"deepseek-tui\",\"deepseek-tui.exe\",\"utcp\",\"utcp.exe\",\"strapi\",\"ghost\",\"drupal\",\"yeswiki\") AND Processes.process_name IN (\"ssh\",\"ssh.exe\",\"git\",\"git.exe\",\"bash\",\"sh\",\"dash\",\"zsh\",\"ksh\",\"powershell.exe\",\"pwsh.exe\",\"pwsh\",\"cmd.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"nc\",\"ncat\",\"socat\",\"python\",\"python3\",\"perl\",\"ruby\") by host, Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process | `drop_dm_object_name(Processes)` | eval injection_class = case(match(process,\"(?i)(-oProxyCommand|-o\\s+ProxyCommand|-oIdentityFile=|ProxyJump=)\"),\"ssh-argv-injection\", match(process,\"(?i)(--upload-pack=|--receive-pack=|-c\\s+core\\.sshCommand|--exec=)\"),\"git-argv-injection\", match(process,\"(?i)(-EncodedCommand|\\s-enc\\s|\\s-ec\\s)\"),\"powershell-encoded\", match(process,\"(?i)(\\$\\(|`[^`]{2,}`|;\\s*(curl|wget|nc|bash|sh|python|powershell|base64|chmod)|\\|\\s*(sh|bash|nc|base64|tee)|&&\\s*(curl|wget|nc|bash|sh|python|powershell))\"),\"shell-metachars\", match(process,\"(?i)(/dev/tcp/|bash\\s+-i|nc\\s+-e|base64\\s+-d|python\\S*\\s+-c\\s+['\\\"]\\s*(import|exec|os\\.system))\"),\"reverse-shell-pattern\", match(process,\"(?i)(--config\\s|\\s-K\\s|--upload-file|\\s-T\\s+[A-Za-z/])\"),\"curl-argv-injection\", \"\") | where injection_class!=\"\" | search NOT user IN (\"SYSTEM\",\"LOCAL SERVICE\",\"NETWORK SERVICE\",\"root\") AND NOT match(user,\"\\\\$$\") | sort 0 - lastTime",
      "defender_kql": "let ServiceParents = dynamic([\"w3wp.exe\",\"php-fpm\",\"php-cgi\",\"php-cgi.exe\",\"php.exe\",\"php\",\"httpd\",\"httpd.exe\",\"apache2\",\"nginx\",\"nginx.exe\",\"node\",\"node.exe\",\"n8n\",\"n8n.exe\",\"python\",\"python3\",\"python.exe\",\"ruby\",\"ruby.exe\",\"java\",\"java.exe\",\"kopia\",\"kopia.exe\",\"kopia-ui\",\"kopia-ui.exe\",\"deepseek-tui\",\"deepseek-tui.exe\",\"utcp\",\"utcp.exe\",\"strapi\",\"ghost\",\"drupal\",\"yeswiki\"]);\nlet InterpreterChildren = dynamic([\"ssh\",\"ssh.exe\",\"git\",\"git.exe\",\"bash\",\"sh\",\"dash\",\"zsh\",\"ksh\",\"powershell.exe\",\"pwsh.exe\",\"pwsh\",\"cmd.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"nc\",\"ncat\",\"socat\",\"python\",\"python3\",\"perl\",\"ruby\"]);\nDeviceProcessEvents\n| where Timestamp > ago(7d)\n| where InitiatingProcessFileName in~ (ServiceParents)\n| where FileName in~ (InterpreterChildren)\n| where AccountName !endswith \"$\"\n| where AccountName !in~ (\"system\",\"local service\",\"network service\",\"root\")\n| extend SshArg   = ProcessCommandLine matches regex @\"(?i)(-oProxyCommand|-o\\s+ProxyCommand|-oIdentityFile=|ProxyJump=)\"\n| extend GitArg   = ProcessCommandLine matches regex @\"(?i)(--upload-pack=|--receive-pack=|-c\\s+core\\.sshCommand|--exec=)\"\n| extend PsEnc    = ProcessCommandLine matches regex @\"(?i)(-EncodedCommand|\\s-enc\\s|\\s-ec\\s)\"\n| extend ShellMet = ProcessCommandLine matches regex @\"(?i)(\\$\\(|`[^`]{2,}`|;\\s*(curl|wget|nc|bash|sh|python|powershell|base64|chmod)|\\|\\s*(sh|bash|nc|base64|tee)|&&\\s*(curl|wget|nc|bash|sh|python|powershell))\"\n| extend RevSh    = ProcessCommandLine matches regex @\"(?i)(/dev/tcp/|bash\\s+-i|nc\\s+-e|base64\\s+-d|python\\S*\\s+-c\\s+['\"\"]\\s*(import|exec|os\\.system))\"\n| extend CurlArg  = ProcessCommandLine matches regex @\"(?i)(--config\\s|\\s-K\\s|--upload-file|\\s-T\\s+[A-Za-z/])\"\n| where SshArg or GitArg or PsEnc or ShellMet or RevSh or CurlArg\n| extend InjectionClass = case(SshArg,\"ssh-argv-injection\", GitArg,\"git-argv-injection\", PsEnc,\"powershell-encoded\", ShellMet,\"shell-metachars\", RevSh,\"reverse-shell-pattern\", CurlArg,\"curl-argv-injection\", \"unknown\")\n| project Timestamp, DeviceName, AccountName,\n          ParentImage = InitiatingProcessFolderPath,\n          ParentCmd   = InitiatingProcessCommandLine,\n          ChildImage  = FolderPath,\n          ChildCmd    = ProcessCommandLine,\n          InjectionClass, SHA256\n| order by Timestamp desc"
    },
    {
      "id": "UC_WEEKLY_SUB_ADMIN_GRANTS_OWNER_ADMINISTRATOR_ROLE_THEN_GRA",
      "title": "[WEEKLY] Sub-admin grants Owner/Administrator role then grantee signs in from a different source within 60 minutes",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "This UC fires when a SaaS or cloud-IdP audit log records assignment of an Owner / Administrator / Global Administrator role (or equivalent privileged group membership) to a non-self principal, and that grantee subsequently authenticates from a different source IP than the grantor within 60 minutes. The fortnight's driver is CVE-2026-47413 in praisonai-platform \u2014 `POST /workspaces/{id}/members` was gated only by `require_workspace_member` and forwarded `user_id` + `role=owner` straight to the service layer with no caller-authority check, letting any workspace member promote an arbitrary account to Owner \u2014 and The Gentlemen ransomware report (Hacker News, 2026-06-11), which documents T1098.001 Additional Cloud Credentials and T1078 Valid Accounts as the standard post-T1190 handoff used to convert an exploit (CVE-2024-55591, CVE-2025-32433, CVE-2025-33073 and others) into durable identity persistence. The kill-chain spans exploit (the underlying authorization bypass or compromised account performing the grant), persistence (the grantee role itself), and privilege escalation (the grantee now operating with admin authority). It would fire on the praisonai exploit pattern, on a low-privilege Okta admin granting Super Admin, on an AssumedRole session attaching AdministratorAccess to a fresh IAM user, or on a compromised helpdesk account adding a user to Global Administrators. It would NOT fire on Global-Admin-grants-Global-Admin lifecycle changes (excluded by the privileged-grantor allowlist), on PIM activation where grantor == grantee, on documented break-glass / automation SPNs (excluded via `false_positive_filters`), or where the grant succeeds but no grantee sign-in lands within the 60-minute window.\n\nRationale: The praisonai-platform CVE (Article 1) and The Gentlemen ransomware analysis (Article 6) explicitly share the same identity-elevation primitive \u2014 a caller without the target role grants Owner/Admin to a second principal who then authenticates and acts \u2014 and the four other articles all describe trust-boundary bypasses (T1190 Exploit Public-Facing Application is tagged on every one of the six) whose payoff is durable identity / account control of the kind this UC's grantee-signin correlation surfaces. Detecting the grant + new-IP-grantee-signin chain catches the praisonai vuln directly today and the broader post-T1190 cloud-account manipulation pattern The Gentlemen and similar actors are pivoting to this fortnight, without re-emitting the package-manager-install or LOLBin-network-egress patterns the catalog already covers.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1098",
          "name": "Account Manipulation"
        },
        {
          "id": "T1098.001",
          "name": "Account Manipulation: Additional Cloud Credentials"
        },
        {
          "id": "T1078",
          "name": "Valid Accounts"
        },
        {
          "id": "T1078.004",
          "name": "Valid Accounts: Cloud Accounts"
        },
        {
          "id": "T1068",
          "name": "Exploitation for Privilege Escalation"
        },
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1556",
          "name": "Modify Authentication Process"
        }
      ],
      "data_models": [
        "Change.All_Changes",
        "Authentication.Authentication"
      ],
      "splunk_spl": "| tstats summariesonly=true count min(_time) as grant_first max(_time) as grant_last values(All_Changes.command) as commands from datamodel=Change.All_Changes where All_Changes.action=\"created\" AND All_Changes.object_category IN (\"role\",\"user\",\"group_membership\",\"directory\") AND (All_Changes.command=\"*owner*\" OR All_Changes.command=\"*admin*\" OR All_Changes.command=\"*Administrator*\" OR All_Changes.command=\"*AdministratorAccess*\" OR All_Changes.command=\"*Global Administrator*\") AND All_Changes.result=\"success\" by All_Changes.user All_Changes.dest All_Changes.object_id\n| `drop_dm_object_name(All_Changes)`\n| rename user as grantor, object_id as grantee\n| where grantor!=grantee AND isnotnull(grantee) AND grantee!=\"\"\n| join type=inner grantee [\n    | tstats summariesonly=true min(_time) as signin_time values(Authentication.src) as signin_src values(Authentication.app) as signin_app from datamodel=Authentication.Authentication where Authentication.action=\"success\" by Authentication.user\n    | `drop_dm_object_name(Authentication)`\n    | rename user as grantee\n  ]\n| eval delay_sec = signin_time - grant_last\n| where delay_sec>=0 AND delay_sec<=3600\n| table grant_first, grant_last, signin_time, delay_sec, grantor, grantee, dest, commands, signin_src, signin_app\n| sort - grant_last",
      "defender_kql": "let LookbackDays = 7d;\nlet CorrelationMinutes = 60;\nlet PrivilegedRoles = dynamic([\"owner\",\"admin\",\"administrator\",\"global administrator\",\"globaladministrator\",\"privileged role administrator\",\"application administrator\",\"cloud application administrator\",\"superuser\",\"super admin\",\"root\"]);\nlet SuspectGrants = CloudAppEvents\n    | where Timestamp > ago(LookbackDays)\n    | where ActionType has_any (\"Add member\",\"Add user\",\"Add owner\",\"Add role\",\"Update role\",\"Assign role\",\"Add member to role\",\"Add owner to group\",\"Add member to group\")\n         or RawEventData has_any (\"\\\"role\\\":\\\"owner\\\"\",\"\\\"role\\\":\\\"admin\\\"\",\"\\\"role\\\":\\\"Owner\\\"\",\"\\\"role\\\":\\\"Administrator\\\"\",\"AdministratorAccess\")\n    | extend RawJ = parse_json(RawEventData)\n    | extend GrantedRole = tolower(tostring(coalesce(RawJ.role, RawJ.RoleAdded, RawJ.NewRole, RawJ.new_role, RawJ[\"target_role\"])))\n    | extend GranteeId = tostring(coalesce(RawJ.user_id, RawJ.TargetUserId, RawJ.targetUserPrincipalName, RawJ.target, RawJ[\"member\"]))\n    | extend GrantorId = tostring(coalesce(AccountObjectId, RawJ.actor_id, RawJ.actorPrincipalId))\n    | where isnotempty(GrantedRole) and isnotempty(GranteeId) and isnotempty(GrantorId)\n    | where GrantedRole in (PrivilegedRoles)\n    | where GrantorId != GranteeId\n    | project GrantTime = Timestamp, GrantorId, GrantorIP = IPAddress, GranteeId, GrantedRole,\n              ObjectName, ApplicationId, UserAgent, ActionType;\nlet GranteeSignIn = AADSignInEventsBeta\n    | where Timestamp > ago(LookbackDays)\n    | where ErrorCode == 0\n    | project SignInTime = Timestamp, GranteeId = AccountObjectId,\n              SignInIP = IPAddress, SignInApp = Application, SignInCountry = Country;\nSuspectGrants\n| join kind=inner GranteeSignIn on GranteeId\n| where SignInTime between (GrantTime .. GrantTime + CorrelationMinutes * 1m)\n| where SignInIP != GrantorIP\n| project GrantTime, SignInTime,\n          DelayMin = datetime_diff('minute', SignInTime, GrantTime),\n          GrantorId, GrantorIP, GranteeId, GrantedRole,\n          SignInIP, SignInApp, SignInCountry,\n          ActionType, ApplicationId, UserAgent\n| order by GrantTime desc"
    },
    {
      "id": "UC_WEEKLY_SUPPLY_CHAIN_REPO_CREDENTIAL_THEFT_OUTBOUND_EXFIL_",
      "title": "[WEEKLY] Supply-chain repo credential theft \u2192 outbound exfil to attacker infra",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Detects the joint pattern across this fortnight's supply-chain stories: a developer workstation, CI runner, or self-hosted GitHub Actions runner executes a script-host or shell that reads developer/CI credential material (env vars, ~/.npmrc, ~/.aws/credentials, ~/.git-credentials, /proc/<pid>/environ, cloud IMDS) and within minutes makes an outbound HTTP(S)/DNS egress to a non-baseline public domain \u2014 the chain seen in Aikido's Mini Shai-Hulud npm worm write-up (developer machines as #1 supply-chain target, masscan.cloud / git-tanstack.com / getsession.org exfil), The Hacker News 'Microsoft Fixes One-Click GitHub Dev Attack That Let Attackers Steal OAuth Tokens' (CVE-2026-45247 OAuth token theft) and 'Claude Code GitHub Action Flaw Let One Malicious Issue Hijack Repositories' (prompt-injection cred-theft via /proc/<pid>/environ on Actions runners), and the DLL-side-load credential phase of Securelist's pirate-streaming miner/RAT. It spans install \u2192 credential access \u2192 C2/exfiltration kill-chain stages by joining process-execution telemetry (script host or shell touching a credential artefact) with a subsequent first-seen outbound network connection from the same host within 10 minutes. Fires when a node/python/bash/PowerShell process reads a credential file or IMDS and the same host then beacons to a rare public domain; does NOT fire on standalone credential reads by IDEs/cred-helpers without egress, nor on egress without a preceding credential-touch, nor on egress to org-baselined destinations such as github.com, registry.npmjs.org or pypi.org. IOC tokens (<c2_domain_list>, <sha256_list>) are substitution slots so the UC survives weekly IOC rotation while the chain persists.\n\nRationale: All five articles share one mechanic: a developer/CI-context process reads credential material (npm/git/AWS files, /proc env, IMDS, OAuth tokens) and the same host then beacons to attacker-controlled infra. The UC joins process-on-credential-artefact with a first-seen public egress in a 10-minute window, so it catches Shai-Hulud npm worms, Claude Code Action prompt-injection cred theft, OAuth-token-stealing GitHub.dev exploits, and miner/RAT secondary-stage exfil without depending on this week's IOC table.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1552.001",
          "name": "Unsecured Credentials: Credentials In Files"
        },
        {
          "id": "T1552.005",
          "name": "Unsecured Credentials: Cloud Instance Metadata API"
        },
        {
          "id": "T1528",
          "name": "Steal Application Access Token"
        },
        {
          "id": "T1555.003",
          "name": "Credentials from Web Browsers"
        },
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        },
        {
          "id": "T1567",
          "name": "Exfiltration Over Web Service"
        },
        {
          "id": "T1041",
          "name": "Exfiltration Over C2 Channel"
        },
        {
          "id": "T1071.001",
          "name": "Application Layer Protocol: Web Protocols"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem",
        "Network_Traffic.All_Traffic",
        "Network_Resolution.DNS"
      ],
      "splunk_spl": "| tstats `summariesonly` min(_time) as cred_time values(Processes.process) as cred_cmd values(Processes.process_name) as cred_proc values(Processes.parent_process_name) as cred_parent from datamodel=Endpoint.Processes where Processes.process_name IN (\"node.exe\",\"node\",\"npm\",\"npm-cli.js\",\"pwsh.exe\",\"powershell.exe\",\"cmd.exe\",\"bash\",\"sh\",\"zsh\",\"python.exe\",\"python\",\"python3\",\"cat\",\"head\",\"tail\",\"awk\",\"sed\",\"grep\",\"strings\",\"xxd\",\"od\",\"tr\",\"dd\",\"curl\",\"wget\",\"git.exe\",\"git\") AND (Processes.process=\"*/.npmrc*\" OR Processes.process=\"*\\\\.npmrc*\" OR Processes.process=\"*/.aws/credentials*\" OR Processes.process=\"*\\\\.aws\\\\credentials*\" OR Processes.process=\"*/.git-credentials*\" OR Processes.process=\"*\\\\.git-credentials*\" OR Processes.process=\"*/.docker/config.json*\" OR Processes.process=\"*/.kube/config*\" OR Processes.process=\"*/.netrc*\" OR Processes.process=\"*/.ssh/id_*\" OR Processes.process=\"*\\\\.ssh\\\\id_*\" OR Processes.process=\"*/proc/*/environ*\" OR Processes.process=\"*169.254.169.254*\" OR Processes.process=\"*metadata.google.internal*\" OR Processes.process=\"*169.254.170.2*\" OR Processes.process=\"*GITHUB_TOKEN*\" OR Processes.process=\"*NPM_TOKEN*\" OR Processes.process=\"*AWS_SESSION_TOKEN*\") by host Processes.user Processes.process_name Processes.parent_process_name Processes.process\n| `drop_dm_object_name(Processes)`\n| rename host as dest\n| join type=inner dest [\n    | tstats `summariesonly` min(_time) as egress_time values(All_Traffic.dest) as egress_ip values(All_Traffic.app) as egress_proto values(All_Traffic.dest_port) as egress_port from datamodel=Network_Traffic.All_Traffic where All_Traffic.action!=\"blocked\" AND All_Traffic.dest_category!=\"internal\" by host All_Traffic.dest All_Traffic.dest_port\n    | `drop_dm_object_name(All_Traffic)`\n    | rename host as dest\n  ]\n| where egress_time >= cred_time AND egress_time <= cred_time + 600\n| eval delay_sec = egress_time - cred_time\n| lookup baseline_egress_domains_30d.csv dest_host OUTPUT first_seen\n| where isnull(first_seen) AND NOT match(egress_ip,\"(?i)(github\\.com|githubusercontent\\.com|registry\\.npmjs\\.org|pypi\\.org|files\\.pythonhosted\\.org|sts\\.amazonaws\\.com|googleapis\\.com|azure\\.com|microsoft\\.com)$\")\n| table cred_time egress_time delay_sec dest user process_name parent_process_name process egress_ip egress_port egress_proto\n| sort 0 - cred_time",
      "defender_kql": "let LookbackDays = 7d;\nlet WindowSec = 600;\nlet CredScripts = dynamic([\"node.exe\",\"npm.exe\",\"pwsh.exe\",\"powershell.exe\",\"cmd.exe\",\"bash.exe\",\"sh\",\"bash\",\"zsh\",\"python.exe\",\"python3\",\"python\",\"cat\",\"head\",\"tail\",\"awk\",\"sed\",\"grep\",\"strings\",\"xxd\",\"od\",\"tr\",\"dd\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\",\"git.exe\",\"git\"]);\nlet CredTargets = dynamic([\".npmrc\",\".aws\\\\credentials\",\".aws/credentials\",\".git-credentials\",\".docker\\\\config.json\",\".docker/config.json\",\".kube\\\\config\",\".kube/config\",\".netrc\",\".ssh\\\\id_\",\".ssh/id_\",\"/proc/\",\"169.254.169.254\",\"metadata.google.internal\",\"169.254.170.2\",\"GITHUB_TOKEN\",\"NPM_TOKEN\",\"AWS_SESSION_TOKEN\",\"environ\"]);\nlet BaselineDomains = DeviceNetworkEvents\n    | where Timestamp between (ago(30d) .. ago(1d))\n    | where RemoteIPType == \"Public\"\n    | summarize BaselineHosts = dcount(DeviceName) by RemoteUrl\n    | where BaselineHosts > 2;\nlet CredReads = DeviceProcessEvents\n    | where Timestamp > ago(LookbackDays)\n    | where FileName in~ (CredScripts)\n    | where ProcessCommandLine has_any (CredTargets)\n    | where AccountName !endswith \"$\"\n    | project CredTime = Timestamp, DeviceId, DeviceName, AccountName,\n              CredProc = FileName, CredCmd = ProcessCommandLine,\n              CredParent = InitiatingProcessFileName,\n              CredParentCmd = InitiatingProcessCommandLine;\nlet Egress = DeviceNetworkEvents\n    | where Timestamp > ago(LookbackDays)\n    | where RemoteIPType == \"Public\"\n    | where isnotempty(RemoteUrl)\n    | where RemoteUrl !endswith \"github.com\"\n         and RemoteUrl !endswith \"githubusercontent.com\"\n         and RemoteUrl !endswith \"registry.npmjs.org\"\n         and RemoteUrl !endswith \"pypi.org\"\n         and RemoteUrl !endswith \"files.pythonhosted.org\"\n         and RemoteUrl !endswith \"amazonaws.com\"\n         and RemoteUrl !endswith \"googleapis.com\"\n         and RemoteUrl !endswith \"azure.com\"\n         and RemoteUrl !endswith \"microsoft.com\"\n    | join kind=leftanti BaselineDomains on RemoteUrl\n    | project EgressTime = Timestamp, DeviceId, DeviceName,\n              RemoteUrl, RemoteIP, RemotePort,\n              EgressProc = InitiatingProcessFileName,\n              EgressCmd  = InitiatingProcessCommandLine;\nCredReads\n| join kind=inner Egress on DeviceId\n| where EgressTime between (CredTime .. CredTime + WindowSec * 1s)\n| extend DelaySec = datetime_diff('second', EgressTime, CredTime)\n| project CredTime, EgressTime, DelaySec, DeviceName, AccountName,\n          CredProc, CredCmd, CredParent, CredParentCmd,\n          RemoteUrl, RemoteIP, RemotePort, EgressProc, EgressCmd\n| order by CredTime desc"
    },
    {
      "id": "UC_WEEKLY_VENDOR_THIRD_PARTY_OAUTH_APP_OR_SP_SIGN_IN_FROM_UN",
      "title": "[WEEKLY] Vendor / Third-Party OAuth App or SP Sign-in From Unbaselined Egress Followed by Bulk SaaS Object Read",
      "kill_chain": "actions",
      "confidence": "High",
      "description": "Chains two events on the same identity within a 30-minute window: (1) a successful OAuth-app, service-principal, or federated SSO sign-in from an (AppId,SrcIP) pair that has never been seen in the prior 30-day baseline; and (2) the same actor performing bulk reads (>=50 files / mail items / repository pulls / customer-record queries) immediately afterwards. This is the joint fingerprint of the ShinyHunters Vimeo (\"Vimeo Data Breach Exposes 119,000 Users\" and \"Vimeo data breach exposes personal information of 119,000 people\", May 2026) and Zara (\"Zara data breach exposed personal information of 197,000 people\", May 2026) intrusions, which abused vendor service principals (e.g. Anodot OAuth app) and named UNC6661/UNC6671 proxy IPs to pivot from federated identity to mass data theft; the RansomHouse Trellix source-code-repository compromise (\"Trellix Confirms Source Code Breach...\", \"Trellix source code breach claimed by RansomHouse hackers\", \"Trellix Breach \u2013 RansomHouse Claims Access...\", \"Trellix discloses data breach...\", May 2026) where attackers logged in to a third-party-managed code SaaS and pulled repo archives; the NVIDIA / GFN.AM regional-partner breach (\"NVIDIA confirms GeForce NOW data breach affecting Armenian users\", \"NVIDIA Data Breach Reportedly Exposes Personal Information of GeForce Users\", May 2026) where a partner-side admin account was used to read the user database; the DAEMON Tools supply-chain breach (\"DAEMON Tools devs confirm breach\", May 2026); and the PhantomCore TrueConf campaign (\"PhantomCore Exploits TrueConf Vulnerabilities...\", April 2026) which pivoted via SaaS valid-account abuse after exploit. Kill-chain stages spanned: initial access via T1199 trusted relationship, defense evasion via T1550.001 application-token reuse, and actions-on-objective via T1530 / T1213 bulk repository / cloud-object read. The rule fires only when BOTH stages are present on the same principal inside the correlation window; it will NOT fire on routine vendor sign-ins from their canonical egress, on ad-hoc downloads under the bulk threshold, or on first-party Microsoft / Google built-in apps (which are excluded from the SP scope).\n\nRationale: The joint signal across all 12 articles is not a CVE or a malware family but a trusted-identity-from-non-canonical-egress pattern that immediately precedes bulk data exfiltration. ShinyHunters (Vimeo, Zara), RansomHouse (Trellix), and the GFN.AM regional partner case all hinge on the same shape: an OAuth-app, service-principal, or federated identity authenticating from infrastructure outside its 30-day baseline, then performing high-volume object reads against the SaaS workload the principal has access to. The two-stage temporal correlation (unbaselined sign-in -> bulk read inside 30 minutes by the same actor) is what raises this above per-IOC hunts and above the generic Datadog SaaS-anomaly defaults, because it pairs the trust-abuse pivot with the impact action in a single alert.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1199",
          "name": "Trusted Relationship"
        },
        {
          "id": "T1078.004",
          "name": "Valid Accounts: Cloud Accounts"
        },
        {
          "id": "T1550.001",
          "name": "Use Alternate Authentication Material: Application Access Token"
        },
        {
          "id": "T1530",
          "name": "Data from Cloud Storage"
        },
        {
          "id": "T1213",
          "name": "Data from Information Repositories"
        },
        {
          "id": "T1195.002",
          "name": "Compromise Software Supply Chain"
        }
      ],
      "data_models": [
        "Authentication",
        "Change",
        "Web"
      ],
      "splunk_spl": "``` Stage 1 \u2014 successful SP / OAuth / federated sign-ins (last 14d) with their (app,src) pair ```\n| tstats `summariesonly` earliest(_time) as auth_time, values(Authentication.src) as auth_src, values(Authentication.app) as auth_app, values(Authentication.signature_id) as auth_sig from datamodel=Authentication.Authentication where Authentication.action=success AND Authentication.signature_id IN (\"ServicePrincipalSignIn\",\"NonInteractiveUserSignIn\",\"UserLoggedIn\",\"ConsoleLogin\",\"AssumeRoleWithSAML\",\"federatedSSO\",\"user.session.start\") AND earliest=-14d@d by Authentication.user span=1m\n| `drop_dm_object_name(Authentication)`\n| rename user as actor\n``` Anti-baseline: keep only (app,src) pairs never (or barely) seen in the prior 30d window ```\n| join type=left auth_app, auth_src [ tstats `summariesonly` count as baseline_count from datamodel=Authentication.Authentication where Authentication.action=success AND earliest=-30d@d latest=-14d@d by Authentication.app, Authentication.src | `drop_dm_object_name(Authentication)` | rename app as auth_app, src as auth_src ]\n| fillnull baseline_count value=0\n| where baseline_count <= 2\n``` Stage 2 \u2014 bulk SaaS object reads by the same actor within 30 minutes ```\n| join type=inner actor max=0 [ tstats `summariesonly` count from datamodel=Change.All_Changes where All_Changes.action IN (\"read\",\"downloaded\",\"accessed\",\"exported\",\"cloned\") AND All_Changes.object_category IN (\"file\",\"email\",\"repository\",\"record\",\"blob\",\"secret\",\"mailbox\") by _time, All_Changes.user, All_Changes.object_id, All_Changes.dest, All_Changes.object_category | `drop_dm_object_name(All_Changes)` | rename user as actor, _time as access_time, dest as access_app, object_category as object_kind ]\n| where access_time >= auth_time AND access_time <= auth_time + 1800\n| stats count as ObjectsRead, dc(object_id) as DistinctObjects, values(object_kind) as ObjectKinds, values(access_app) as AccessApps, min(access_time) as FirstAccess, max(access_time) as LastAccess by actor, auth_app, auth_src, auth_time\n| where ObjectsRead >= 50 OR DistinctObjects >= 25\n| convert ctime(auth_time) ctime(FirstAccess) ctime(LastAccess)",
      "defender_kql": "let _Lookback = 14d;\nlet _Baseline = 30d;\nlet _CorrWindow = 30m;\nlet _MinObjects = 50;\nlet _MinDistinct = 25;\nlet _FirstPartyApps = dynamic([\"Office 365\",\"Microsoft Teams\",\"Microsoft Outlook\",\"SharePoint Online\",\"OneDrive\",\"Microsoft 365 Apps\",\"Microsoft Graph PowerShell\",\"Microsoft Authentication Broker\",\"Microsoft Edge\",\"Windows Sign In\"]);\n// Stage 1 \u2014 baseline of (AppId, IPAddress) pairs seen in the prior 30d window\nlet _Baseline_Pairs =\n    AADSignInEventsBeta\n    | where Timestamp between (ago(_Baseline) .. ago(_Lookback))\n    | where ErrorCode == 0\n    | summarize Seen = count() by ApplicationId, IPAddress\n    | where Seen > 2;\n// Stage 1 \u2014 recent successful auths from (AppId, IPAddress) pairs NOT in the baseline\nlet SuspectAuth =\n    AADSignInEventsBeta\n    | where Timestamp > ago(_Lookback)\n    | where ErrorCode == 0\n    | where Application !in~ (_FirstPartyApps)\n    | where ClientAppUsed in (\"Other clients\",\"Browser\",\"Mobile Apps and Desktop clients\")\n    | join kind=leftanti _Baseline_Pairs on ApplicationId, IPAddress\n    | project SignInTime = Timestamp, AccountObjectId, AccountUpn,\n              Application, ApplicationId, AuthIPAddress = IPAddress, Country;\n// Stage 2 \u2014 bulk SaaS object reads by the same actor within 30 minutes of the suspicious sign-in\nCloudAppEvents\n| where Timestamp > ago(_Lookback)\n| where ActionType in~ (\"FileDownloaded\",\"FileAccessed\",\"FilePreviewed\",\"FileSyncDownloadedFull\",\"MailItemsAccessed\",\"SearchQueryPerformed\",\"MessageRead\",\"Download\",\"RepoCloned\",\"RepoArchiveDownload\",\"SecretRead\",\"Export\")\n| join kind=inner SuspectAuth on $left.AccountObjectId == $right.AccountObjectId\n| where Timestamp between (SignInTime .. SignInTime + _CorrWindow)\n| summarize ObjectsRead = count(),\n            DistinctObjects = dcount(ObjectName),\n            Actions = make_set(ActionType, 12),\n            SampleObjects = make_set(ObjectName, 10),\n            SaaSApps = make_set(Application, 6),\n            FirstAction = min(Timestamp),\n            LastAction  = max(Timestamp)\n            by AccountObjectId, AccountUpn, Application, ApplicationId, AuthIPAddress, Country, SignInTime\n| where ObjectsRead >= _MinObjects or DistinctObjects >= _MinDistinct\n| project SignInTime, FirstAction, LastAction, AccountUpn, Application,\n          AuthIPAddress, Country, ObjectsRead, DistinctObjects,\n          Actions, SaaSApps, SampleObjects\n| order by FirstAction desc"
    },
    {
      "id": "UC_WEEKLY_WEB_APP_INTERPRETER_NODE_PYTHON_JAVA_PHP_SPAWNS_SH",
      "title": "[WEEKLY] Web App Interpreter (Node/Python/Java/PHP) Spawns Shell or Net-Download LOLBin on Internet-Facing Host",
      "kill_chain": "exploit",
      "confidence": "High",
      "description": "Detects the post-exploitation execution tail of the May 11-16 2026 wave of authentication/authorization bypass disclosures in internet-facing application runtimes \u2014 Node.js, Python (uvicorn/gunicorn), Java, PHP-FPM, Ruby, .NET \u2014 where the application's own interpreter process unexpectedly spawns a shell (sh/bash/cmd/pwsh) or a network-staging LOLBin (curl/wget/nc/python -c). This fortnight the pattern is being driven by CVE-2026-44791 (n8n XML-node prototype pollution \u2192 RCE), CVE-2026-44338 (PraisonAI missing-auth, exploited within 4 hours of disclosure), CVE-2026-42074 (OpenClaude `dangerouslyDisableSandbox` model-controlled escape), CVE-2026-44849 (Portainer Swarm endpoint-security bypass to privileged container), and the Obot `/mcp-connect/{id}` ACR bypass \u2014 together with the Cisco SD-WAN CVE-2026-20182 and Burst Statistics CVE-2026-8181 auth bypasses whose follow-on stages share the same interpreter-spawns-shell shape. Kill-chain stages spanned: exploit \u2192 execution \u2192 ingress tool transfer / discovery, correlated to recent inbound public traffic on the same host. The rule fires when a baseline-rare interpreter\u2192shell-or-LOLBin parent-child pair lands on a host that has received external inbound HTTP traffic in the same window; it does NOT fire on CI/CD runners, build agents, or developer workstations where node/python legitimately spawn shells as part of routine workflow \u2014 those parents should be added to the per-environment allowlist.\n\nRationale: Across all 12 fortnight articles the common observable tail of the auth/authz-bypass exploit is the same: the vulnerable application's own runtime \u2014 node, python, java, php-fpm, .net, n8n's node, OpenClaude's node, PraisonAI's python, Portainer's golang-into-container \u2014 spawns a shell or staging LOLBin shortly after unauthenticated external traffic hits the listening port. Anchoring on the interpreter\u2192shell/LOLBin parent-child pair AND requiring co-temporal public inbound to the same host catches the joint chain while suppressing the legitimate CI/CD case where the same parent-child pair occurs without external probing.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        },
        {
          "id": "T1059.004",
          "name": "Unix Shell"
        },
        {
          "id": "T1059.001",
          "name": "PowerShell"
        },
        {
          "id": "T1059.006",
          "name": "Python"
        },
        {
          "id": "T1105",
          "name": "Ingress Tool Transfer"
        },
        {
          "id": "T1068",
          "name": "Exploitation for Privilege Escalation"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Web.Web",
        "Network_Traffic.All_Traffic"
      ],
      "splunk_spl": "| tstats `summariesonly` count as exec_count, min(_time) as firstTime, max(_time) as lastTime, values(Processes.process) as command_lines, values(Processes.user) as users, values(Processes.process_hash) as hashes from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"node\",\"node.exe\",\"python\",\"python.exe\",\"python3\",\"java\",\"java.exe\",\"php-fpm\",\"php-fpm7\",\"php-fpm8\",\"ruby\",\"gunicorn\",\"uvicorn\",\"n8n\",\"n8n.exe\",\"dotnet\",\"dotnet.exe\",\"w3wp.exe\",\"httpd\",\"nginx\") Processes.process_name IN (\"sh\",\"bash\",\"zsh\",\"dash\",\"ash\",\"cmd.exe\",\"powershell.exe\",\"pwsh\",\"pwsh.exe\",\"curl\",\"curl.exe\",\"wget\",\"wget.exe\",\"nc\",\"ncat\",\"ncat.exe\",\"python\",\"python3\",\"perl\",\"socat\") (NOT Processes.user IN (\"SYSTEM\",\"LOCAL SERVICE\",\"NETWORK SERVICE\")) (NOT (Processes.user=\"*$\")) by host, Processes.parent_process_name, Processes.process_name | `drop_dm_object_name(Processes)` | join type=inner host [ | tstats `summariesonly` dc(Web.src) as inbound_public_sources, values(Web.src) as inbound_ips, values(Web.url) as inbound_urls from datamodel=Web where Web.http_method IN (\"POST\",\"GET\",\"PUT\",\"PATCH\") Web.status<500 NOT (Web.src=\"10.0.0.0/8\" OR Web.src=\"172.16.0.0/12\" OR Web.src=\"192.168.0.0/16\" OR Web.src=\"127.0.0.0/8\") by Web.dest | `drop_dm_object_name(Web)` | rename dest as host | where inbound_public_sources>=1 ] | search NOT [| inputlookup ci_build_hosts.csv | fields host] | eval firstTime=strftime(firstTime,\"%Y-%m-%d %H:%M:%S\"), lastTime=strftime(lastTime,\"%Y-%m-%d %H:%M:%S\") | table firstTime lastTime host parent_process_name process_name exec_count command_lines users inbound_ips inbound_urls | sort - lastTime",
      "defender_kql": "let Lookback = 24h;\nlet WindowMin = 5m;\nlet InterpreterParents = dynamic([\"node.exe\",\"node\",\"python.exe\",\"python\",\"python3\",\"python3.exe\",\"java.exe\",\"java\",\"php-fpm.exe\",\"php-fpm\",\"ruby.exe\",\"ruby\",\"gunicorn\",\"uvicorn\",\"n8n.exe\",\"n8n\",\"dotnet.exe\",\"dotnet\",\"w3wp.exe\"]);\nlet ShellLolBinChildren = dynamic([\"sh\",\"bash\",\"zsh\",\"dash\",\"ash\",\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"pwsh\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\",\"nc\",\"ncat\",\"ncat.exe\",\"python.exe\",\"python\",\"python3\",\"perl\",\"socat\"]);\nlet _ci_hosts = dynamic([]);\nlet InboundProbed = DeviceNetworkEvents\n    | where Timestamp > ago(Lookback)\n    | where ActionType in (\"InboundConnectionAccepted\",\"ConnectionAccepted\",\"HttpConnectionInspected\",\"ListeningConnectionCreated\")\n    | where RemoteIPType == \"Public\"\n    | where LocalPort in (80, 443, 8000, 8080, 8443, 5678, 9000, 9443, 3000)\n    | summarize ProbeTime = min(Timestamp), Probers = make_set(RemoteIP, 25) by DeviceId, DeviceName;\nDeviceProcessEvents\n| where Timestamp > ago(Lookback)\n| where InitiatingProcessFileName in~ (InterpreterParents)\n| where FileName in~ (ShellLolBinChildren)\n| where AccountName !endswith \"$\"\n| where AccountName !in~ (\"system\",\"local service\",\"network service\")\n| where DeviceName !in~ (_ci_hosts)\n| where isnotempty(ProcessCommandLine)\n| join kind=inner InboundProbed on DeviceId\n| where Timestamp between (ProbeTime .. ProbeTime + WindowMin)\n| extend DelaySec = datetime_diff('second', Timestamp, ProbeTime)\n| project Timestamp, DelaySec, DeviceName, AccountName,\n          ParentImage = InitiatingProcessFileName,\n          ParentCmd   = InitiatingProcessCommandLine,\n          ChildImage  = FileName,\n          ChildCmd    = ProcessCommandLine,\n          ChildSHA256 = SHA256,\n          Probers\n| order by Timestamp desc"
    },
    {
      "id": "UC_WEEKLY_WEB_SERVER_PROCESS_POST_EXPLOIT_ANCHOR_PLUGIN_EXTE",
      "title": "[WEEKLY] Web-Server Process Post-Exploit Anchor: Plugin/Extension RCE Leading to Shell Spawn or Webroot Script Drop",
      "kill_chain": "install",
      "confidence": "High",
      "description": "Detects the post-exploitation anchor common to plugin/extension RCE chains: a web-server or app-server process (w3wp, httpd/apache2, nginx, php-fpm/php-cgi, java/tomcat, node) becomes the parent of an interactive shell, scripting host, or recon/download utility, OR writes a new server-side script file (*.php, *.phtml, *.jsp, *.aspx, *.cfm) into a webroot/plugins directory. This fortnight that pattern was driven by Grav CMS Direct-Install ZIP-upload RCE (CVE-2026-42607), Portainer Docker plugin-endpoint authorization bypass to host RCE (CVE-2026-44848), WordPress Burst Statistics auth-bypass admin-create (CVE-2026-8181), Avada Builder arbitrary-read + SQLi (CVE-2026-4782/4798), the Funnel Builder skimmer-injection vulnerability, the Checkmarx Jenkins AST plugin supply-chain compromise by TeamPCP, and cPanel/WHM CVE-2026-41940 Filemanager backdoor deployment \u2014 every case converges on plugin/extension endpoints as the initial wedge and on web-server-process child execution or script drop as the install-stage tell. Kill-chain stages spanned: exploit \u2192 install (and the first hop of c2/actions when the spawned utility is curl/wget/certutil). Fires on: w3wp spawning powershell/cmd with cmdline arguments, httpd/php-fpm spawning sh/bash/python, nginx-as-parent writing PHP into /user/plugins or /wp-content/plugins, java/tomcat spawning whoami/id. Does NOT fire on: package-manager updates or CI/CD deployments running under a non-web service account, admin-portal changes that never materialise as a child process or new script on disk, and webshells dropped via filesystem activity by an interactive user (those need separate UCs).\n\nRationale: Every article in scope this fortnight (Grav, Portainer, WordPress Burst/Avada/Funnel Builder, Checkmarx Jenkins AST, cPanel Filemanager) lands on the same install-stage anchor: the legitimate web/app-server process becomes the parent of a shell/utility or writes a server-side script into the webroot. Detecting on that anchor catches the joint pattern without coupling to any single CVE's URL signature or IOC set \u2014 making the UC durable as plugin endpoints and IOCs rotate week to week.",
      "implementations": [
        "splunk",
        "defender"
      ],
      "mitre_attack": [
        {
          "id": "T1190",
          "name": "Exploit Public-Facing Application"
        },
        {
          "id": "T1505.003",
          "name": "Server Software Component: Web Shell"
        },
        {
          "id": "T1059",
          "name": "Command and Scripting Interpreter"
        },
        {
          "id": "T1059.004",
          "name": "Command and Scripting Interpreter: Unix Shell"
        },
        {
          "id": "T1136.001",
          "name": "Create Account: Local Account"
        }
      ],
      "data_models": [
        "Endpoint.Processes",
        "Endpoint.Filesystem",
        "Web"
      ],
      "splunk_spl": "| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process) as parent_cmd values(Processes.user) as user from datamodel=Endpoint.Processes where Processes.parent_process_name IN (\"w3wp.exe\",\"httpd.exe\",\"httpd\",\"apache2\",\"nginx.exe\",\"nginx\",\"php-fpm\",\"php-cgi.exe\",\"php.exe\",\"java.exe\",\"javaw.exe\",\"tomcat.exe\",\"node.exe\") AND Processes.process_name IN (\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"wscript.exe\",\"cscript.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"sh\",\"bash\",\"dash\",\"zsh\",\"python\",\"python3\",\"python.exe\",\"perl\",\"perl.exe\",\"ruby\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\",\"certutil.exe\",\"bitsadmin.exe\",\"whoami.exe\",\"whoami\",\"id\",\"uname\",\"nc\",\"ncat.exe\") by Processes.dest Processes.parent_process_name Processes.process_name | `drop_dm_object_name(Processes)` | eval signal=\"webserver_spawned_shell_or_utility\" | append [ | tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_name) as file_name values(Filesystem.file_path) as file_path values(Filesystem.process_name) as parent_proc from datamodel=Endpoint.Filesystem where Filesystem.process_name IN (\"w3wp.exe\",\"httpd.exe\",\"httpd\",\"apache2\",\"nginx.exe\",\"nginx\",\"php.exe\",\"php-cgi.exe\",\"php-fpm\",\"java.exe\",\"tomcat.exe\",\"node.exe\") AND (Filesystem.file_path=\"*\\\\inetpub\\\\wwwroot\\\\*\" OR Filesystem.file_path=\"*\\\\htdocs\\\\*\" OR Filesystem.file_path=\"*\\\\webapps\\\\*\" OR Filesystem.file_path=\"*/var/www/*\" OR Filesystem.file_path=\"*/usr/share/nginx/*\" OR Filesystem.file_path=\"*/srv/http/*\" OR Filesystem.file_path=\"*/user/plugins/*\" OR Filesystem.file_path=\"*/wp-content/plugins/*\" OR Filesystem.file_path=\"*/sites/default/files/*\") AND (Filesystem.file_name=\"*.php\" OR Filesystem.file_name=\"*.phtml\" OR Filesystem.file_name=\"*.php5\" OR Filesystem.file_name=\"*.php7\" OR Filesystem.file_name=\"*.jsp\" OR Filesystem.file_name=\"*.jspx\" OR Filesystem.file_name=\"*.aspx\" OR Filesystem.file_name=\"*.asp\" OR Filesystem.file_name=\"*.cfm\" OR Filesystem.file_name=\"*.cgi\") by Filesystem.dest Filesystem.process_name Filesystem.file_name | `drop_dm_object_name(Filesystem)` | eval signal=\"webserver_wrote_script_to_webroot\" ] | eval risk_message=\"Web-server process post-exploit anchor \u2014 likely plugin/extension RCE chain (Grav/Portainer/WordPress/Jenkins/cPanel UCs in scope)\" | convert ctime(firstTime) ctime(lastTime) | sort - lastTime",
      "defender_kql": "let WebProcs = dynamic([\"w3wp.exe\",\"httpd.exe\",\"nginx.exe\",\"php-fpm\",\"php-cgi.exe\",\"php.exe\",\"java.exe\",\"javaw.exe\",\"tomcat.exe\",\"node.exe\",\"apache2\"]);\nlet ShellProcs = dynamic([\"cmd.exe\",\"powershell.exe\",\"pwsh.exe\",\"wscript.exe\",\"cscript.exe\",\"mshta.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"sh\",\"bash\",\"dash\",\"zsh\",\"python\",\"python3\",\"python.exe\",\"perl\",\"perl.exe\",\"ruby\",\"curl.exe\",\"curl\",\"wget.exe\",\"wget\",\"certutil.exe\",\"bitsadmin.exe\",\"whoami.exe\",\"whoami\",\"id\",\"uname\",\"nc\",\"ncat.exe\"]);\nlet WebShellExt = dynamic([\".php\",\".phtml\",\".php5\",\".php7\",\".jsp\",\".jspx\",\".aspx\",\".asp\",\".cfm\",\".cgi\"]);\nlet WebrootPaths = dynamic([@\"\\inetpub\\wwwroot\",@\"\\htdocs\\\",@\"\\webapps\\\",@\"\\xampp\\htdocs\\\",\"/var/www\",\"/usr/share/nginx\",\"/srv/http\",\"/user/plugins/\",\"/wp-content/plugins/\",\"/sites/default/files/\"]);\nlet Spawn = DeviceProcessEvents\n    | where Timestamp > ago(7d)\n    | where AccountName !endswith \"$\"\n    | where InitiatingProcessFileName in~ (WebProcs)\n    | where FileName in~ (ShellProcs)\n    | project Timestamp, DeviceName, AccountName,\n              ParentImage = InitiatingProcessFileName,\n              ParentCmd = InitiatingProcessCommandLine,\n              ChildImage = FileName,\n              ChildCmd = ProcessCommandLine,\n              EventKind = \"WebServerSpawnsShellOrUtility\";\nlet Drop = DeviceFileEvents\n    | where Timestamp > ago(7d)\n    | where ActionType in (\"FileCreated\",\"FileRenamed\")\n    | where InitiatingProcessFileName in~ (WebProcs)\n    | where FolderPath has_any (WebrootPaths)\n    | where FileName has_any (WebShellExt)\n    | project Timestamp, DeviceName,\n              AccountName = InitiatingProcessAccountName,\n              ParentImage = InitiatingProcessFileName,\n              ParentCmd = InitiatingProcessCommandLine,\n              ChildImage = FileName,\n              ChildCmd = FolderPath,\n              EventKind = \"WebServerWritesScriptToWebroot\";\nunion Spawn, Drop\n| order by Timestamp desc"
    }
  ]
}