diff --git a/filters/audits/vmware.md b/filters/audits/vmware.md new file mode 100644 index 000000000..2b8a83cb4 --- /dev/null +++ b/filters/audits/vmware.md @@ -0,0 +1,42 @@ +# VMware ESXi normalization and rule review + +Use origin.host, canonical authentication failures, and corresponding alert-side grouping names. + +This draft targets UTMStack `v11`. It contains 1 filter changes +and 14 rule changes for this technology only. Review covered +1 filter configurations and 14 matching shipped rule files. +Unchanged rules are listed in the regression manifest; they are not duplicated in the diff. + +## Contract and validation + +- Compared exact standard names/types with go-sdk v1.1.31 and the supplied UTMStack dictionaries. +- Checked documented pipeline ordering, rename/move behavior, open vendor log fields, + event-side versus alert-side fields, and surviving fields used by affected rule predicates/history/grouping. +- Strict SDK configuration decoding and actual CEL compilation pass for this scope. +- 1 synthetic normalization cases pass, including SDK Event conversion and any + trigger predicate assertions recorded in the manifest. +- The scoped alerts module tests and `git diff --check` pass with the shared contract runner applied. + +The shared alert-contract PR supplies the reusable Go runner for the manifest in +`plugins/alerts/testdata/filter-contracts/vmware.json`. Apply that support before running `go test ./...` in `plugins/alerts`. + +The changed rules also require the shared alert-grouping fix to resolve `lastEvent.*` values correctly at runtime. + +The model starts from synthetic extraction results. It does not run complex grok, +JSON/KV/XML/CSV extraction, time conversion, dynamic plugins, historical OpenSearch +queries, or the closed EventProcessor. Raw vendor logs and resulting alerts must +still be checked in staging before rollout. No customer false-positive reduction +has been measured and no production rollout is included. + + + +## References + +- [SDK schema](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/plugins.proto) +- [Filter steps](https://github.com/threatwinds/go-sdk/wiki/Filter-Steps-Reference) +- [Standard event schema](https://github.com/threatwinds/go-sdk/wiki/Standard-Event-Schema) +- [Rule implementation](https://github.com/threatwinds/go-sdk/wiki/Implementing-Rules) + +`afterEvents`, empty noncapturing grok names, supported numeric strings, and custom +`log.*` fields are accepted. Existing textual protocol casing and vendor action names +are preserved unless a concrete consumer mismatch requires correction. diff --git a/filters/vmware/vmware-esxi.yml b/filters/vmware/vmware-esxi.yml index b2f58acc1..47f2dd0a9 100644 --- a/filters/vmware/vmware-esxi.yml +++ b/filters/vmware/vmware-esxi.yml @@ -19,7 +19,7 @@ pipeline: pattern: '\<{{.data}}\>' - fieldName: log.deviceTime pattern: '{{.year}}(-){{.monthNumber}}(-){{.monthDay}}(T){{.time}}(Z)' - - fieldName: origin.hostname + - fieldName: origin.host pattern: '{{.hostname}}' - fieldName: log.process pattern: '{{.hostname}}(\:)' @@ -40,7 +40,7 @@ pipeline: pattern: '\<{{.data}}\>' - fieldName: log.deviceTime pattern: '{{.year}}(-){{.monthNumber}}(-){{.monthDay}}(T){{.time}}(Z)' - - fieldName: origin.hostname + - fieldName: origin.host pattern: '{{.hostname}}' - fieldName: log.process pattern: '{{.hostname}}' @@ -55,7 +55,7 @@ pipeline: pattern: '\<{{.data}}\>' - fieldName: log.deviceTime pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}T{{.time}}Z' - - fieldName: origin.hostname + - fieldName: origin.host pattern: '{{.hostname}}' - fieldName: log.process pattern: '{{.hostname}}' @@ -136,14 +136,14 @@ pipeline: function: string params: key: actionResult - value: "failed" - where: 'exists("log.message") && contains("log.message", "authentication failed")' + value: failure + where: exists("log.message") && contains("log.message", "authentication failed") - add: function: string params: key: actionResult - value: "failed" - where: 'exists("log.message") && contains("log.message", "authentication of user") && contains("log.message", "failed") && !exists("actionResult")' + value: failure + where: exists("log.message") && contains("log.message", "authentication of user") && contains("log.message", "failed") && !exists("actionResult") - add: function: string params: diff --git a/plugins/alerts/testdata/filter-contracts/vmware.json b/plugins/alerts/testdata/filter-contracts/vmware.json new file mode 100644 index 000000000..ac55167ea --- /dev/null +++ b/plugins/alerts/testdata/filter-contracts/vmware.json @@ -0,0 +1,44 @@ +{ + "technology": "VMware ESXi", + "filters": [ + "filters/vmware/vmware-esxi.yml" + ], + "rules": [ + "rules/vmware/vmware-esxi/esxi_account_manipulation.yml", + "rules/vmware/vmware-esxi/esxi_disk_theft.yml", + "rules/vmware/vmware-esxi/esxi_firewall_modification.yml", + "rules/vmware/vmware-esxi/esxi_host_compromise.yml", + "rules/vmware/vmware-esxi/esxi_ransomware_detection.yml", + "rules/vmware/vmware-esxi/esxi_ssh_access.yml", + "rules/vmware/vmware-esxi/esxi_syslog_disruption.yml", + "rules/vmware/vmware-esxi/esxi_vib_sideloading.yml", + "rules/vmware/vmware-esxi/hypervisor_escape_attempts.yml", + "rules/vmware/vmware-esxi/powercli_script_execution.yml", + "rules/vmware/vmware-esxi/vcenter_server_attacks.yml", + "rules/vmware/vmware-esxi/vm_escape_detection.yml", + "rules/vmware/vmware-esxi/vmware_tools_vulnerabilities.yml", + "rules/vmware/vmware-esxi/vsphere_api_abuse.yml" + ], + "fixtures": [ + { + "name": "vmware_auth_failure", + "filter": "vmware/vmware-esxi.yml", + "input": { + "origin": { + "host": "esxi-lab" + }, + "log": { + "message": "authentication failed" + } + }, + "expected": { + "origin.host": "esxi-lab", + "actionResult": "failure" + }, + "absent": [ + "origin.hostname" + ], + "rules": {} + } + ] +} diff --git a/rules/vmware/vmware-esxi/esxi_account_manipulation.yml b/rules/vmware/vmware-esxi/esxi_account_manipulation.yml index 15548cd36..fada1bb8f 100644 --- a/rules/vmware/vmware-esxi/esxi_account_manipulation.yml +++ b/rules/vmware/vmware-esxi/esxi_account_manipulation.yml @@ -40,4 +40,4 @@ where: | )) ) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/esxi_disk_theft.yml b/rules/vmware/vmware-esxi/esxi_disk_theft.yml index 59b3b0ebb..13ba6b8fd 100644 --- a/rules/vmware/vmware-esxi/esxi_disk_theft.yml +++ b/rules/vmware/vmware-esxi/esxi_disk_theft.yml @@ -40,5 +40,5 @@ where: | (contains("log.message", "Datastore") && contains("log.message", "browse") && contains("log.message", ".vmdk")) ) groupBy: - - adversary.hostname + - adversary.host - adversary.ip diff --git a/rules/vmware/vmware-esxi/esxi_firewall_modification.yml b/rules/vmware/vmware-esxi/esxi_firewall_modification.yml index 8e924db50..03685de0d 100644 --- a/rules/vmware/vmware-esxi/esxi_firewall_modification.yml +++ b/rules/vmware/vmware-esxi/esxi_firewall_modification.yml @@ -34,7 +34,7 @@ where: | contains("log.message", "set --allowed-all true") || contains("log.message", "set --enabled") )) || - (contains("log.message", "iptables") && exists("origin.hostname")) + (contains("log.message", "iptables") && exists("origin.host")) ) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/esxi_host_compromise.yml b/rules/vmware/vmware-esxi/esxi_host_compromise.yml index 3216988d9..82a6e4028 100644 --- a/rules/vmware/vmware-esxi/esxi_host_compromise.yml +++ b/rules/vmware/vmware-esxi/esxi_host_compromise.yml @@ -42,6 +42,6 @@ where: | contains("log.message", "log events removed") || (equals("log.process", "vmkernel") && contains("log.message", "SCSI sense")) || (contains("log.eventInfo", "ransom") || contains("log.eventInfo", "encrypt"))) && - exists("origin.hostname") + exists("origin.host") groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/esxi_ransomware_detection.yml b/rules/vmware/vmware-esxi/esxi_ransomware_detection.yml index c3e00ebff..3c50f57a9 100644 --- a/rules/vmware/vmware-esxi/esxi_ransomware_detection.yml +++ b/rules/vmware/vmware-esxi/esxi_ransomware_detection.yml @@ -37,4 +37,4 @@ where: | (contains("log.message", "chmod") && contains("log.message", "+x") && contains("log.message", ".sh")) ) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/esxi_ssh_access.yml b/rules/vmware/vmware-esxi/esxi_ssh_access.yml index 9054812d6..8df96e6ba 100644 --- a/rules/vmware/vmware-esxi/esxi_ssh_access.yml +++ b/rules/vmware/vmware-esxi/esxi_ssh_access.yml @@ -33,5 +33,5 @@ where: | (contains("log.message", "ssh") && contains("log.message", "connection from")) ) groupBy: - - adversary.hostname + - adversary.host - adversary.ip diff --git a/rules/vmware/vmware-esxi/esxi_syslog_disruption.yml b/rules/vmware/vmware-esxi/esxi_syslog_disruption.yml index a112529cd..09e637d95 100644 --- a/rules/vmware/vmware-esxi/esxi_syslog_disruption.yml +++ b/rules/vmware/vmware-esxi/esxi_syslog_disruption.yml @@ -37,4 +37,4 @@ where: | (contains("log.message", "Syslog.global.logHost") && contains("log.message", "changed")) ) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/esxi_vib_sideloading.yml b/rules/vmware/vmware-esxi/esxi_vib_sideloading.yml index bf89be95d..aedb53971 100644 --- a/rules/vmware/vmware-esxi/esxi_vib_sideloading.yml +++ b/rules/vmware/vmware-esxi/esxi_vib_sideloading.yml @@ -33,4 +33,4 @@ where: | (contains("log.message", "esxcli software acceptance") && contains("log.message", "set")) ) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/hypervisor_escape_attempts.yml b/rules/vmware/vmware-esxi/hypervisor_escape_attempts.yml index 179e09f36..9bf3339a1 100644 --- a/rules/vmware/vmware-esxi/hypervisor_escape_attempts.yml +++ b/rules/vmware/vmware-esxi/hypervisor_escape_attempts.yml @@ -33,4 +33,4 @@ where: | (regexMatch("log.message", "(?i)CVE-2024-37085")) || (regexMatch("log.message", "(?i)VMCI") && regexMatch("log.message", "(?i)(backdoor|socket|unauthorized)")) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/powercli_script_execution.yml b/rules/vmware/vmware-esxi/powercli_script_execution.yml index 7e5c6b222..249c3d665 100644 --- a/rules/vmware/vmware-esxi/powercli_script_execution.yml +++ b/rules/vmware/vmware-esxi/powercli_script_execution.yml @@ -30,4 +30,4 @@ where: | (regexMatch("log.message", "(?i)Syslog\\.global\\.log") && regexMatch("log.message", "(?i)Set-VMHost")) || regexMatch("log.message", "(?i)Get-VMHostSysLogServer") groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/vcenter_server_attacks.yml b/rules/vmware/vmware-esxi/vcenter_server_attacks.yml index c57f7437e..504be103b 100644 --- a/rules/vmware/vmware-esxi/vcenter_server_attacks.yml +++ b/rules/vmware/vmware-esxi/vcenter_server_attacks.yml @@ -38,13 +38,13 @@ where: | contains("log.message", "VIB install") || contains("log.message", "authentication failed") || (equals("log.process", "vpxd") && equals("severity", "error"))) && - exists("origin.hostname") + exists("origin.host") afterEvents: - indexPattern: v11-log-vmware-esxi-* with: - - field: origin.hostname + - field: origin.host operator: filter_term - value: '{{.origin.hostname}}' + value: '{{.origin.host}}' within: 30m count: 5 groupBy: diff --git a/rules/vmware/vmware-esxi/vm_escape_detection.yml b/rules/vmware/vmware-esxi/vm_escape_detection.yml index fc90cbdde..f97254e7e 100644 --- a/rules/vmware/vmware-esxi/vm_escape_detection.yml +++ b/rules/vmware/vmware-esxi/vm_escape_detection.yml @@ -34,4 +34,4 @@ where: | (regexMatch("log.eventInfo", "(?i)(vm.*escape|breakout|containment.*breach)")) groupBy: - lastEvent.log.process - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/vmware_tools_vulnerabilities.yml b/rules/vmware/vmware-esxi/vmware_tools_vulnerabilities.yml index 11428f501..cea3b485e 100644 --- a/rules/vmware/vmware-esxi/vmware_tools_vulnerabilities.yml +++ b/rules/vmware/vmware-esxi/vmware_tools_vulnerabilities.yml @@ -35,10 +35,10 @@ where: | afterEvents: - indexPattern: v11-log-vmware-esxi-* with: - - field: origin.hostname + - field: origin.host operator: filter_term - value: '{{.origin.hostname}}' + value: '{{.origin.host}}' within: 15m count: 5 groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/vsphere_api_abuse.yml b/rules/vmware/vmware-esxi/vsphere_api_abuse.yml index cb25a9c1d..eee01dfb9 100644 --- a/rules/vmware/vmware-esxi/vsphere_api_abuse.yml +++ b/rules/vmware/vmware-esxi/vsphere_api_abuse.yml @@ -43,11 +43,11 @@ where: | afterEvents: - indexPattern: v11-log-vmware-esxi-* with: - - field: origin.hostname + - field: origin.host operator: filter_term - value: '{{.origin.hostname}}' + value: '{{.origin.host}}' within: 5m count: 10 groupBy: - lastEvent.log.eventInfo - - adversary.hostname + - adversary.host