From 88bc26147470c862a3527cb30c72dd7c05f040ae Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Wed, 16 Sep 2026 20:34:13 -0400 Subject: [PATCH 1/3] fix: align Bitdefender GravityZone filter and rule contracts --- filters/antivirus/bitdefender_gz.yml | 59 +++++- filters/audits/bitdefender.md | 42 +++++ .../filter-contracts/bitdefender.json | 170 ++++++++++++++++++ .../bitdefender_gz/apt_detection.yml | 2 +- .../high_severity_threat_detection.yml | 2 +- .../malware_outbreak_multiple_hosts.yml | 2 +- .../multiple_malware_from_single_source.yml | 2 +- .../network_threat_detection.yml | 2 +- .../phishing_access_blocked.yaml | 2 +- .../quarantine_failure_detection.yml | 2 +- 10 files changed, 271 insertions(+), 14 deletions(-) create mode 100644 filters/audits/bitdefender.md create mode 100644 plugins/alerts/testdata/filter-contracts/bitdefender.json diff --git a/filters/antivirus/bitdefender_gz.yml b/filters/antivirus/bitdefender_gz.yml index bc73922ea..ea2df8ed2 100644 --- a/filters/antivirus/bitdefender_gz.yml +++ b/filters/antivirus/bitdefender_gz.yml @@ -504,8 +504,8 @@ pipeline: - rename: from: - - log.severity - to: severity + - log.severity + to: log.cefSeverity - rename: from: @@ -560,23 +560,30 @@ pipeline: - rename: from: - - log.BitdefenderGZEventSourceIP + - log.BitdefenderGZEventSourceIP to: origin.ip + where: '!exists("origin.ip")' # Adding actionResult field to indicate whether the action was successful or failed + - add: + function: string + params: + key: actionResult + value: denied + where: oneOf("action", ["blocked", "block", "aph_blocked", "portscan_blocked", "quarantined"]) - add: function: string params: key: actionResult value: success - where: 'oneOf("action", ["blocked", "block", "aph_blocked", "portscan_blocked", "deleted", "disinfected", "quarantined", "restored"])' + where: oneOf("action", ["deleted", "disinfected", "restored"]) - add: function: string params: key: actionResult - value: failed - where: 'oneOf("action", ["still present", "ignored", "no action", "reportOnly"])' + value: failure + where: oneOf("action", ["still present", "ignored", "no action", "reportOnly"]) # Adding geolocation to origin ip - dynamic: @@ -620,4 +627,42 @@ pipeline: - log.dvc - log.request - log.suser - - log.fname \ No newline at end of file + - log.fname + + # Keep addresses in IP fields and retain other source values under log. + - rename: + from: + - origin.ip + to: log.unparsedOriginIp + where: exists("origin.ip") && (!(inCIDR("origin.ip","0.0.0.0/0") || inCIDR("origin.ip","::/0")) || oneOf("origin.ip",["0.0.0.0","::"])) + - rename: + from: + - target.ip + to: log.unparsedTargetIp + where: exists("target.ip") && (!(inCIDR("target.ip","0.0.0.0/0") || inCIDR("target.ip","::/0")) || oneOf("target.ip",["0.0.0.0","::"])) + + # Normalize the source event severity. + - add: + function: string + params: + key: severity + value: info + where: (greaterOrEqual("log.cefSeverity",0) && lessOrEqual("log.cefSeverity",3)) || oneOf("log.cefSeverity",["Low","low","Unknown"]) + - add: + function: string + params: + key: severity + value: warning + where: (greaterOrEqual("log.cefSeverity",4) && lessOrEqual("log.cefSeverity",6)) || oneOf("log.cefSeverity",["Medium","medium"]) + - add: + function: string + params: + key: severity + value: error + where: (greaterOrEqual("log.cefSeverity",7) && lessOrEqual("log.cefSeverity",8)) || oneOf("log.cefSeverity",["High","high"]) + - add: + function: string + params: + key: severity + value: critical + where: (greaterOrEqual("log.cefSeverity",9) && lessOrEqual("log.cefSeverity",10)) || oneOf("log.cefSeverity",["Very-High","Very High","very-high"]) diff --git a/filters/audits/bitdefender.md b/filters/audits/bitdefender.md new file mode 100644 index 000000000..19096a6ac --- /dev/null +++ b/filters/audits/bitdefender.md @@ -0,0 +1,42 @@ +# Bitdefender GravityZone normalization and rule review + +Preserve attacker IPs, normalize response outcomes and severity, and align phishing/priority rules. + +This draft targets UTMStack `v11`. It contains 1 filter changes +and 7 rule changes for this technology only. Review covered +1 filter configurations and 21 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. +- 8 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/bitdefender.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/plugins/alerts/testdata/filter-contracts/bitdefender.json b/plugins/alerts/testdata/filter-contracts/bitdefender.json new file mode 100644 index 000000000..aeae2463e --- /dev/null +++ b/plugins/alerts/testdata/filter-contracts/bitdefender.json @@ -0,0 +1,170 @@ +{ + "technology": "Bitdefender GravityZone", + "filters": [ + "filters/antivirus/bitdefender_gz.yml" + ], + "rules": [ + "rules/antivirus/bitdefender_gz/antivirus_service_stopped.yml", + "rules/antivirus/bitdefender_gz/apt_detection.yml", + "rules/antivirus/bitdefender_gz/av_console_lateral_movement.yml", + "rules/antivirus/bitdefender_gz/av_policy_override.yml", + "rules/antivirus/bitdefender_gz/bootkit_detection.yml", + "rules/antivirus/bitdefender_gz/crypto_mining_detection.yml", + "rules/antivirus/bitdefender_gz/email_threat_spreading.yml", + "rules/antivirus/bitdefender_gz/fileless_malware_detection.yml", + "rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml", + "rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml", + "rules/antivirus/bitdefender_gz/memory_threat_detection.yml", + "rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml", + "rules/antivirus/bitdefender_gz/network_threat_detection.yml", + "rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml", + "rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml", + "rules/antivirus/bitdefender_gz/ransomware_behavior_detection.yml", + "rules/antivirus/bitdefender_gz/realtime_protection_disabled.yml", + "rules/antivirus/bitdefender_gz/rootkit_detection.yml", + "rules/antivirus/bitdefender_gz/suspicious_exclusions_added.yml", + "rules/antivirus/bitdefender_gz/usb_malware_propagation.yml", + "rules/antivirus/bitdefender_gz/zero_day_malware_detection.yml" + ], + "fixtures": [ + { + "name": "Bitdefender phishing aph_blocked", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "actFull": "aph_blocked", + "BitdefenderGZModule": "aph" + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml": false + } + }, + { + "name": "Bitdefender phishing reportOnly", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "actFull": "reportOnly", + "BitdefenderGZModule": "aph" + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml": true + } + }, + { + "name": "Bitdefender attacker priority", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "BitdefenderGZDetectionAttackerIp": "198.51.100.10", + "BitdefenderGZEventSourceIP": "10.0.0.2" + } + }, + "expected": { + "origin.ip": "198.51.100.10" + }, + "absent": [], + "rules": {} + }, + { + "name": "Bitdefender CEF priority 0", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "severity": "0", + "BitdefenderGZModule": "network-monitor" + } + }, + "expected": { + "severity": "info", + "log.cefSeverity": "0" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/network_threat_detection.yml": false + } + }, + { + "name": "Bitdefender CEF priority 3", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "severity": "3", + "BitdefenderGZModule": "network-monitor" + } + }, + "expected": { + "severity": "info", + "log.cefSeverity": "3" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/network_threat_detection.yml": false + } + }, + { + "name": "Bitdefender CEF priority 6", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "severity": "6", + "BitdefenderGZModule": "network-monitor" + } + }, + "expected": { + "severity": "warning", + "log.cefSeverity": "6" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/network_threat_detection.yml": false + } + }, + { + "name": "Bitdefender CEF priority 8", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "severity": "8", + "BitdefenderGZModule": "network-monitor" + } + }, + "expected": { + "severity": "error", + "log.cefSeverity": "8" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/network_threat_detection.yml": true + } + }, + { + "name": "Bitdefender CEF priority 10", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "severity": "10", + "BitdefenderGZModule": "network-monitor" + } + }, + "expected": { + "severity": "critical", + "log.cefSeverity": "10" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/network_threat_detection.yml": true + } + } + ] +} diff --git a/rules/antivirus/bitdefender_gz/apt_detection.yml b/rules/antivirus/bitdefender_gz/apt_detection.yml index 8d91eab8b..5f30c2629 100644 --- a/rules/antivirus/bitdefender_gz/apt_detection.yml +++ b/rules/antivirus/bitdefender_gz/apt_detection.yml @@ -40,7 +40,7 @@ description: | 5. Collect forensic artifacts before remediating - memory image and endpoint logs - since a targeted intrusion warrants attribution work 6. Isolate the endpoint if the detection action shows the threat was not blocked, then hunt for what ran while it was active where: | - greaterOrEqual("severity", 8) && + (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) && ( (equals("log.BitdefenderGZModule", "hd") && regexMatch("log.BitdefenderGZAttackTypes", "(?i)targeted attack")) || diff --git a/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml b/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml index d513c6d45..524628e6b 100644 --- a/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml +++ b/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml @@ -38,7 +38,7 @@ description: | - Check that signatures were current at deviceTime, using log.BitdefenderGZSignaturesNumber where: | oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - greaterOrEqual("severity", 8) + (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) groupBy: - target.host - target.malware diff --git a/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml b/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml index 0327d9b5d..e26dcc8ce 100644 --- a/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml +++ b/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml @@ -33,7 +33,7 @@ description: | 7. Keep the incident open until a full day passes with no new host reporting the same malware where: | oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - greaterOrEqual("severity", 8) && + (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) && exists("target.malware") correlation: - indexPattern: v11-log-antivirus-bitdefender-gz-* diff --git a/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml b/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml index f045cae50..4bb04a2a7 100644 --- a/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml +++ b/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml @@ -35,7 +35,7 @@ description: | 7. Reimage if the same host keeps reappearing in this rule across days where: | oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - greaterOrEqual("severity", 8) + (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) correlation: - indexPattern: v11-log-antivirus-bitdefender-gz-* within: 1h diff --git a/rules/antivirus/bitdefender_gz/network_threat_detection.yml b/rules/antivirus/bitdefender_gz/network_threat_detection.yml index b452ab552..7153b51ff 100644 --- a/rules/antivirus/bitdefender_gz/network_threat_detection.yml +++ b/rules/antivirus/bitdefender_gz/network_threat_detection.yml @@ -37,7 +37,7 @@ description: | 6. Block the source at the perimeter, and only then close the alert. A blocked attempt means this attack failed, not that the attacker stopped where: | oneOf("log.BitdefenderGZModule", ["network-monitor", "fw"]) && - greaterOrEqual("severity", 8) + (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) correlation: - indexPattern: v11-log-antivirus-bitdefender-gz-* within: 2h diff --git a/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml b/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml index 19fe39758..3fa3b1d69 100644 --- a/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml +++ b/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml @@ -37,7 +37,7 @@ description: | 7. Submit the URL for blocking at the perimeter so the rest of the estate is covered where: | equals("log.BitdefenderGZModule", "aph") && - equals("actionResult", "success") + equals("action", "reportOnly") groupBy: - target.user - adversary.url diff --git a/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml b/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml index d83430101..f5cb7762d 100644 --- a/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml +++ b/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml @@ -42,7 +42,7 @@ description: | where: | equals("log.eventType", "AntiMalware") && ( - equals("actionResult", "failed") || + oneOf("actionResult", ["failure", "failed"]) || (greaterOrEqual("log.BitdefenderGZPresentMalwareCnt", 1) && equals("log.BitdefenderGZQuarantinedMalwareCnt", 0) && equals("log.BitdefenderGZCleanedMalwareCnt", 0)) From 164cc40cea3c0acfb3f7151950cde9158e9001e2 Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Thu, 17 Sep 2026 18:07:27 -0400 Subject: [PATCH 2/3] fix(bitdefender): correct CEF mappings and scoped detections --- filters/antivirus/bitdefender_gz.yml | 2223 ++++++++++++----- filters/audits/bitdefender.md | 123 +- plugins/alerts/bitdefender_contract_test.go | 466 ++++ plugins/alerts/bitdefender_history_test.go | 264 ++ plugins/alerts/testdata/bitdefender_raw.json | 891 +++++++ .../filter-contracts/bitdefender.json | 166 +- .../antivirus_service_stopped.yml | 47 +- .../bitdefender_gz/apt_detection.yml | 60 +- .../av_console_lateral_movement.yml | 81 +- .../bitdefender_gz/av_policy_override.yml | 53 +- .../bitdefender_gz/bootkit_detection.yml | 58 +- .../crypto_mining_detection.yml | 50 +- .../bitdefender_gz/email_threat_spreading.yml | 46 +- .../fileless_malware_detection.yml | 54 +- .../high_severity_threat_detection.yml | 51 +- .../malware_outbreak_multiple_hosts.yml | 77 +- .../memory_threat_detection.yml | 52 +- .../multiple_malware_from_single_source.yml | 69 +- .../network_threat_detection.yml | 76 +- .../phishing_access_blocked.yaml | 52 +- .../quarantine_failure_detection.yml | 59 +- .../ransomware_behavior_detection.yml | 51 +- .../realtime_protection_disabled.yml | 37 +- .../bitdefender_gz/rootkit_detection.yml | 45 +- .../suspicious_exclusions_added.yml | 46 +- .../usb_malware_propagation.yml | 71 +- .../zero_day_malware_detection.yml | 48 +- 27 files changed, 3804 insertions(+), 1512 deletions(-) create mode 100644 plugins/alerts/bitdefender_contract_test.go create mode 100644 plugins/alerts/bitdefender_history_test.go create mode 100644 plugins/alerts/testdata/bitdefender_raw.json diff --git a/filters/antivirus/bitdefender_gz.yml b/filters/antivirus/bitdefender_gz.yml index ea2df8ed2..557f3f143 100644 --- a/filters/antivirus/bitdefender_gz.yml +++ b/filters/antivirus/bitdefender_gz.yml @@ -1,668 +1,1557 @@ -# Bitdefender GravityZone filter, version 3.1.0 -# Based on https://www.bitdefender.com/business/support/en/77212-237089-event-types.html -# and the previous version of the same filter - +# Bitdefender GravityZone CEF filter, version 3.2.0 +# CEF-encoded originals remain under log; no unsupported decoder is assumed. +# Re-read the protected raw header after generic KV so extension text cannot overwrite it. pipeline: - - dataTypes: - - antivirus-bitdefender-gz - steps: - # Using grok to parse header of the message - - grok: - patterns: - - fieldName: log.syslogPriority - pattern: '\<{{.data}}\>' - - fieldName: log.syslogVersion - pattern: '{{.integer}}' - - fieldName: log.syslogDeviceTime - pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}\w{{.time}}\w' - - fieldName: log.syslogHostIP - pattern: '{{.ipv4}}|{{.ipv6}}|{{.word}}' - - fieldName: log.notDefined - pattern: '{{.integer}}' - - fieldName: log.0trash - pattern: '{{.word}}\:{{.integer}}' - - fieldName: log.restData - pattern: '{{.greedy}}' - source: raw - - - grok: - patterns: - - fieldName: log.syslogPriority - pattern: '\<{{.data}}\>' - - fieldName: log.syslogVersion - pattern: '{{.integer}}' - - fieldName: log.syslogDeviceTime - pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}\w{{.time}}\w' - - fieldName: log.hostId - pattern: '{{.word}}' - - fieldName: log.0trash - pattern: '{{.word}}' - - fieldName: log.processPid - pattern: '\[{{.integer}}\]' - - fieldName: log.1trash - pattern: '{{.word}}\:{{.integer}}' - - fieldName: log.restData - pattern: '{{.greedy}}' - source: raw - - - grok: - patterns: - - fieldName: log.syslogPriority - pattern: '\<{{.data}}\>' - - fieldName: log.syslogDeviceTime - pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}\w{{.time}}\w' - - fieldName: log.hostId - pattern: '{{.word}}' - - fieldName: log.0trash - pattern: '{{.word}}' - - fieldName: log.processPid - pattern: '\[{{.integer}}\]' - - fieldName: log.1trash - pattern: '{{.word}}\:{{.integer}}' - - fieldName: log.restData - pattern: '{{.greedy}}' - source: raw - - - grok: - patterns: - - fieldName: log.syslogPriority - pattern: '\<{{.data}}\>' - - fieldName: log.syslogVersion - pattern: '{{.integer}}' - - fieldName: log.syslogDeviceTime - pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}\w{{.time}}\w' - - fieldName: log.syslogHostIP - pattern: '{{.ipv4}}|{{.ipv6}}|{{.word}}' - - fieldName: log.0trash - pattern: '{{.word}}\:{{.integer}}' - - fieldName: log.restData - pattern: '{{.greedy}}' - source: raw - - - grok: - patterns: - - fieldName: log.syslogPriority - pattern: '\<{{.data}}\>' - - fieldName: log.syslogVersion - pattern: '{{.integer}}' - - fieldName: log.syslogDeviceTime - pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}\w{{.time}}\w' - - fieldName: log.restData - pattern: '{{.greedy}}' - source: raw - - - grok: - patterns: - - fieldName: log.syslogPriority - pattern: '\<{{.data}}\>' - - fieldName: log.0trash - pattern: '{{.word}}\:{{.integer}}' - - fieldName: log.restData - pattern: '{{.greedy}}' - source: raw - - - grok: - patterns: - - fieldName: log.cefVersion - pattern: 'CEF\:{{.integer}}' - - fieldName: log.restData - pattern: '{{.greedy}}' - source: raw - - # Using grok to parse components of the cef_message - - grok: - patterns: - - fieldName: log.productVendor - pattern: '\|{{.data}}\|' - - fieldName: log.product - pattern: '{{.data}}\|' - - fieldName: log.productVersion - pattern: '{{.data}}\|' - - fieldName: log.signatureID - pattern: '{{.data}}\|' - - fieldName: log.eventType - pattern: '{{.data}}\|' - - fieldName: log.severity - pattern: '{{.data}}\|' - - fieldName: log.restData - pattern: '{{.greedy}}' - source: log.restData - - # ---- CEF-aware extraction of space-bearing extension values ---- - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}dvc=' - - fieldName: log.dvcToParse - pattern: '{{.data}}{{.word}}\=' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.restData - - - grok: - patterns: - - fieldName: log.dvcFull - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.dvcToParse - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}dvc=' - - fieldName: log.dvcFull - pattern: '{{.greedy}}' - source: log.restData - where: '!exists("log.dvcFull")' - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}request=' - - fieldName: log.requestToParse - pattern: '{{.data}}{{.word}}\=' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.restData - - - grok: - patterns: - - fieldName: log.requestFull - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.requestToParse - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}request=' - - fieldName: log.requestFull - pattern: '{{.greedy}}' - source: log.restData - where: '!exists("log.requestFull")' - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}act=' - - fieldName: log.actToParse - pattern: '{{.data}}{{.word}}\=' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.restData - - - grok: - patterns: - - fieldName: log.actFull - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.actToParse - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}act=' - - fieldName: log.actFull - pattern: '{{.greedy}}' - source: log.restData - where: '!exists("log.actFull")' - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}sproc=' - - fieldName: log.sprocToParse - pattern: '{{.data}}{{.word}}\=' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.restData - - - grok: - patterns: - - fieldName: log.sprocFull - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.sprocToParse - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}sproc=' - - fieldName: log.sprocFull - pattern: '{{.greedy}}' - source: log.restData - where: '!exists("log.sprocFull")' - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}filePath=' - - fieldName: log.filePathToParse - pattern: '{{.data}}{{.word}}\=' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.restData - - - grok: - patterns: - - fieldName: log.filePathFull - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.filePathToParse - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}filePath=' - - fieldName: log.filePathFull - pattern: '{{.greedy}}' - source: log.restData - where: '!exists("log.filePathFull")' - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}BitdefenderGZDetectionName=' - - fieldName: log.BitdefenderGZDetectionNameToParse - pattern: '{{.data}}{{.word}}\=' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.restData - - - grok: - patterns: - - fieldName: log.BitdefenderGZDetectionNameFull - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.BitdefenderGZDetectionNameToParse - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}BitdefenderGZDetectionName=' - - fieldName: log.BitdefenderGZDetectionNameFull - pattern: '{{.greedy}}' - source: log.restData - where: '!exists("log.BitdefenderGZDetectionNameFull")' - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}BitdefenderGZAttackTypes=' - - fieldName: log.BitdefenderGZAttackTypesToParse - pattern: '{{.data}}{{.word}}\=' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.restData - - - grok: - patterns: - - fieldName: log.BitdefenderGZAttackTypesFull - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.BitdefenderGZAttackTypesToParse - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}BitdefenderGZAttackTypes=' - - fieldName: log.BitdefenderGZAttackTypesFull - pattern: '{{.greedy}}' - source: log.restData - where: '!exists("log.BitdefenderGZAttackTypesFull")' - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}suser=' - - fieldName: log.suserToParse - pattern: '{{.data}}{{.word}}\=' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.restData - - - grok: - patterns: - - fieldName: log.suserFull - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.suserToParse - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}suser=' - - fieldName: log.suserFull - pattern: '{{.greedy}}' - source: log.restData - where: '!exists("log.suserFull")' - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}fname=' - - fieldName: log.fnameToParse - pattern: '{{.data}}{{.word}}\=' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.restData - - - grok: - patterns: - - fieldName: log.fnameFull - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.fnameToParse - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}fname=' - - fieldName: log.fnameFull - pattern: '{{.greedy}}' - source: log.restData - where: '!exists("log.fnameFull")' - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}BitdefenderGZMalwareName=' - - fieldName: log.BitdefenderGZMalwareNameToParse - pattern: '{{.data}}{{.word}}\=' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.restData - - - grok: - patterns: - - fieldName: log.BitdefenderGZMalwareNameFull - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.BitdefenderGZMalwareNameToParse - - - grok: - patterns: - - fieldName: log.2trash - pattern: '{{.data}}BitdefenderGZMalwareName=' - - fieldName: log.BitdefenderGZMalwareNameFull - pattern: '{{.greedy}}' - source: log.restData - where: '!exists("log.BitdefenderGZMalwareNameFull")' - - - kv: - fieldSplit: " " - valueSplit: "=" - source: log.restData - - # Renaming useful fields - - rename: - from: - - log.spt - to: origin.port - - - rename: - from: - - log.src - to: target.ip - - - rename: - from: - - log.dvcFull - to: target.ip - where: '!exists("target.ip")' - - - rename: - from: - - log.dvchost - to: target.host - - - rename: - from: - - log.sprocFull - to: target.process - - - rename: - from: - - log.filePathFull - to: target.path - - - rename: - from: - - log.actFull - - log.BitdefenderGZMainAction - to: action - - - rename: - from: - - log.BitdefenderGZDetectionNameFull - to: log.BitdefenderGZDetectionName - - - rename: - from: - - log.BitdefenderGZAttackTypesFull - to: log.BitdefenderGZAttackTypes - - # Removing unnecessary characters - - trim: - function: prefix - substring: '|' - fields: - - log.productVendor - - - trim: - function: suffix - substring: '|' - fields: - - log.productVendor - - log.product - - log.productVersion - - log.signatureID - - log.eventType - - log.severity - - - trim: - function: prefix - substring: '<' - fields: - - log.syslogPriority - - - trim: - function: suffix - substring: '>' - fields: - - log.syslogPriority - - - trim: - function: prefix - substring: '[' - fields: - - log.processPid - - - trim: - function: suffix - substring: ']' - fields: - - log.processPid - - - rename: - from: - - log.start - - log.end - - log.BitdefenderGZDetectionTime - to: deviceTime - - - rename: - from: - - log.severity - to: log.cefSeverity - - - rename: - from: - - log.BitdefenderGZComputerFQDN - to: target.domain - - - rename: - from: - - log.suserFull - to: target.user - - - rename: - from: - - log.BitdefenderGZMalwareNameFull - to: target.malware - - - rename: - from: - - log.BitdefenderGZMalwareType - to: target.malwareType - - - rename: - from: - - log.BitdefenderGZMalwareHash - - log.BitdefenderGZFileHashSha256 - to: target.sha256 - - - rename: - from: - - log.fileHash - to: target.md5 - - - rename: - from: - - log.fnameFull - to: target.filename - - - rename: - from: - - log.requestFull - to: origin.url - - - rename: - from: - - log.BitdefenderGZDetectionLocalPort - to: target.port - - - rename: - from: - - log.BitdefenderGZDetectionAttackerIp - to: origin.ip - - - rename: - from: - - log.BitdefenderGZEventSourceIP - to: origin.ip - where: '!exists("origin.ip")' - - # Adding actionResult field to indicate whether the action was successful or failed - - add: - function: string - params: - key: actionResult - value: denied - where: oneOf("action", ["blocked", "block", "aph_blocked", "portscan_blocked", "quarantined"]) - - add: - function: string - params: - key: actionResult - value: success - where: oneOf("action", ["deleted", "disinfected", "restored"]) - - - add: - function: string - params: - key: actionResult - value: failure - where: oneOf("action", ["still present", "ignored", "no action", "reportOnly"]) - - # Adding geolocation to origin ip - - dynamic: - plugin: com.utmstack.geolocation - params: - source: origin.ip - destination: origin.geolocation - where: exists("origin.ip") - - # Reformat and field conversions - - cast: - fields: - - origin.port - - target.port - to: int - - # Removing unused fields. - - delete: - fields: - - log.0trash - - log.1trash - - log.2trash - - log.3trash - - log.restData - - log.irrelevant - - log.cefVersion - - log.dvcFull - - log.dvcToParse - - log.requestToParse - - log.actToParse - - log.sprocToParse - - log.filePathToParse - - log.fnameToParse - - log.BitdefenderGZDetectionNameToParse - - log.BitdefenderGZMalwareNameToParse - - log.BitdefenderGZAttackTypesToParse - - log.suserToParse - - log.act - - log.sproc - - log.filePath - - log.dvc - - log.request - - log.suser - - log.fname - - # Keep addresses in IP fields and retain other source values under log. - - rename: - from: - - origin.ip - to: log.unparsedOriginIp - where: exists("origin.ip") && (!(inCIDR("origin.ip","0.0.0.0/0") || inCIDR("origin.ip","::/0")) || oneOf("origin.ip",["0.0.0.0","::"])) - - rename: - from: - - target.ip - to: log.unparsedTargetIp - where: exists("target.ip") && (!(inCIDR("target.ip","0.0.0.0/0") || inCIDR("target.ip","::/0")) || oneOf("target.ip",["0.0.0.0","::"])) - - # Normalize the source event severity. - - add: - function: string - params: - key: severity - value: info - where: (greaterOrEqual("log.cefSeverity",0) && lessOrEqual("log.cefSeverity",3)) || oneOf("log.cefSeverity",["Low","low","Unknown"]) - - add: - function: string - params: - key: severity - value: warning - where: (greaterOrEqual("log.cefSeverity",4) && lessOrEqual("log.cefSeverity",6)) || oneOf("log.cefSeverity",["Medium","medium"]) - - add: - function: string - params: - key: severity - value: error - where: (greaterOrEqual("log.cefSeverity",7) && lessOrEqual("log.cefSeverity",8)) || oneOf("log.cefSeverity",["High","high"]) - - add: - function: string - params: - key: severity - value: critical - where: (greaterOrEqual("log.cefSeverity",9) && lessOrEqual("log.cefSeverity",10)) || oneOf("log.cefSeverity",["Very-High","Very High","very-high"]) +- dataTypes: + - antivirus-bitdefender-gz + steps: + - delete: + fields: + - log.cefExtension + - log.endpointKey + - log.endpointKeyType + - log.correlationCandidate + - log.eventType + - log.cefSeverity + - log.BitdefenderGZAction + - log.BitdefenderGZAppControlStatus + - log.BitdefenderGZApplicationControlBlockType + - log.BitdefenderGZApplicationControlType + - log.BitdefenderGZAttCkId + - log.BitdefenderGZAttackEntry + - log.BitdefenderGZAttackType + - log.BitdefenderGZAttackTypes + - log.BitdefenderGZAvcStatus + - log.BitdefenderGZBlockedMalwareCnt + - log.BitdefenderGZBlockingRuleName + - log.BitdefenderGZCleanedMalwareCnt + - log.BitdefenderGZCompanyId + - log.BitdefenderGZComputerFQDN + - log.BitdefenderGZDeletedMalwareCnt + - log.BitdefenderGZDetectionAction + - log.BitdefenderGZDetectionAttackTechnique + - log.BitdefenderGZDetectionAttackerIp + - log.BitdefenderGZDetectionCve + - log.BitdefenderGZDetectionLocalPort + - log.BitdefenderGZDetectionName + - log.BitdefenderGZDetectionPath + - log.BitdefenderGZDetectionTime + - log.BitdefenderGZDetectionVictimIp + - log.BitdefenderGZDeviceId + - log.BitdefenderGZDeviceName + - log.BitdefenderGZDlpStatus + - log.BitdefenderGZDriverName + - log.BitdefenderGZEndpointId + - log.BitdefenderGZErrorCode + - log.BitdefenderGZErrorMessage + - log.BitdefenderGZEventSourceIP + - log.BitdefenderGZEventType + - log.BitdefenderGZExploitType + - log.BitdefenderGZFileHashSha256 + - log.BitdefenderGZFwProtocolId + - log.BitdefenderGZIgnoredMalwareCnt + - log.BitdefenderGZIncidentId + - log.BitdefenderGZIncidentNumber + - log.BitdefenderGZIsFilelessAttack + - log.BitdefenderGZMainAction + - log.BitdefenderGZMalwareHash + - log.BitdefenderGZMalwareName + - log.BitdefenderGZMalwareStatus + - log.BitdefenderGZMalwareType + - log.BitdefenderGZModule + - log.BitdefenderGZNewHwid + - log.BitdefenderGZOldHwid + - log.BitdefenderGZParentPath + - log.BitdefenderGZParentPid + - log.BitdefenderGZParentProcessPath + - log.BitdefenderGZPresentMalwareCnt + - log.BitdefenderGZProcessCommandLine + - log.BitdefenderGZProcessInfoPath + - log.BitdefenderGZProcessPid + - log.BitdefenderGZPuStatus + - log.BitdefenderGZQuarantinedMalwareCnt + - log.BitdefenderGZScanEngineType + - log.BitdefenderGZSeverityScore + - log.BitdefenderGZSignaturesNumber + - log.BitdefenderGZStatus + - log.BitdefenderGZTargetType + - log.BitdefenderGZTaskId + - log.BitdefenderGZTaskName + - log.BitdefenderGZTaskScanType + - log.BitdefenderGZTaskSuccessful + - log.BitdefenderGZTaskType + - log.BitdefenderGZUserName + - log.BitdefenderGZVendorId + - log.act + - log.app_control_status + - log.avc_status + - log.cnt + - log.deviceExternalId + - log.dhost + - log.dlp_status + - log.driverName + - log.dvc + - log.dvchost + - log.end + - log.fileHash + - log.filePath + - log.fname + - log.is_fileless_attack + - log.label + - log.malware_status + - log.msg + - log.proto + - log.pu_status + - log.request + - log.sproc + - log.spt + - log.src + - log.start + - log.suid + - log.suser + - log.taskName + - grok: + source: raw + patterns: + - fieldName: '' + pattern: '^(?:(?:<[0-9]{1,3}>)?(?:[A-Za-z]{3}\s+[0-9]{1,2}\s+[0-9]{2}:[0-9]{2}:[0-9]{2}\s+\S+\s+(?:\S+(?:\[[0-9]+\])?:\s*)?|(?:[0-9]+\s+)?[0-9]{4}-[0-9]{2}-[0-9]{2}T\S+\s+(?:\S+\s+){0,5})|<[0-9]{1,3}>)?CEF:' + - fieldName: log.cefVersion + pattern: '[0-9]+' + - fieldName: '' + pattern: \|Bitdefender\|GravityZone\| + - fieldName: log.productVersion + pattern: (?:\\.|[^\\|])* + - fieldName: '' + pattern: \| + - fieldName: log.signatureID + pattern: (?:\\.|[^\\|])* + - fieldName: '' + pattern: \| + - fieldName: log.eventType + pattern: (?:\\.|[^\\|])* + - fieldName: '' + pattern: \| + - fieldName: log.cefSeverity + pattern: (?:\\.|[^\\|])* + - fieldName: '' + pattern: \| + - fieldName: log.cefExtension + pattern: (?s:.*)$ + - kv: + source: log.cefExtension + fieldSplit: ' ' + valueSplit: '=' + where: exists("log.cefExtension") + - delete: + fields: + - log.eventType + - log.cefSeverity + - log.cefExtension + - grok: + source: raw + patterns: + - fieldName: '' + pattern: '^(?:(?:<[0-9]{1,3}>)?(?:[A-Za-z]{3}\s+[0-9]{1,2}\s+[0-9]{2}:[0-9]{2}:[0-9]{2}\s+\S+\s+(?:\S+(?:\[[0-9]+\])?:\s*)?|(?:[0-9]+\s+)?[0-9]{4}-[0-9]{2}-[0-9]{2}T\S+\s+(?:\S+\s+){0,5})|<[0-9]{1,3}>)?CEF:' + - fieldName: log.cefVersion + pattern: '[0-9]+' + - fieldName: '' + pattern: \|Bitdefender\|GravityZone\| + - fieldName: log.productVersion + pattern: (?:\\.|[^\\|])* + - fieldName: '' + pattern: \| + - fieldName: log.signatureID + pattern: (?:\\.|[^\\|])* + - fieldName: '' + pattern: \| + - fieldName: log.eventType + pattern: (?:\\.|[^\\|])* + - fieldName: '' + pattern: \| + - fieldName: log.cefSeverity + pattern: (?:\\.|[^\\|])* + - fieldName: '' + pattern: \| + - fieldName: log.cefExtension + pattern: (?s:.*)$ + - delete: + fields: + - log.BitdefenderGZAction + - log.BitdefenderGZAppControlStatus + - log.BitdefenderGZApplicationControlBlockType + - log.BitdefenderGZApplicationControlType + - log.BitdefenderGZAttCkId + - log.BitdefenderGZAttackEntry + - log.BitdefenderGZAttackType + - log.BitdefenderGZAttackTypes + - log.BitdefenderGZAvcStatus + - log.BitdefenderGZBlockedMalwareCnt + - log.BitdefenderGZBlockingRuleName + - log.BitdefenderGZCleanedMalwareCnt + - log.BitdefenderGZCompanyId + - log.BitdefenderGZComputerFQDN + - log.BitdefenderGZDeletedMalwareCnt + - log.BitdefenderGZDetectionAction + - log.BitdefenderGZDetectionAttackTechnique + - log.BitdefenderGZDetectionAttackerIp + - log.BitdefenderGZDetectionCve + - log.BitdefenderGZDetectionLocalPort + - log.BitdefenderGZDetectionName + - log.BitdefenderGZDetectionPath + - log.BitdefenderGZDetectionTime + - log.BitdefenderGZDetectionVictimIp + - log.BitdefenderGZDeviceId + - log.BitdefenderGZDeviceName + - log.BitdefenderGZDlpStatus + - log.BitdefenderGZDriverName + - log.BitdefenderGZEndpointId + - log.BitdefenderGZErrorCode + - log.BitdefenderGZErrorMessage + - log.BitdefenderGZEventSourceIP + - log.BitdefenderGZEventType + - log.BitdefenderGZExploitType + - log.BitdefenderGZFileHashSha256 + - log.BitdefenderGZFwProtocolId + - log.BitdefenderGZIgnoredMalwareCnt + - log.BitdefenderGZIncidentId + - log.BitdefenderGZIncidentNumber + - log.BitdefenderGZIsFilelessAttack + - log.BitdefenderGZMainAction + - log.BitdefenderGZMalwareHash + - log.BitdefenderGZMalwareName + - log.BitdefenderGZMalwareStatus + - log.BitdefenderGZMalwareType + - log.BitdefenderGZModule + - log.BitdefenderGZNewHwid + - log.BitdefenderGZOldHwid + - log.BitdefenderGZParentPath + - log.BitdefenderGZParentPid + - log.BitdefenderGZParentProcessPath + - log.BitdefenderGZPresentMalwareCnt + - log.BitdefenderGZProcessCommandLine + - log.BitdefenderGZProcessInfoPath + - log.BitdefenderGZProcessPid + - log.BitdefenderGZPuStatus + - log.BitdefenderGZQuarantinedMalwareCnt + - log.BitdefenderGZScanEngineType + - log.BitdefenderGZSeverityScore + - log.BitdefenderGZSignaturesNumber + - log.BitdefenderGZStatus + - log.BitdefenderGZTargetType + - log.BitdefenderGZTaskId + - log.BitdefenderGZTaskName + - log.BitdefenderGZTaskScanType + - log.BitdefenderGZTaskSuccessful + - log.BitdefenderGZTaskType + - log.BitdefenderGZUserName + - log.BitdefenderGZVendorId + - log.act + - log.app_control_status + - log.appcontrolstatus + - log.avc_status + - log.avcstatus + - log.cnt + - log.deviceExternalId + - log.dhost + - log.dlp_status + - log.dlpstatus + - log.driverName + - log.dvc + - log.dvchost + - log.end + - log.fileHash + - log.filePath + - log.fname + - log.is_fileless_attack + - log.isfilelessattack + - log.label + - log.malware_status + - log.malwarestatus + - log.msg + - log.proto + - log.pu_status + - log.pustatus + - log.request + - log.sproc + - log.spt + - log.src + - log.start + - log.suid + - log.suser + - log.taskName + - log.endpointKey + - log.endpointKeyType + - log.correlationCandidate + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZAction= + - fieldName: log.BitdefenderGZAction + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZAppControlStatus= + - fieldName: log.BitdefenderGZAppControlStatus + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZApplicationControlBlockType= + - fieldName: log.BitdefenderGZApplicationControlBlockType + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZApplicationControlType= + - fieldName: log.BitdefenderGZApplicationControlType + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZAttCkId= + - fieldName: log.BitdefenderGZAttCkId + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZAttackEntry= + - fieldName: log.BitdefenderGZAttackEntry + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZAttackType= + - fieldName: log.BitdefenderGZAttackType + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZAttackTypes= + - fieldName: log.BitdefenderGZAttackTypes + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZAvcStatus= + - fieldName: log.BitdefenderGZAvcStatus + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZBlockedMalwareCnt= + - fieldName: log.BitdefenderGZBlockedMalwareCnt + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZBlockingRuleName= + - fieldName: log.BitdefenderGZBlockingRuleName + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZCleanedMalwareCnt= + - fieldName: log.BitdefenderGZCleanedMalwareCnt + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZCompanyId= + - fieldName: log.BitdefenderGZCompanyId + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZComputerFQDN= + - fieldName: log.BitdefenderGZComputerFQDN + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDeletedMalwareCnt= + - fieldName: log.BitdefenderGZDeletedMalwareCnt + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDetectionAction= + - fieldName: log.BitdefenderGZDetectionAction + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDetectionAttackTechnique= + - fieldName: log.BitdefenderGZDetectionAttackTechnique + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDetectionAttackerIp= + - fieldName: log.BitdefenderGZDetectionAttackerIp + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDetectionCve= + - fieldName: log.BitdefenderGZDetectionCve + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDetectionLocalPort= + - fieldName: log.BitdefenderGZDetectionLocalPort + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDetectionName= + - fieldName: log.BitdefenderGZDetectionName + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDetectionPath= + - fieldName: log.BitdefenderGZDetectionPath + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDetectionTime= + - fieldName: log.BitdefenderGZDetectionTime + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDetectionVictimIp= + - fieldName: log.BitdefenderGZDetectionVictimIp + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDeviceId= + - fieldName: log.BitdefenderGZDeviceId + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDeviceName= + - fieldName: log.BitdefenderGZDeviceName + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDlpStatus= + - fieldName: log.BitdefenderGZDlpStatus + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZDriverName= + - fieldName: log.BitdefenderGZDriverName + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZEndpointId= + - fieldName: log.BitdefenderGZEndpointId + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZErrorCode= + - fieldName: log.BitdefenderGZErrorCode + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZErrorMessage= + - fieldName: log.BitdefenderGZErrorMessage + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZEventSourceIP= + - fieldName: log.BitdefenderGZEventSourceIP + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZEventType= + - fieldName: log.BitdefenderGZEventType + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZExploitType= + - fieldName: log.BitdefenderGZExploitType + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZFileHashSha256= + - fieldName: log.BitdefenderGZFileHashSha256 + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZFwProtocolId= + - fieldName: log.BitdefenderGZFwProtocolId + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZIgnoredMalwareCnt= + - fieldName: log.BitdefenderGZIgnoredMalwareCnt + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZIncidentId= + - fieldName: log.BitdefenderGZIncidentId + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZIncidentNumber= + - fieldName: log.BitdefenderGZIncidentNumber + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZIsFilelessAttack= + - fieldName: log.BitdefenderGZIsFilelessAttack + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZMainAction= + - fieldName: log.BitdefenderGZMainAction + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZMalwareHash= + - fieldName: log.BitdefenderGZMalwareHash + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZMalwareName= + - fieldName: log.BitdefenderGZMalwareName + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZMalwareStatus= + - fieldName: log.BitdefenderGZMalwareStatus + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZMalwareType= + - fieldName: log.BitdefenderGZMalwareType + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZModule= + - fieldName: log.BitdefenderGZModule + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZNewHwid= + - fieldName: log.BitdefenderGZNewHwid + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZOldHwid= + - fieldName: log.BitdefenderGZOldHwid + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZParentPath= + - fieldName: log.BitdefenderGZParentPath + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZParentPid= + - fieldName: log.BitdefenderGZParentPid + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZParentProcessPath= + - fieldName: log.BitdefenderGZParentProcessPath + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZPresentMalwareCnt= + - fieldName: log.BitdefenderGZPresentMalwareCnt + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZProcessCommandLine= + - fieldName: log.BitdefenderGZProcessCommandLine + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZProcessInfoPath= + - fieldName: log.BitdefenderGZProcessInfoPath + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZProcessPid= + - fieldName: log.BitdefenderGZProcessPid + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZPuStatus= + - fieldName: log.BitdefenderGZPuStatus + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZQuarantinedMalwareCnt= + - fieldName: log.BitdefenderGZQuarantinedMalwareCnt + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZScanEngineType= + - fieldName: log.BitdefenderGZScanEngineType + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZSeverityScore= + - fieldName: log.BitdefenderGZSeverityScore + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZSignaturesNumber= + - fieldName: log.BitdefenderGZSignaturesNumber + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZStatus= + - fieldName: log.BitdefenderGZStatus + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZTargetType= + - fieldName: log.BitdefenderGZTargetType + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZTaskId= + - fieldName: log.BitdefenderGZTaskId + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZTaskName= + - fieldName: log.BitdefenderGZTaskName + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZTaskScanType= + - fieldName: log.BitdefenderGZTaskScanType + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZTaskSuccessful= + - fieldName: log.BitdefenderGZTaskSuccessful + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZTaskType= + - fieldName: log.BitdefenderGZTaskType + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZUserName= + - fieldName: log.BitdefenderGZUserName + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*BitdefenderGZVendorId= + - fieldName: log.BitdefenderGZVendorId + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*act= + - fieldName: log.act + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*app_control_status= + - fieldName: log.appcontrolstatus + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*avc_status= + - fieldName: log.avcstatus + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*cnt= + - fieldName: log.cnt + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*deviceExternalId= + - fieldName: log.deviceExternalId + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*dhost= + - fieldName: log.dhost + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*dlp_status= + - fieldName: log.dlpstatus + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*driverName= + - fieldName: log.driverName + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*dvc= + - fieldName: log.dvc + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*dvchost= + - fieldName: log.dvchost + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*end= + - fieldName: log.end + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*fileHash= + - fieldName: log.fileHash + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*filePath= + - fieldName: log.filePath + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*fname= + - fieldName: log.fname + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*is_fileless_attack= + - fieldName: log.isfilelessattack + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*label= + - fieldName: log.label + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*malware_status= + - fieldName: log.malwarestatus + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*msg= + - fieldName: log.msg + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*proto= + - fieldName: log.proto + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*pu_status= + - fieldName: log.pustatus + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*request= + - fieldName: log.request + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*sproc= + - fieldName: log.sproc + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*spt= + - fieldName: log.spt + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*src= + - fieldName: log.src + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*start= + - fieldName: log.start + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*suid= + - fieldName: log.suid + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*suser= + - fieldName: log.suser + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=])*?[ \t]+)*taskName= + - fieldName: log.taskName + pattern: (?:\\.|[^\\=])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.BitdefenderGZTaskName + patterns: + - fieldName: log.taskName + pattern: (?s)^.+$ + where: equals("log.BitdefenderGZModule","task-status") + - grok: + source: log.msg + patterns: + - fieldName: log.taskName + pattern: (?s)^.+$ + where: equals("log.BitdefenderGZModule","task-status") && !exists("log.taskName") + - grok: + source: log.dvc + patterns: + - fieldName: target.ip + pattern: (?s)^.+$ + where: (inCIDR("log.dvc","0.0.0.0/0") || inCIDR("log.dvc","::/0")) && !inCIDR("log.dvc","0.0.0.0/32") && !inCIDR("log.dvc","::/128") + - grok: + source: log.BitdefenderGZDetectionVictimIp + patterns: + - fieldName: target.ip + pattern: (?s)^.+$ + where: equals("log.BitdefenderGZModule","network-monitor") && (inCIDR("log.BitdefenderGZDetectionVictimIp","0.0.0.0/0") || inCIDR("log.BitdefenderGZDetectionVictimIp","::/0")) + && !inCIDR("log.BitdefenderGZDetectionVictimIp","0.0.0.0/32") && !inCIDR("log.BitdefenderGZDetectionVictimIp","::/128") + - grok: + source: log.BitdefenderGZDetectionAttackerIp + patterns: + - fieldName: origin.ip + pattern: (?s)^.+$ + where: equals("log.BitdefenderGZModule","network-monitor") && (inCIDR("log.BitdefenderGZDetectionAttackerIp","0.0.0.0/0") || inCIDR("log.BitdefenderGZDetectionAttackerIp","::/0")) + && !inCIDR("log.BitdefenderGZDetectionAttackerIp","0.0.0.0/32") && !inCIDR("log.BitdefenderGZDetectionAttackerIp","::/128") + - grok: + source: log.BitdefenderGZEventSourceIP + patterns: + - fieldName: origin.ip + pattern: (?s)^.+$ + where: equals("log.BitdefenderGZModule","fw") && (inCIDR("log.BitdefenderGZEventSourceIP","0.0.0.0/0") || inCIDR("log.BitdefenderGZEventSourceIP","::/0")) + && !inCIDR("log.BitdefenderGZEventSourceIP","0.0.0.0/32") && !inCIDR("log.BitdefenderGZEventSourceIP","::/128") + - grok: + source: log.dvchost + patterns: + - fieldName: target.host + pattern: (?s)^.+$ + where: exists("log.dvchost") && !oneOf("log.dvchost",["","-","unknown"]) + - grok: + source: log.BitdefenderGZComputerFQDN + patterns: + - fieldName: target.domain + pattern: (?s)^.+$ + where: exists("log.BitdefenderGZComputerFQDN") && !oneOf("log.BitdefenderGZComputerFQDN",["","-","unknown"]) + - grok: + source: log.deviceExternalId + patterns: + - fieldName: log.endpointKey + pattern: (?s)^.+$ + where: exists("log.deviceExternalId") && !oneOf("log.deviceExternalId",["","-","unknown"]) && !exists("log.endpointKey") + - add: + function: string + params: + key: log.endpointKeyType + value: computer-id + where: exists("log.endpointKey") && !exists("log.endpointKeyType") + - grok: + source: log.BitdefenderGZEndpointId + patterns: + - fieldName: log.endpointKey + pattern: (?s)^.+$ + where: exists("log.BitdefenderGZEndpointId") && !oneOf("log.BitdefenderGZEndpointId",["","-","unknown"]) && !exists("log.endpointKey") + - add: + function: string + params: + key: log.endpointKeyType + value: endpoint-id + where: exists("log.endpointKey") && !exists("log.endpointKeyType") + - grok: + source: target.host + patterns: + - fieldName: log.endpointKey + pattern: (?s)^.+$ + where: exists("target.host") && !oneOf("target.host",["","-","unknown"]) && !exists("log.endpointKey") + - add: + function: string + params: + key: log.endpointKeyType + value: host + where: exists("log.endpointKey") && !exists("log.endpointKeyType") + - grok: + source: target.ip + patterns: + - fieldName: log.endpointKey + pattern: (?s)^.+$ + where: exists("target.ip") && !oneOf("target.ip",["","-","unknown"]) && !exists("log.endpointKey") + - add: + function: string + params: + key: log.endpointKeyType + value: ip + where: exists("log.endpointKey") && !exists("log.endpointKeyType") + - grok: + source: log.suser + patterns: + - fieldName: target.user + pattern: (?s)^.+$ + where: regexMatch("log.suser","^[^\\\\\\r\\n]+$") && !equals("log.BitdefenderGZModule","task-status") + - grok: + source: log.suser + patterns: + - fieldName: origin.user + pattern: (?s)^.+$ + where: regexMatch("log.suser","^[^\\\\\\r\\n]+$") && equals("log.BitdefenderGZModule","task-status") + - grok: + source: log.suid + patterns: + - fieldName: origin.user + pattern: (?s)^.+$ + where: equals("log.BitdefenderGZModule","task-status") && !exists("origin.user") && exists("log.suid") && !oneOf("log.suid",["","-"]) + - grok: + source: log.suid + patterns: + - fieldName: target.user + pattern: (?s)^.+$ + where: '!equals("log.BitdefenderGZModule","task-status") && !exists("target.user") && exists("log.suid") && !oneOf("log.suid",["","-"])' + - grok: + source: log.BitdefenderGZMalwareName + patterns: + - fieldName: target.malware + pattern: (?s)^.+$ + where: exists("log.BitdefenderGZMalwareName") && !oneOf("log.BitdefenderGZMalwareName",["","-","unknown"]) + - grok: + source: log.BitdefenderGZDetectionName + patterns: + - fieldName: target.malware + pattern: (?s)^.+$ + where: '!exists("target.malware") && oneOf("log.BitdefenderGZModule",["av","avc","hd","new-incident","network-sandboxing"])' + - grok: + source: log.sproc + patterns: + - fieldName: '' + pattern: ^(?:.*[/\\])? + - fieldName: target.process + pattern: '[^/\\\r\n]+$' + - grok: + source: log.filePath + patterns: + - fieldName: '' + pattern: ^(?:.*[/\\])? + - fieldName: target.process + pattern: '[^/\\\r\n]+$' + where: equals("log.BitdefenderGZModule","avc") && !exists("target.process") + - grok: + source: log.BitdefenderGZProcessCommandLine + patterns: + - fieldName: target.command + pattern: (?s)^.+$ + where: regexMatch("log.BitdefenderGZProcessCommandLine","^[^\\\\\\r\\n]+$") + - grok: + source: log.fname + patterns: + - fieldName: target.filename + pattern: (?s)^.+$ + where: regexMatch("log.fname","^[^\\\\\\r\\n]+$") + - grok: + source: log.filePath + patterns: + - fieldName: '' + pattern: ^.*[/\\] + - fieldName: target.filename + pattern: '[^/\\\r\n]+$' + where: '!exists("target.filename")' + - grok: + source: log.filePath + patterns: + - fieldName: target.path + pattern: ^/.*/ + - fieldName: '' + pattern: '[^/]+$' + where: regexMatch("log.filePath","^[^\\\\\\r\\n]+$") + - grok: + source: log.BitdefenderGZMalwareHash + patterns: + - fieldName: target.sha256 + pattern: (?s)^.+$ + where: regexMatch("log.BitdefenderGZMalwareHash","^[0-9A-Fa-f]{64}$") + - grok: + source: log.BitdefenderGZFileHashSha256 + patterns: + - fieldName: target.sha256 + pattern: (?s)^.+$ + where: regexMatch("log.BitdefenderGZFileHashSha256","^[0-9A-Fa-f]{64}$") + - grok: + source: log.fileHash + patterns: + - fieldName: target.md5 + pattern: (?s)^.+$ + where: regexMatch("log.fileHash","^[0-9A-Fa-f]{32}$") + - grok: + source: log.request + patterns: + - fieldName: '' + pattern: ^(?i:https?://)? + - fieldName: origin.domain + pattern: (?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])? + - fieldName: '' + pattern: (?::[0-9]{1,5})?(?:[/\?#].*|$) + where: oneOf("log.BitdefenderGZModule",["aph","uc","dp","new-incident"]) + - grok: + source: log.request + patterns: + - fieldName: origin.url + pattern: (?s)^.+$ + where: regexMatch("log.request","(?i)^https?://[^\\s\\\\]+$") && oneOf("log.BitdefenderGZModule",["aph","uc","dp","new-incident"]) + - grok: + source: log.BitdefenderGZDetectionLocalPort + patterns: + - fieldName: target.port + pattern: (?s)^.+$ + where: equals("log.BitdefenderGZModule","network-monitor") && regexMatch("log.BitdefenderGZDetectionLocalPort","^[0-9]+$") && + greaterThan("log.BitdefenderGZDetectionLocalPort",0) && lessOrEqual("log.BitdefenderGZDetectionLocalPort",65535) + - cast: + fields: + - target.port + to: int + where: exists("target.port") + - add: + function: string + params: + key: protocol + value: icmp + where: equals("log.BitdefenderGZModule","fw") && equals("log.BitdefenderGZFwProtocolId","1") + - add: + function: string + params: + key: protocol + value: tcp + where: equals("log.BitdefenderGZModule","fw") && equals("log.BitdefenderGZFwProtocolId","6") + - add: + function: string + params: + key: protocol + value: udp + where: equals("log.BitdefenderGZModule","fw") && equals("log.BitdefenderGZFwProtocolId","17") + - add: + function: string + params: + key: protocol + value: ipv6-icmp + where: equals("log.BitdefenderGZModule","fw") && equals("log.BitdefenderGZFwProtocolId","58") + - grok: + source: log.act + patterns: + - fieldName: action + pattern: (?s)^.+$ + where: exists("log.act") && !oneOf("log.act",["","-","unknown"]) + - grok: + source: log.BitdefenderGZMainAction + patterns: + - fieldName: action + pattern: (?s)^.+$ + where: '!exists("action") && exists("log.BitdefenderGZMainAction") && !oneOf("log.BitdefenderGZMainAction",["","-","unknown"])' + - grok: + source: log.BitdefenderGZAction + patterns: + - fieldName: action + pattern: (?s)^.+$ + where: '!exists("action") && exists("log.BitdefenderGZAction") && !oneOf("log.BitdefenderGZAction",["","-","unknown"])' + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("action","(?i)^(blocked|block|aph_blocked|portscan_blocked|data_protection_blocked|uc_site_blocked|uc_app_blocked|quarantined)$") + - add: + function: string + params: + key: actionResult + value: success + where: oneOf("log.BitdefenderGZModule",["av","avc"]) && regexMatch("action","(?i)^(deleted|disinfected|restored)$") + - add: + function: string + params: + key: actionResult + value: failure + where: oneOf("log.BitdefenderGZModule",["av","avc"]) && equalsIgnoreCase("action","still present") + - add: + function: string + params: + key: actionResult + value: success + where: equals("log.BitdefenderGZModule","task-status") && oneOf("log.BitdefenderGZTaskSuccessful",["1","true"]) + - add: + function: string + params: + key: actionResult + value: failure + where: equals("log.BitdefenderGZModule","task-status") && oneOf("log.BitdefenderGZTaskSuccessful",["0","false"]) && greaterThan("log.BitdefenderGZErrorCode",0) + - grok: + source: log.start + patterns: + - fieldName: deviceTime + pattern: (?s)^.+$ + where: '!exists("deviceTime") && regexMatch("log.start","^(?:[1-9][0-9]{3}-(?:(?:01|03|05|07|08|10|12)-(?:0[1-9]|[12][0-9]|3[01])|(?:04|06|09|11)-(?:0[1-9]|[12][0-9]|30)|02-(?:0[1-9]|1[0-9]|2[0-8]))|(?:[1-9][0-9](?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\\.[0-9]+)?(?:Z|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9])$")' + - grok: + source: log.end + patterns: + - fieldName: deviceTime + pattern: (?s)^.+$ + where: '!exists("deviceTime") && regexMatch("log.end","^(?:[1-9][0-9]{3}-(?:(?:01|03|05|07|08|10|12)-(?:0[1-9]|[12][0-9]|3[01])|(?:04|06|09|11)-(?:0[1-9]|[12][0-9]|30)|02-(?:0[1-9]|1[0-9]|2[0-8]))|(?:[1-9][0-9](?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\\.[0-9]+)?(?:Z|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9])$")' + - grok: + source: log.BitdefenderGZDetectionTime + patterns: + - fieldName: deviceTime + pattern: (?s)^.+$ + where: '!exists("deviceTime") && regexMatch("log.BitdefenderGZDetectionTime","^(?:[1-9][0-9]{3}-(?:(?:01|03|05|07|08|10|12)-(?:0[1-9]|[12][0-9]|3[01])|(?:04|06|09|11)-(?:0[1-9]|[12][0-9]|30)|02-(?:0[1-9]|1[0-9]|2[0-8]))|(?:[1-9][0-9](?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\\.[0-9]+)?(?:Z|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9])$")' + - add: + function: string + params: + key: severity + value: info + where: (greaterOrEqual("log.cefSeverity",0) && lessOrEqual("log.cefSeverity",3)) || oneOf("log.cefSeverity",["Low","low","Unknown"]) + - add: + function: string + params: + key: severity + value: warning + where: (greaterOrEqual("log.cefSeverity",4) && lessOrEqual("log.cefSeverity",6)) || oneOf("log.cefSeverity",["Medium","medium"]) + - add: + function: string + params: + key: severity + value: error + where: (greaterOrEqual("log.cefSeverity",7) && lessOrEqual("log.cefSeverity",8)) || oneOf("log.cefSeverity",["High","high"]) + - add: + function: string + params: + key: severity + value: critical + where: (greaterOrEqual("log.cefSeverity",9) && lessOrEqual("log.cefSeverity",10)) || oneOf("log.cefSeverity",["Very-High","Very + High","very-high"]) + - dynamic: + plugin: com.utmstack.geolocation + params: + source: origin.ip + destination: origin.geolocation + where: exists("origin.ip") + - dynamic: + plugin: com.utmstack.geolocation + params: + source: target.ip + destination: target.geolocation + where: exists("target.ip") + - add: + function: string + params: + key: log.correlationCandidate.multiple_malware_from_single_source + value: match + where: '(oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && + + greaterOrEqual("log.cefSeverity", 8)) && exists("dataSource") && !equals("dataSource","") && exists("log.BitdefenderGZCompanyId") + && !equals("log.BitdefenderGZCompanyId","") && exists("log.endpointKey") && exists("log.endpointKeyType")' + - add: + function: string + params: + key: log.correlationCandidate.network_threat_detection + value: match + where: "(oneOf(\"log.BitdefenderGZModule\", [\"network-monitor\", \"fw\"]) &&\ngreaterOrEqual(\"log.cefSeverity\", 8)\n && equals(\"\ + actionResult\",\"denied\") && exists(\"origin.ip\")) && exists(\"dataSource\") && !equals(\"dataSource\",\"\") && exists(\"\ + log.BitdefenderGZCompanyId\") && !equals(\"log.BitdefenderGZCompanyId\",\"\") && exists(\"log.endpointKey\") && exists(\"log.endpointKeyType\"\ + )" + - add: + function: string + params: + key: log.correlationCandidate.av_console_lateral_movement + value: match + where: (equals("log.BitdefenderGZModule","task-status") && equals("actionResult","success") && regexMatch("log.taskName","(?i)(uninstall|restore.*quarantine|(?:run|execute).*script|(?:run|execute).*command)") + && exists("log.suid") && !equals("log.suid","") && exists("log.BitdefenderGZTaskType")) && exists("dataSource") && !equals("dataSource","") + && exists("log.BitdefenderGZCompanyId") && !equals("log.BitdefenderGZCompanyId","") && exists("log.endpointKey") && exists("log.endpointKeyType") + - add: + function: string + params: + key: log.correlationCandidate.usb_malware_propagation + value: match + where: (oneOf("log.BitdefenderGZModule",["av","avc","hd"]) && (regexMatch("log.filePath","(?i)(autorun\\.inf|\\\\\\$recycle\\.bin\\\\.*\\.(exe|scr|vbs|bat|cmd))") + || regexMatch("log.BitdefenderGZDetectionName","(?i)(autorun|worm\\.autoruner|inf/autorun|usb\\.worm)") || regexMatch("target.malware","(?i)(autorun|usb\\.worm)"))) + && exists("dataSource") && !equals("dataSource","") && exists("log.BitdefenderGZCompanyId") && !equals("log.BitdefenderGZCompanyId","") + && exists("log.endpointKey") && exists("log.endpointKeyType") + - add: + function: string + params: + key: log.correlationCandidate.malware_outbreak_multiple_hosts + value: match + where: '(oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && + + greaterOrEqual("log.cefSeverity", 8) && + + exists("target.malware")) && exists("dataSource") && !equals("dataSource","") && exists("log.BitdefenderGZCompanyId") && !equals("log.BitdefenderGZCompanyId","") + && exists("log.endpointKey") && exists("log.endpointKeyType")' + - delete: + fields: + - log.cefExtension diff --git a/filters/audits/bitdefender.md b/filters/audits/bitdefender.md index 19096a6ac..7fcc391ea 100644 --- a/filters/audits/bitdefender.md +++ b/filters/audits/bitdefender.md @@ -1,42 +1,105 @@ -# Bitdefender GravityZone normalization and rule review +# Bitdefender GravityZone review -Preserve attacker IPs, normalize response outcomes and severity, and align phishing/priority rules. +This replacement draft reviews one CEF filter and all 21 source rules against +ThreatWinds go-sdk v1.1.31, the official filter/rule wiki, vendor documentation and +35 bounded raw/indexed records from three instances. The previous PR is historical +review input. No customer configuration, production deployment or merge is included. -This draft targets UTMStack `v11`. It contains 1 filter changes -and 7 rule changes for this technology only. Review covered -1 filter configurations and 21 matching shipped rule files. -Unchanged rules are listed in the regression manifest; they are not duplicated in the diff. +## Confirmed producer corrections -## Contract and validation +- All 35 raw records are native Bitdefender GravityZone CEF. The module is the first + extension key immediately after the header pipe and matches all 35 indexed values. + It is produced by extension parsing; the plugin does not need to inject it. +- Parse only recognized CEF envelopes at the start of a record. Reconstruct consumed + fields at escaped CEF key boundaries so spaces are preserved and escaped key-like + text cannot invent addresses, actions or classifications. Re-read the protected raw + header after generic KV parsing so extension keys cannot overwrite its event/severity. +- Validate original IP fields before promotion and reject equivalent zero-address forms. + `dvc` is the managed endpoint. Explicit Network Attack Defense attacker/victim fields + and firewall source fields retain their documented roles. Three sampled incident + `src` values had displaced the managed endpoint. Their exact CEF roles, and those of + `spt`, are not established by the available mapping documentation; retain them under + `log` instead of attributing them to the wrong endpoint. +- Prefer the consistently present `deviceExternalId` for correlation. The alternative + product endpoint identifier differs in all 15 sampled records containing both values. + Fallback namespaces distinguish computer ID, product endpoint ID, hostname and IP; + they cannot accidentally count as different endpoints within the same namespace. +- Explicit block/quarantine actions produce `denied`. Deleted/disinfected/restored mean + successful remediation only in antimalware/behavioral modules; `still present` means + remediation failure. Report-only, ignored and no-action records do not assert failure + or a successful connection. Task success/failure is scoped to the task's own fields. +- Keep infected-object categories (`file`, `process`, `boot`, etc.) in + `log.BitdefenderGZMalwareType`; they are not SDK malware types such as trojan/ransomware. + Preserve full original values while mapping process and file basenames, safe commands, + account identifiers, hashes, domains, valid ports and firewall protocols. Scheme-less + request hosts map to `origin.domain`; a URL is populated only when complete and safe. +- Preserve original timestamps. Promote calendar-valid RFC3339 values without replacing + an existing ingress `deviceTime`. Five sampled task/inventory records have no raw time + field; their stored time cannot be credited to this filter's timestamp extraction. +- The intentional CEF severity migration uses the SDK wiki vocabulary. CEF priority is + retained in `log.cefSeverity`; high vendor priority alone is not proof of compromise. -- 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. -- 8 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. +## Rule contracts -The shared alert-contract PR supplies the reusable Go runner for the manifest in -`plugins/alerts/testdata/filter-contracts/bitdefender.json`. Apply that support before running `go test ./...` in `plugins/alerts`. +All 21 consumers have positive and negative raw-model assertions. Five history rules +use exact candidate populations and collector/company/identity scopes. Input-supplied +markers are cleared before derivation. Malware histories exclude inventory and task +records; network history requires explicit denied outcomes and a valid source address. +Cross-endpoint counts are event counts on other endpoints, not distinct-host counts. -The changed rules also require the shared alert-grouping fix to resolve `lastEvent.*` values correctly at runtime. +Sensitive task activity requires successful task status, a relevant task label, creator +and task type. Normal scan/update events no longer satisfy it. Task labels remain leads, +not proof that a console was compromised or a configuration was changed. Exclusion +alerts likewise require a successful exclusion-related task, not generic policy text. +Ordinary device-control blocks do not count as USB malware. Explicit fileless flags and +behavioral command evidence replace the assumption that every suspicious file is fileless. -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. +The phishing rule describes report-only detection without claiming a page loaded or +credentials were submitted. Other descriptions distinguish detection, blocking, +remediation and confirmed compromise. Mining URL rules consume the domain, so a mining +keyword only in a URL path does not qualify. Grouping includes source and managed-endpoint +identity, with vendor/indicator details where available. +## Validation and limits +- 77 public synthetic CEF fixtures exercise native and bounded syslog envelopes, all + consumers, escaping, header/marker injection controls, endpoint roles, original IP + guards, namespaces, hashes, ports, timestamps and benign controls. +- Actual SDK CEL, configuration/Event/Alert serialization and placeholder handling are + used. Five SDK history queries run against a local mock with below/at-threshold, + expiry, scope-isolation, missing-placeholder and identity-fallback checks. +- The private 35-record replay retains all module values, changes three target IPs back + to the managed endpoint, maps 24 explicit blocks to denied, adds seven file basenames + and fixes/adds 15 process basenames. The seven matching rule predicates across 14 + records are candidates, not observed alerts or a population-rate estimate. +- Shared schema/manifest checks are run with the reviewed alerts foundation. The single + manifest fixture is a normalization-stage negative control; the separate Go suite + supplies the raw extraction and positive-consumer tests. These layers are not additive + counts of unique scenarios. External geolocation and the closed executor are not run. -## References +CEF backslash/equals escapes are retained exactly. The documented pipeline has no general +CEF unescape step: encoded Windows directories and commands are not silently published as +decoded standard values. Safe basenames and request domains still map; the original full +values remain in `log` and protected `raw`. Full decoded Windows paths, commands and URLs +need a supported decoder and staging verification. No scheme is invented for partial URLs. -- [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) +Live evidence covers ten event classes. Other module/alias variants are compatibility +fixtures based on existing consumers and vendor field semantics, not observed CEF traffic. +The vendor syslog page documents JSON semantics rather than a complete CEF mapping table. +Legacy wrappers, parser throughput, closed-runtime behavior and alert-volume changes require +staging. New candidate markers require history warm-up (up to 24 hours); optional grouping +fields can still coalesce when absent. Filter and rules must ship together. The shared +alert-grouping fix has a separate fleet-wide rollout and is not included here. -`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. +Private record IDs, deployed configuration and replay results stay in the local evidence +pack. All three sampled deployed filters had SHA-256 +`730a7caea1cad8f31f503ab3af775f2121691db9fdd811fa595ac219ad5a553b`. + +## Sources + +- [Versioned SDK schema](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/plugins.proto) +- [Standard fields and semantics](https://github.com/threatwinds/go-sdk/wiki/Standard-Event-Schema) +- [Filter operations](https://github.com/threatwinds/go-sdk/wiki/Filter-Steps-Reference) +- [Correlation semantics](https://github.com/threatwinds/go-sdk/wiki/Implementing-Rules) +- [Bitdefender event types](https://www.bitdefender.com/business/support/en/77212-237089-event-types.html) +- [Bitdefender syslog event fields](https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html) diff --git a/plugins/alerts/bitdefender_contract_test.go b/plugins/alerts/bitdefender_contract_test.go new file mode 100644 index 000000000..67b6ecd81 --- /dev/null +++ b/plugins/alerts/bitdefender_contract_test.go @@ -0,0 +1,466 @@ +package main + +// Offline Bitdefender extraction model, not the closed EventProcessor. +// Explicit YAML grok/rename/cast/trim/add/delete and observed KV splitting are +// modeled. CEL, Event serialization, placeholder expansion, query creation and +// history thresholds use SDK v1.1.31. External geolocation is not executed. +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "os" + "path/filepath" + "reflect" + "regexp" + "strings" + "testing" + "text/template" + + "github.com/threatwinds/go-sdk/plugins" + "github.com/threatwinds/go-sdk/utils" + "github.com/tidwall/gjson" + "google.golang.org/protobuf/encoding/protojson" +) + +type bitdefFixture struct { + Name string `json:"name"` + DataSource string `json:"dataSource"` + Raw string `json:"raw"` + Expected map[string]any `json:"expected"` + Absent []string `json:"absent"` + Matches []string `json:"matches"` +} + +func bitdefPut(m map[string]any, path string, value any, remove bool) { + p := strings.Split(path, ".") + for _, k := range p[:len(p)-1] { + n, ok := m[k].(map[string]any) + if !ok { + if remove { + return + } + n = map[string]any{} + m[k] = n + } + m = n + } + if remove { + delete(m, p[len(p)-1]) + } else { + m[p[len(p)-1]] = value + } +} +func bitdefGet(m map[string]any, p string) (any, bool) { + var v any = m + for _, k := range strings.Split(p, ".") { + n, ok := v.(map[string]any) + if !ok { + return nil, false + } + v, ok = n[k] + if !ok { + return nil, false + } + } + return v, true +} +func bitdefConfig(t *testing.T) *plugins.Config { + t.Helper() + b, e := utils.ReadPbYaml("../../filters/antivirus/bitdefender_gz.yml") + if e != nil { + t.Fatal(e) + } + c := new(plugins.Config) + if e = protojson.Unmarshal(b, c); e != nil { + t.Fatal(e) + } + return c +} +func bitdefRegex(t *testing.T, g *plugins.Grok, cfg *plugins.Config) *regexp.Regexp { + t.Helper() + var pattern strings.Builder + for i, p := range g.Patterns { + if p.FieldName != "" { + fmt.Fprintf(&pattern, "(?P%s)", i, p.Pattern) + } else { + pattern.WriteString("(?:" + p.Pattern + ")") + } + } + pats := map[string]string{"greedy": ".*", "data": ".*?", "word": "[A-Za-z0-9_-]+", "space": "\\s+"} + for k, v := range cfg.Patterns { + pats[k] = v + } + tmpl, e := template.New("grok").Option("missingkey=error").Parse(pattern.String()) + if e != nil { + t.Fatal(e) + } + var b bytes.Buffer + if e = tmpl.Execute(&b, pats); e != nil { + t.Fatal(e) + } + r, e := regexp.Compile(b.String()) + if e != nil { + t.Fatal(e) + } + return r +} +func bitdefParse(t *testing.T, cfg *plugins.Config, raw string, dataSource string, cache *plugins.CELCache) string { + t.Helper() + draft := map[string]any{"raw": raw, "dataType": "antivirus-bitdefender-gz", "dataSource": dataSource, "log": map[string]any{}} + for _, stage := range cfg.Pipeline { + matched := false + for _, dataType := range stage.DataTypes { + if dataType == "antivirus-bitdefender-gz" { + matched = true + } + } + if !matched { + continue + } + for _, s := range stage.Steps { + b, e := protojson.Marshal(s) + if e != nil { + t.Fatal(e) + } + var step map[string]map[string]any + if e = json.Unmarshal(b, &step); e != nil { + t.Fatal(e) + } + for kind, body := range step { + if w, ok := body["where"].(string); ok && w != "" { + snapshot, err := json.Marshal(draft) + if err != nil { + t.Fatal(err) + } + match, e := cache.Eval(w, string(snapshot)) + if e != nil { + t.Fatal(e) + } + if !match { + continue + } + } + switch kind { + case "grok": + g := s.Grok + src := g.Source + if src == "" { + src = "raw" + } + v, ok := bitdefGet(draft, src) + if !ok { + continue + } + str, ok := v.(string) + if !ok { + t.Fatalf("non-string grok source %s", src) + } + r := bitdefRegex(t, g, cfg) + m := r.FindStringSubmatch(str) + if m == nil { + continue + } + for i, p := range g.Patterns { + if p.FieldName != "" { + bitdefPut(draft, p.FieldName, m[r.SubexpIndex(fmt.Sprintf("f%d", i))], false) + } + } + case "rename": + for _, p := range s.Rename.From { + if v, ok := bitdefGet(draft, p); ok { + bitdefPut(draft, s.Rename.To, v, false) + bitdefPut(draft, p, nil, true) + break + } + } + case "trim": + for _, p := range s.Trim.Fields { + if v, ok := bitdefGet(draft, p); ok { + str, ok := v.(string) + if !ok { + continue + } + switch s.Trim.Function { + case "prefix": + str = strings.TrimPrefix(str, s.Trim.Substring) + case "suffix": + str = strings.TrimSuffix(str, s.Trim.Substring) + default: + t.Fatalf("unsupported trim %s", s.Trim.Function) + } + bitdefPut(draft, p, str, false) + } + } + case "add": + if s.Add.Function != "string" { + t.Fatalf("unsupported add function %s", s.Add.Function) + } + bitdefPut(draft, s.Add.Params["key"].GetStringValue(), s.Add.Params["value"].AsInterface(), false) + case "delete": + for _, p := range s.Delete.Fields { + bitdefPut(draft, p, nil, true) + } + case "kv": + v, ok := bitdefGet(draft, s.Kv.Source) + if !ok { + continue + } + // Observed KV output splits quoted multiword values. + // Explicit YAML grok steps rebuild consumed fields afterward. + for _, item := range strings.Split(v.(string), s.Kv.FieldSplit) { + pair := strings.SplitN(item, s.Kv.ValueSplit, 2) + if len(pair) != 2 { + continue + } + key := pair[0] + utils.SanitizeField(&key) + if key != "" { + bitdefPut(draft, "log."+key, pair[1], false) + } + } + case "dynamic": + if s.Dynamic.Plugin != "com.utmstack.geolocation" { + t.Fatalf("unsupported dynamic plugin %s", s.Dynamic.Plugin) + } + field := s.Dynamic.Params["source"].GetStringValue() + v, ok := bitdefGet(draft, field) + if !ok { + t.Fatalf("missing dynamic source %s", field) + } + ip := net.ParseIP(fmt.Sprint(v)) + if ip == nil || ip.IsUnspecified() { + t.Fatalf("invalid address reaches geolocation: %s", field) + } + // The external geolocation service is not executed. + case "json": + source, ok := bitdefGet(draft, s.Json.Source) + if !ok { + continue + } + str, ok := source.(string) + if !ok { + t.Fatalf("JSON source is not a string") + } + var parsed map[string]any + if e := json.Unmarshal([]byte(str), &parsed); e != nil { + t.Fatal(e) + } + for key, value := range bitdefSanitizeJSON(parsed) { + bitdefPut(draft, "log."+key, value, false) + } + case "cast": + for _, field := range s.Cast.Fields { + if value, ok := bitdefGet(draft, field); ok { + switch s.Cast.To { + case "string": + bitdefPut(draft, field, utils.CastString(value), false) + case "int": + bitdefPut(draft, field, utils.CastInt64(value), false) + default: + t.Fatalf("unsupported cast %s", s.Cast.To) + } + } + } + case "drop": + return "" + default: + t.Fatalf("unsupported filter step %s", kind) + } + } + } + } + b, e := json.Marshal(draft) + if e != nil { + t.Fatal(e) + } + in := string(b) + ev := new(plugins.Event) + if e = utils.StringToProtoMessage(&in, ev); e != nil { + t.Fatal(e) + } + out, e := utils.ProtoMessageToString(ev) + if e != nil { + t.Fatal(e) + } + return *out +} +func bitdefRules(t *testing.T) map[string]*plugins.Rule { + t.Helper() + paths, e := filepath.Glob("../../rules/antivirus/bitdefender_gz/*.y*ml") + if e != nil { + t.Fatal(e) + } + out := map[string]*plugins.Rule{} + for _, p := range paths { + b, e := utils.ReadPbYaml(p) + if e != nil { + t.Fatal(e) + } + r := new(plugins.Rule) + if e = protojson.Unmarshal(b, r); e != nil { + t.Fatal(e) + } + r.Normalize() + out[strings.TrimSuffix(filepath.Base(p), filepath.Ext(p))] = r + } + return out +} + +func bitdefSanitizeJSON(input map[string]any) map[string]any { + out := map[string]any{} + for key, value := range input { + utils.SanitizeField(&key) + if nested, ok := value.(map[string]any); ok { + value = bitdefSanitizeJSON(nested) + } + out[key] = value + } + return out +} + +func bitdefFixtures(t *testing.T) []bitdefFixture { + t.Helper() + b, e := os.ReadFile("testdata/bitdefender_raw.json") + if e != nil { + t.Fatal(e) + } + var cases []bitdefFixture + if e = json.Unmarshal(b, &cases); e != nil { + t.Fatal(e) + } + return cases +} + +func TestBitdefenderRawContracts(t *testing.T) { + cfg, rules, cache := bitdefConfig(t), bitdefRules(t), plugins.NewCELCache("bitdefender-raw") + positive := map[string]int{} + negative := map[string]int{} + if len(rules) != 21 { + t.Fatalf("rules: %d", len(rules)) + } + for _, f := range bitdefFixtures(t) { + t.Run(f.Name, func(t *testing.T) { + out := bitdefParse(t, cfg, f.Raw, f.DataSource, cache) + for field, want := range f.Expected { + got := gjson.Get(out, field) + if !got.Exists() || !reflect.DeepEqual(got.Value(), want) { + t.Errorf("%s got %v want %v", field, got.Value(), want) + } + } + for _, field := range f.Absent { + if gjson.Get(out, field).Exists() { + t.Errorf("unexpected %s", field) + } + } + if gjson.Get(out, "raw").String() != f.Raw { + t.Error("raw altered") + } + expected := map[string]bool{} + for _, n := range f.Matches { + expected[n] = true + } + for name, r := range rules { + yes, e := cache.Eval(r.Where, out) + if e != nil { + t.Fatalf("%s: %v", name, e) + } + if yes != expected[name] { + t.Errorf("%s matched %v want %v", name, yes, expected[name]) + } + if yes { + positive[name]++ + } else { + negative[name]++ + } + if yes { + for _, search := range r.Correlation { + for _, term := range search.With { + value := term.Value.GetStringValue() + if strings.HasPrefix(value, "{{.") { + field := strings.TrimSuffix(strings.TrimPrefix(value, "{{."), "}}") + if !gjson.Get(out, field).Exists() { + t.Errorf("%s unresolved %s", name, field) + } + } + } + } + } + if yes { + ev := new(plugins.Event) + if e := utils.StringToProtoMessage(&out, ev); e != nil { + t.Fatal(e) + } + if r.Adversary != "origin" { + t.Errorf("unexpected actor direction %s", r.Adversary) + } + alert := &plugins.Alert{Adversary: ev.Origin, Target: ev.Target, Events: []*plugins.Event{ev}} + wire, e := utils.ProtoMessageToString(alert) + if e != nil { + t.Fatal(e) + } + if gjson.Get(out, "target.ip").String() != gjson.Get(*wire, "target.ip").String() { + t.Error("endpoint identity lost") + } + if gjson.Get(out, "origin.ip").String() != gjson.Get(*wire, "adversary.ip").String() { + t.Error("attacker identity lost") + } + } + } + }) + } + for name := range rules { + if positive[name] == 0 || negative[name] == 0 { + t.Errorf("%s missing positive/negative coverage: %d/%d", name, positive[name], negative[name]) + } + } +} +func TestBitdefenderPrivateReplay(t *testing.T) { + p := os.Getenv("BITDEFENDER_PRIVATE_DOCUMENTS") + if p == "" { + t.Skip("private live records supplied separately") + } + b, e := os.ReadFile(p) + if e != nil { + t.Fatal(e) + } + var docs []struct { + ID string `json:"id"` + Index string `json:"index"` + Instance string `json:"instance"` + Source map[string]any `json:"source"` + } + if e = json.Unmarshal(b, &docs); e != nil { + t.Fatal(e) + } + cfg, rules, cache := bitdefConfig(t), bitdefRules(t), plugins.NewCELCache("bitdefender-private") + results := []map[string]any{} + for _, d := range docs { + out := bitdefParse(t, cfg, d.Source["raw"].(string), d.Source["dataSource"].(string), cache) + matches := []string{} + for name, r := range rules { + yes, e := cache.Eval(r.Where, out) + if e != nil { + t.Fatal(e) + } + if yes { + matches = append(matches, name) + } + } + var parsed map[string]any + if e = json.Unmarshal([]byte(out), &parsed); e != nil { + t.Fatal(e) + } + results = append(results, map[string]any{"id": d.ID, "index": d.Index, "instance": d.Instance, "parsed": parsed, "matches": matches}) + } + if p := os.Getenv("BITDEFENDER_PRIVATE_OUTPUT"); p != "" { + b, e := json.MarshalIndent(results, "", " ") + if e != nil { + t.Fatal(e) + } + if e = os.WriteFile(p, b, 0600); e != nil { + t.Fatal(e) + } + } + t.Logf("replayed %d private records; predicate candidates are not observed alerts", len(docs)) +} diff --git a/plugins/alerts/bitdefender_history_test.go b/plugins/alerts/bitdefender_history_test.go new file mode 100644 index 000000000..17f48d573 --- /dev/null +++ b/plugins/alerts/bitdefender_history_test.go @@ -0,0 +1,264 @@ +package main + +// Offline history requests use the real SDK and an isolated loopback mock. +// The mock evaluates only the term/not-term/time clauses asserted below. +import ( + "encoding/json" + "fmt" + sdkos "github.com/threatwinds/go-sdk/os" + "github.com/threatwinds/go-sdk/plugins" + "github.com/tidwall/gjson" + "io" + "net/http" + "net/http/httptest" + "os" + "os/exec" + "strings" + "testing" + "time" +) + +func TestBitdefenderSDKHistory(t *testing.T) { + if os.Getenv("UTM_BITDEFENDER_HISTORY_CHILD") != "1" { + c := exec.Command(os.Args[0], "-test.run=^TestBitdefenderSDKHistory$") + c.Env = append(os.Environ(), "UTM_BITDEFENDER_HISTORY_CHILD=1") + if b, e := c.CombinedOutput(); e != nil { + t.Fatalf("isolated history: %v\n%s", e, b) + } + return + } + cfg, rules, cache := bitdefConfig(t), bitdefRules(t), plugins.NewCELCache("bitdef-history") + var history []string + var terms, notTerms map[string]string + var window time.Duration + queries := 0 + mapping := map[string]any{"properties": map[string]any{}} + props := mapping["properties"].(map[string]any) + paths := []string{"dataSource", "log.BitdefenderGZCompanyId", "log.endpointKeyType", "log.endpointKey", "target.malware", "origin.ip", "log.suid", "log.BitdefenderGZTaskType"} + for name, r := range rules { + if len(r.Correlation) > 0 { + paths = append(paths, "log.correlationCandidate."+name) + } + } + for _, path := range paths { + node := props + parts := strings.Split(path, ".") + for _, part := range parts[:len(parts)-1] { + if node[part] == nil { + node[part] = map[string]any{"properties": map[string]any{}} + } + node = node[part].(map[string]any)["properties"].(map[string]any) + } + node[parts[len(parts)-1]] = map[string]any{"type": "text", "fields": map[string]any{"keyword": map[string]any{"type": "keyword"}}} + } + props["@timestamp"] = map[string]any{"type": "date"} + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + if strings.HasSuffix(r.URL.Path, "/_mapping") { + _ = json.NewEncoder(w).Encode(map[string]any{"v11-log-antivirus-bitdefender-gz-test": map[string]any{"mappings": mapping}}) + return + } + if r.URL.Path != "/v11-log-antivirus-bitdefender-gz-*/_search" { + t.Errorf("unexpected request %s", r.URL.Path) + http.Error(w, "bad request", 400) + return + } + queries++ + body, e := io.ReadAll(r.Body) + if e != nil { + t.Error(e) + return + } + q := string(body) + clauses := append(gjson.Get(q, "query.bool.filter").Array(), gjson.Get(q, "query.bool.must").Array()...) + negatives := gjson.Get(q, "query.bool.must_not").Array() + gotTerms := map[string]string{} + gotNot := map[string]string{} + cutoff := time.Time{} + for _, clause := range clauses { + if term := clause.Get("term"); term.Exists() { + for field, value := range term.Map() { + gotTerms[strings.TrimSuffix(field, ".keyword")] = value.Get("value").String() + } + } else if span := clause.Get("range"); span.Exists() { + cutoff, e = time.Parse(time.RFC3339Nano, span.Get("@timestamp.gte").String()) + if e != nil { + t.Error(e) + } + } else { + t.Errorf("unsupported clause %s", clause.Raw) + } + } + for _, clause := range negatives { + if nested := clause.Get("bool.must"); nested.Exists() { + if len(nested.Array()) != 1 { + t.Error("unexpected negative bool") + } + clause = nested.Array()[0] + } + if term := clause.Get("term"); term.Exists() { + for field, value := range term.Map() { + gotNot[strings.TrimSuffix(field, ".keyword")] = value.Get("value").String() + } + } else { + t.Errorf("unsupported negative %s", clause.Raw) + } + } + same := func(a, b map[string]string) bool { + if len(a) != len(b) { + return false + } + for k, v := range a { + if b[k] != v { + return false + } + } + return true + } + if !same(gotTerms, terms) || !same(gotNot, notTerms) { + t.Errorf("scope mismatch: terms=%v negatives=%v", gotTerms, gotNot) + } + if delta := time.Since(cutoff) - window; delta < -2*time.Second || delta > 2*time.Second { + t.Errorf("unexpected time cutoff %v", delta) + } + hits := []map[string]any{} + for _, doc := range history { + match := true + for f, v := range gotTerms { + if !gjson.Get(doc, f).Exists() || gjson.Get(doc, f).String() != v { + match = false + } + } + for f, v := range gotNot { + if gjson.Get(doc, f).String() == v { + match = false + } + } + stamp, e := time.Parse(time.RFC3339Nano, gjson.Get(doc, "@timestamp").String()) + if e != nil || stamp.Before(cutoff) { + match = false + } + if match { + hits = append(hits, map[string]any{"_id": fmt.Sprint(len(hits)), "_index": "v11-log-antivirus-bitdefender-gz-test", "_source": map[string]any{}}) + } + } + _ = json.NewEncoder(w).Encode(map[string]any{"took": 1, "hits": map[string]any{"total": map[string]any{"value": len(hits), "relation": "eq"}, "hits": hits}}) + })) + defer server.Close() + if e := sdkos.Connect([]string{server.URL}, "", ""); e != nil { + t.Fatal(e) + } + mutate := func(doc, path string, value any) string { + var m map[string]any + if e := json.Unmarshal([]byte(doc), &m); e != nil { + t.Fatal(e) + } + bitdefPut(m, path, value, value == nil) + b, e := json.Marshal(m) + if e != nil { + t.Fatal(e) + } + return string(b) + } + cases := []struct { + rule, fixture, within string + count uint64 + cross bool + extra map[string]string + }{ + {"malware_outbreak_multiple_hosts", "AV high severity candidate", "24h", 2, true, map[string]string{"target.malware": "Generic.Test"}}, + {"multiple_malware_from_single_source", "AV high severity candidate", "1h", 3, false, nil}, + {"usb_malware_propagation", "USB malware signature", "30m", 3, false, nil}, + {"network_threat_detection", "network physical roles", "2h", 5, false, map[string]string{"origin.ip": "198.51.100.9"}}, + {"av_console_lateral_movement", "sensitive task", "1h", 3, true, map[string]string{"log.suid": "admin-id", "log.BitdefenderGZTaskType": "280"}}, + } + fixtures := map[string]bitdefFixture{} + for _, f := range bitdefFixtures(t) { + fixtures[f.Name] = f + } + for _, tc := range cases { + t.Run(tc.rule, func(t *testing.T) { + r := rules[tc.rule] + if r == nil || len(r.Correlation) != 1 { + t.Fatal("missing history") + } + search := r.Correlation[0] + if search.Count != tc.count || search.Within != tc.within { + t.Fatal("threshold/window changed") + } + var e error + window, e = time.ParseDuration(tc.within) + if e != nil { + t.Fatal(e) + } + f := fixtures[tc.fixture] + out := bitdefParse(t, cfg, f.Raw, f.DataSource, cache) + if ok, e := cache.Eval(r.Where, out); e != nil || !ok { + t.Fatalf("raw trigger failed: %v %v", ok, e) + } + marker := "log.correlationCandidate." + tc.rule + terms = map[string]string{"dataSource": "collector-test", "log.BitdefenderGZCompanyId": "company-test", "log.endpointKeyType": "computer-id", marker: "match"} + notTerms = map[string]string{} + if tc.cross { + notTerms["log.endpointKey"] = "endpoint-test" + } else { + terms["log.endpointKey"] = "endpoint-test" + } + for k, v := range tc.extra { + terms[k] = v + } + prior := mutate(out, "@timestamp", time.Now().Add(-time.Minute).UTC().Format(time.RFC3339Nano)) + if tc.cross { + prior = mutate(prior, "log.endpointKey", "other-endpoint") + } + check := func(name, doc string, count uint64, want bool) { + t.Run(name, func(t *testing.T) { + history = nil + for i := uint64(0); i < count; i++ { + history = append(history, doc) + } + ok, _, e := search.Execute(&out) + if e != nil || ok != want { + t.Fatalf("history %v want %v: %v", ok, want, e) + } + }) + } + check("below_threshold", prior, tc.count-1, false) + check("at_threshold", prior, tc.count, true) + check("expired", mutate(prior, "@timestamp", time.Now().Add(-window-time.Minute).UTC().Format(time.RFC3339Nano)), tc.count, false) + check("inside_window", mutate(prior, "@timestamp", time.Now().Add(-window+time.Minute).UTC().Format(time.RFC3339Nano)), tc.count, true) + for _, field := range []string{"dataSource", "log.BitdefenderGZCompanyId", "log.endpointKeyType"} { + check("different_"+field, mutate(prior, field, "other"), tc.count, false) + } + if tc.cross { + check("same_endpoint", mutate(prior, "log.endpointKey", "endpoint-test"), tc.count, false) + } else { + check("different_endpoint", mutate(prior, "log.endpointKey", "other"), tc.count, false) + } + check("unrelated_population", mutate(prior, marker, nil), tc.count, false) + for field := range tc.extra { + check("different_"+field, mutate(prior, field, "other"), tc.count, false) + } + for _, pair := range []struct{ old, new string }{{"deviceExternalId=endpoint-test ", ""}, {"BitdefenderGZCompanyId=company-test ", ""}} { + raw := strings.Replace(f.Raw, pair.old, pair.new, 1) + candidate := bitdefParse(t, cfg, raw, f.DataSource, cache) + // Device identity falls back to the actual hostname. Company identity has no invented fallback. + want := strings.HasPrefix(pair.old, "deviceExternalId") + if ok, e := cache.Eval(r.Where, candidate); e != nil || ok != want { + t.Fatalf("identity fallback %v want %v: %v", ok, want, e) + } + if want && gjson.Get(candidate, "log.endpointKeyType").String() != "host" { + t.Error("expected hostname namespace") + } + } + without := mutate(out, "log.endpointKey", nil) + before := queries + if _, _, e := search.Execute(&without); e == nil { + t.Error("missing required placeholder accepted") + } + if queries != before { + t.Error("missing placeholder executed query") + } + }) + } +} diff --git a/plugins/alerts/testdata/bitdefender_raw.json b/plugins/alerts/testdata/bitdefender_raw.json new file mode 100644 index 000000000..7d5846004 --- /dev/null +++ b/plugins/alerts/testdata/bitdefender_raw.json @@ -0,0 +1,891 @@ +[ + { + "name": "blocked incident keeps managed endpoint", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test src=198.51.100.5 spt=443 BitdefenderGZMainAction=blocked BitdefenderGZDetectionName=Generic.Test", + "expected": { + "target.ip": "192.0.2.10", + "log.src": "198.51.100.5", + "actionResult": "denied", + "target.malware": "Generic.Test" + }, + "absent": [ + "origin.ip", + "origin.port" + ], + "matches": [] + }, + { + "name": "native first extension key", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test", + "expected": { + "log.BitdefenderGZModule": "new-incident", + "log.endpointKey": "endpoint-test", + "log.endpointKeyType": "computer-id" + }, + "absent": [], + "matches": [] + }, + { + "name": "AV action blocked", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|AntiMalware|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=blocked BitdefenderGZMalwareName=Generic.Test", + "expected": { + "action": "blocked", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "AV action Disinfected", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|AntiMalware|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=Disinfected BitdefenderGZMalwareName=Generic.Test", + "expected": { + "action": "Disinfected", + "actionResult": "success" + }, + "absent": [], + "matches": [] + }, + { + "name": "AV action deleted", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|AntiMalware|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=deleted BitdefenderGZMalwareName=Generic.Test", + "expected": { + "action": "deleted", + "actionResult": "success" + }, + "absent": [], + "matches": [] + }, + { + "name": "AV action restored", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|AntiMalware|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=restored BitdefenderGZMalwareName=Generic.Test", + "expected": { + "action": "restored", + "actionResult": "success" + }, + "absent": [], + "matches": [] + }, + { + "name": "AV action quarantined", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|AntiMalware|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=quarantined BitdefenderGZMalwareName=Generic.Test", + "expected": { + "action": "quarantined", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "AV action still present", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|AntiMalware|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=still present BitdefenderGZMalwareName=Generic.Test", + "expected": { + "action": "still present", + "actionResult": "failure" + }, + "absent": [], + "matches": [ + "quarantine_failure_detection" + ] + }, + { + "name": "AV action ignored", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|AntiMalware|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=ignored BitdefenderGZMalwareName=Generic.Test", + "expected": { + "action": "ignored" + }, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "AV action no action", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|AntiMalware|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=no action BitdefenderGZMalwareName=Generic.Test", + "expected": { + "action": "no action" + }, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "block fw", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|fw|3|BitdefenderGZModule=fw BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=portscan_blocked", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "block aph", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|aph|3|BitdefenderGZModule=aph BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=aph_blocked", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "block dp", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|dp|3|BitdefenderGZModule=dp BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=data_protection_blocked", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "block uc", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|uc|3|BitdefenderGZModule=uc BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=uc_site_blocked", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "phishing report only", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|aph|3|BitdefenderGZModule=aph BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=reportOnly request=https://phishing.example.test/login suser=analyst", + "expected": { + "origin.url": "https://phishing.example.test/login", + "target.user": "analyst" + }, + "absent": [ + "actionResult" + ], + "matches": [ + "phishing_access_blocked" + ] + }, + { + "name": "encoded URL remains vendor data", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|aph|3|BitdefenderGZModule=aph BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test act=aph_blocked request=https://example.test/?id\\=1", + "expected": { + "log.request": "https://example.test/?id\\=1" + }, + "absent": [ + "origin.url" + ], + "matches": [] + }, + { + "name": "encoded windows path retains original", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|av|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test filePath=C:\\\\Program Files\\\\Example\\\\bad.exe BitdefenderGZMalwareType=file", + "expected": { + "target.filename": "bad.exe", + "log.filePath": "C:\\\\Program Files\\\\Example\\\\bad.exe", + "log.BitdefenderGZMalwareType": "file" + }, + "absent": [ + "target.path", + "target.malwareType" + ], + "matches": [] + }, + { + "name": "process basename command and hash", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|avc|3|BitdefenderGZModule=avc BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test sproc=C:\\\\Windows\\\\System32\\\\cmd.exe BitdefenderGZProcessCommandLine=cmd.exe /c echo test BitdefenderGZMalwareHash=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expected": { + "target.process": "cmd.exe", + "target.command": "cmd.exe /c echo test", + "target.sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "absent": [], + "matches": [ + "zero_day_malware_detection" + ] + }, + { + "name": "safe POSIX file mapping", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|av|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test filePath=/opt/example/bad.bin", + "expected": { + "target.path": "/opt/example/", + "target.filename": "bad.bin" + }, + "absent": [], + "matches": [] + }, + { + "name": "invalid hash retained", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|av|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZMalwareHash=not-a-hash fileHash=bad", + "expected": {}, + "absent": [ + "target.sha256", + "target.md5" + ], + "matches": [] + }, + { + "name": "original IP guard 0.0.0.0", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|50|Firewall|3|BitdefenderGZModule=fw dvc=0.0.0.0 BitdefenderGZEventSourceIP=0.0.0.0", + "expected": { + "log.dvc": "0.0.0.0", + "log.BitdefenderGZEventSourceIP": "0.0.0.0" + }, + "absent": [ + "origin.ip", + "target.ip", + "log.endpointKey" + ], + "matches": [] + }, + { + "name": "original IP guard ::", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|50|Firewall|3|BitdefenderGZModule=fw dvc=:: BitdefenderGZEventSourceIP=::", + "expected": { + "log.dvc": "::", + "log.BitdefenderGZEventSourceIP": "::" + }, + "absent": [ + "origin.ip", + "target.ip", + "log.endpointKey" + ], + "matches": [] + }, + { + "name": "original IP guard 0:0:0:0:0:0:0:0", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|50|Firewall|3|BitdefenderGZModule=fw dvc=0:0:0:0:0:0:0:0 BitdefenderGZEventSourceIP=0:0:0:0:0:0:0:0", + "expected": { + "log.dvc": "0:0:0:0:0:0:0:0", + "log.BitdefenderGZEventSourceIP": "0:0:0:0:0:0:0:0" + }, + "absent": [ + "origin.ip", + "target.ip", + "log.endpointKey" + ], + "matches": [] + }, + { + "name": "original IP guard ::ffff:0.0.0.0", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|50|Firewall|3|BitdefenderGZModule=fw dvc=::ffff:0.0.0.0 BitdefenderGZEventSourceIP=::ffff:0.0.0.0", + "expected": { + "log.dvc": "::ffff:0.0.0.0", + "log.BitdefenderGZEventSourceIP": "::ffff:0.0.0.0" + }, + "absent": [ + "origin.ip", + "target.ip", + "log.endpointKey" + ], + "matches": [] + }, + { + "name": "original IP guard host.example", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|50|Firewall|3|BitdefenderGZModule=fw dvc=host.example BitdefenderGZEventSourceIP=host.example", + "expected": { + "log.dvc": "host.example", + "log.BitdefenderGZEventSourceIP": "host.example" + }, + "absent": [ + "origin.ip", + "target.ip", + "log.endpointKey" + ], + "matches": [] + }, + { + "name": "original IP guard 999.1.2.3", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|50|Firewall|3|BitdefenderGZModule=fw dvc=999.1.2.3 BitdefenderGZEventSourceIP=999.1.2.3", + "expected": { + "log.dvc": "999.1.2.3", + "log.BitdefenderGZEventSourceIP": "999.1.2.3" + }, + "absent": [ + "origin.ip", + "target.ip", + "log.endpointKey" + ], + "matches": [] + }, + { + "name": "original IP guard -", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|50|Firewall|3|BitdefenderGZModule=fw dvc=- BitdefenderGZEventSourceIP=-", + "expected": { + "log.dvc": "-", + "log.BitdefenderGZEventSourceIP": "-" + }, + "absent": [ + "origin.ip", + "target.ip", + "log.endpointKey" + ], + "matches": [] + }, + { + "name": "network physical roles", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|network-monitor|9|BitdefenderGZModule=network-monitor BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZDetectionAttackerIp=198.51.100.9 BitdefenderGZDetectionVictimIp=192.0.2.12 BitdefenderGZDetectionLocalPort=443 BitdefenderGZMainAction=block", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.12", + "target.port": 443, + "actionResult": "denied" + }, + "absent": [], + "matches": [ + "network_threat_detection" + ] + }, + { + "name": "bad local port 0", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|network-monitor|3|BitdefenderGZModule=network-monitor BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZDetectionLocalPort=0", + "expected": {}, + "absent": [ + "target.port" + ], + "matches": [] + }, + { + "name": "bad local port 65536", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|network-monitor|3|BitdefenderGZModule=network-monitor BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZDetectionLocalPort=65536", + "expected": {}, + "absent": [ + "target.port" + ], + "matches": [] + }, + { + "name": "bad local port -1", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|network-monitor|3|BitdefenderGZModule=network-monitor BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZDetectionLocalPort=-1", + "expected": {}, + "absent": [ + "target.port" + ], + "matches": [] + }, + { + "name": "bad local port 443x", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|network-monitor|3|BitdefenderGZModule=network-monitor BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZDetectionLocalPort=443x", + "expected": {}, + "absent": [ + "target.port" + ], + "matches": [] + }, + { + "name": "escaped key cannot forge action", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|aph|3|BitdefenderGZModule=aph BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test msg=Message act\\=reportOnly dvc\\=198.51.100.99 act=aph_blocked", + "expected": { + "action": "aph_blocked", + "target.ip": "192.0.2.10", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "spaces preserve detection", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|av|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZMalwareName=Generic Multi Word Name act=deleted", + "expected": { + "target.malware": "Generic Multi Word Name", + "actionResult": "success" + }, + "absent": [], + "matches": [] + }, + { + "name": "unrecognized embedded CEF other", + "dataSource": "collector-test", + "raw": "not syslog CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test src=198.51.100.5 spt=443 BitdefenderGZMainAction=blocked BitdefenderGZDetectionName=Generic.Test", + "expected": {}, + "absent": [ + "target.ip", + "action", + "log.BitdefenderGZModule" + ], + "matches": [] + }, + { + "name": "unrecognized embedded CEF ", + "dataSource": "collector-test", + "raw": "not syslog CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test src=198.51.100.5 spt=443 BitdefenderGZMainAction=blocked BitdefenderGZDetectionName=Generic.Test", + "expected": {}, + "absent": [ + "target.ip", + "action", + "log.BitdefenderGZModule" + ], + "matches": [] + }, + { + "name": "foreign vendor header", + "dataSource": "collector-test", + "raw": "CEF:0|Other|Product|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test src=198.51.100.5 spt=443 BitdefenderGZMainAction=blocked BitdefenderGZDetectionName=Generic.Test", + "expected": {}, + "absent": [ + "target.ip", + "action", + "log.BitdefenderGZModule" + ], + "matches": [] + }, + { + "name": "wrapper <134>Sep 17 10:20:30 relay ", + "dataSource": "collector-test", + "raw": "<134>Sep 17 10:20:30 relay CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test src=198.51.100.5 spt=443 BitdefenderGZMainAction=blocked BitdefenderGZDetectionName=Generic.Test", + "expected": { + "target.ip": "192.0.2.10", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "wrapper <134>1 2026-09-17T10:20:30Z relay product - - - ", + "dataSource": "collector-test", + "raw": "<134>1 2026-09-17T10:20:30Z relay product - - - CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test src=198.51.100.5 spt=443 BitdefenderGZMainAction=blocked BitdefenderGZDetectionName=Generic.Test", + "expected": { + "target.ip": "192.0.2.10", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "antitampering", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|antitampering|3|BitdefenderGZModule=antitampering BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test", + "expected": {}, + "absent": [], + "matches": [ + "antivirus_service_stopped" + ] + }, + { + "name": "realtime disabled sanitized alias", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|modules|3|BitdefenderGZModule=modules BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test malware_status=0", + "expected": {}, + "absent": [], + "matches": [ + "realtime_protection_disabled" + ] + }, + { + "name": "avc disabled", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|modules|3|BitdefenderGZModule=modules BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZAvcStatus=0", + "expected": {}, + "absent": [], + "matches": [ + "av_policy_override" + ] + }, + { + "name": "normal modules enabled", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|modules|3|BitdefenderGZModule=modules BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test malware_status=1 BitdefenderGZAvcStatus=1", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "boot object remains vendor type", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|av|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZMalwareType=boot", + "expected": { + "log.BitdefenderGZMalwareType": "boot" + }, + "absent": [ + "target.malwareType" + ], + "matches": [ + "bootkit_detection" + ] + }, + { + "name": "memory object", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|av|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZMalwareType=process", + "expected": {}, + "absent": [], + "matches": [ + "memory_threat_detection" + ] + }, + { + "name": "miner signature", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|av|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZMalwareName=Trojan.Xmrig", + "expected": {}, + "absent": [], + "matches": [ + "crypto_mining_detection" + ] + }, + { + "name": "exchange malware", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|exchange-malware|3|BitdefenderGZModule=exchange-malware BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test", + "expected": {}, + "absent": [], + "matches": [ + "email_threat_spreading" + ] + }, + { + "name": "fileless explicit", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|hd|3|BitdefenderGZModule=hd BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZIsFilelessAttack=true", + "expected": {}, + "absent": [], + "matches": [ + "fileless_malware_detection", + "zero_day_malware_detection" + ] + }, + { + "name": "fileless sanitized alias", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|hd|3|BitdefenderGZModule=hd BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test is_fileless_attack=true", + "expected": {}, + "absent": [], + "matches": [ + "fileless_malware_detection", + "zero_day_malware_detection" + ] + }, + { + "name": "fileless ambiguous label negative", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|hd|3|BitdefenderGZModule=hd BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZAttackType=suspicious files", + "expected": {}, + "absent": [], + "matches": [ + "zero_day_malware_detection" + ] + }, + { + "name": "AV high severity candidate", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|av|9|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZMalwareName=Generic.Test", + "expected": {}, + "absent": [], + "matches": [ + "high_severity_threat_detection", + "malware_outbreak_multiple_hosts", + "multiple_malware_from_single_source" + ] + }, + { + "name": "targeted high severity", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|hd|9|BitdefenderGZModule=hd BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZAttackTypes=targeted attack", + "expected": {}, + "absent": [], + "matches": [ + "apt_detection", + "high_severity_threat_detection", + "multiple_malware_from_single_source", + "zero_day_malware_detection" + ] + }, + { + "name": "ransomware signature", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|av|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZMalwareName=Ransom.Test", + "expected": {}, + "absent": [], + "matches": [ + "ransomware_behavior_detection" + ] + }, + { + "name": "rootkit signature", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|av|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZMalwareName=Rootkit.Test", + "expected": {}, + "absent": [], + "matches": [ + "rootkit_detection", + "bootkit_detection" + ] + }, + { + "name": "USB malware signature", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|av|3|BitdefenderGZModule=av BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZDetectionName=USB.Worm", + "expected": {}, + "absent": [], + "matches": [ + "usb_malware_propagation" + ] + }, + { + "name": "ordinary device block negative", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|device-control|3|BitdefenderGZModule=device-control BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZAction=blocked", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "sensitive task", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|task-status|3|BitdefenderGZModule=task-status BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test msg=Run script maintenance BitdefenderGZTaskType=280 BitdefenderGZTaskSuccessful=1 suid=admin-id", + "expected": { + "origin.user": "admin-id" + }, + "absent": [], + "matches": [ + "av_console_lateral_movement" + ] + }, + { + "name": "normal successful task", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|task-status|3|BitdefenderGZModule=task-status BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test msg=Scan task BitdefenderGZTaskType=272 BitdefenderGZTaskSuccessful=1 suid=admin-id", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "exclusion task", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|task-status|3|BitdefenderGZModule=task-status BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test msg=Update exclusion list BitdefenderGZTaskSuccessful=1", + "expected": {}, + "absent": [], + "matches": [ + "suspicious_exclusions_added" + ] + }, + { + "name": "failed exclusion task", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|task-status|3|BitdefenderGZModule=task-status BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test msg=Update exclusion list BitdefenderGZTaskSuccessful=0 BitdefenderGZErrorCode=5", + "expected": { + "actionResult": "failure" + }, + "absent": [], + "matches": [] + }, + { + "name": "ordinary policy task negative", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|task-status|3|BitdefenderGZModule=task-status BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test msg=Update policy BitdefenderGZTaskSuccessful=1", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "forged history marker cleared", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|task-status|3|BitdefenderGZModule=task-status BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test correlationCandidate.multiple_malware_from_single_source=match", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "distinct vendor identifiers keep stable computer identity", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZEndpointId=separate-product-id", + "expected": { + "log.endpointKey": "endpoint-test", + "log.endpointKeyType": "computer-id" + }, + "absent": [], + "matches": [] + }, + { + "name": "extension cannot override header", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test eventType=AntiMalware cefSeverity=9 cefExtension=x", + "expected": { + "log.eventType": "new-incident", + "log.cefSeverity": "3", + "severity": "info" + }, + "absent": [], + "matches": [] + }, + { + "name": "escaped extension cannot override header", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test msg=eventType\\=AntiMalware cefSeverity\\=9", + "expected": { + "log.eventType": "new-incident", + "log.cefSeverity": "3", + "severity": "info" + }, + "absent": [], + "matches": [] + }, + { + "name": "invalid or unsupported time 2026-02-29T01:00:00Z", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test start=2026-02-29T01:00:00Z", + "expected": { + "log.start": "2026-02-29T01:00:00Z" + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "invalid or unsupported time 2026-04-31T01:00:00Z", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test start=2026-04-31T01:00:00Z", + "expected": { + "log.start": "2026-04-31T01:00:00Z" + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "invalid or unsupported time 2026-09-00T01:00:00Z", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test start=2026-09-00T01:00:00Z", + "expected": { + "log.start": "2026-09-00T01:00:00Z" + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "invalid or unsupported time 2026-09-17T01:00:00+29:00", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test start=2026-09-17T01:00:00+29:00", + "expected": { + "log.start": "2026-09-17T01:00:00+29:00" + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "invalid or unsupported time 1690000000000", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test start=1690000000000", + "expected": { + "log.start": "1690000000000" + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "valid time 2024-02-29T01:00:00Z", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test start=2024-02-29T01:00:00Z", + "expected": { + "deviceTime": "2024-02-29T01:00:00Z" + }, + "absent": [], + "matches": [] + }, + { + "name": "valid time 2026-09-17T01:00:00.123+03:00", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test start=2026-09-17T01:00:00.123+03:00", + "expected": { + "deviceTime": "2026-09-17T01:00:00.123+03:00" + }, + "absent": [], + "matches": [] + }, + { + "name": "endpoint id fallback", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=new-incident BitdefenderGZCompanyId=company-test BitdefenderGZEndpointId=product-id dvc=192.0.2.10 dvchost=workstation-test", + "expected": { + "log.endpointKey": "product-id", + "log.endpointKeyType": "endpoint-id" + }, + "absent": [], + "matches": [] + }, + { + "name": "schemeless phishing domain", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=uc BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test request=phishing.example.test/signin", + "expected": { + "origin.domain": "phishing.example.test" + }, + "absent": [ + "origin.url" + ], + "matches": [] + }, + { + "name": "escaped scheme-less query domain", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=uc BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test request=phishing.example.test/?id\\=1", + "expected": { + "origin.domain": "phishing.example.test" + }, + "absent": [ + "origin.url" + ], + "matches": [] + }, + { + "name": "mining domain indicator", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=uc BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test request=coinhive.example.test/", + "expected": { + "origin.domain": "coinhive.example.test" + }, + "absent": [ + "origin.url" + ], + "matches": [ + "crypto_mining_detection" + ] + }, + { + "name": "mining name in URL path negative", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|new-incident|3|BitdefenderGZModule=uc BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test request=safe.example.test/coinhive", + "expected": { + "origin.domain": "safe.example.test" + }, + "absent": [ + "origin.url" + ], + "matches": [] + } +] diff --git a/plugins/alerts/testdata/filter-contracts/bitdefender.json b/plugins/alerts/testdata/filter-contracts/bitdefender.json index aeae2463e..31e318745 100644 --- a/plugins/alerts/testdata/filter-contracts/bitdefender.json +++ b/plugins/alerts/testdata/filter-contracts/bitdefender.json @@ -28,142 +28,48 @@ ], "fixtures": [ { - "name": "Bitdefender phishing aph_blocked", + "name": "Bitdefender input-supplied classification and history are cleared", "filter": "antivirus/bitdefender_gz.yml", "input": { + "dataType": "antivirus-bitdefender-gz", "log": { - "actFull": "aph_blocked", - "BitdefenderGZModule": "aph" + "BitdefenderGZModule": "aph", + "act": "reportOnly", + "endpointKey": "forged", + "correlationCandidate": { + "network_threat_detection": "match" + } } }, - "expected": { - "actionResult": "denied" - }, - "absent": [], - "rules": { - "rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml": false - } - }, - { - "name": "Bitdefender phishing reportOnly", - "filter": "antivirus/bitdefender_gz.yml", - "input": { - "log": { - "actFull": "reportOnly", - "BitdefenderGZModule": "aph" - } - }, - "expected": { - "actionResult": "failure" - }, - "absent": [], - "rules": { - "rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml": true - } - }, - { - "name": "Bitdefender attacker priority", - "filter": "antivirus/bitdefender_gz.yml", - "input": { - "log": { - "BitdefenderGZDetectionAttackerIp": "198.51.100.10", - "BitdefenderGZEventSourceIP": "10.0.0.2" - } - }, - "expected": { - "origin.ip": "198.51.100.10" - }, - "absent": [], - "rules": {} - }, - { - "name": "Bitdefender CEF priority 0", - "filter": "antivirus/bitdefender_gz.yml", - "input": { - "log": { - "severity": "0", - "BitdefenderGZModule": "network-monitor" - } - }, - "expected": { - "severity": "info", - "log.cefSeverity": "0" - }, - "absent": [], - "rules": { - "rules/antivirus/bitdefender_gz/network_threat_detection.yml": false - } - }, - { - "name": "Bitdefender CEF priority 3", - "filter": "antivirus/bitdefender_gz.yml", - "input": { - "log": { - "severity": "3", - "BitdefenderGZModule": "network-monitor" - } - }, - "expected": { - "severity": "info", - "log.cefSeverity": "3" - }, - "absent": [], - "rules": { - "rules/antivirus/bitdefender_gz/network_threat_detection.yml": false - } - }, - { - "name": "Bitdefender CEF priority 6", - "filter": "antivirus/bitdefender_gz.yml", - "input": { - "log": { - "severity": "6", - "BitdefenderGZModule": "network-monitor" - } - }, - "expected": { - "severity": "warning", - "log.cefSeverity": "6" - }, - "absent": [], - "rules": { - "rules/antivirus/bitdefender_gz/network_threat_detection.yml": false - } - }, - { - "name": "Bitdefender CEF priority 8", - "filter": "antivirus/bitdefender_gz.yml", - "input": { - "log": { - "severity": "8", - "BitdefenderGZModule": "network-monitor" - } - }, - "expected": { - "severity": "error", - "log.cefSeverity": "8" - }, - "absent": [], - "rules": { - "rules/antivirus/bitdefender_gz/network_threat_detection.yml": true - } - }, - { - "name": "Bitdefender CEF priority 10", - "filter": "antivirus/bitdefender_gz.yml", - "input": { - "log": { - "severity": "10", - "BitdefenderGZModule": "network-monitor" - } - }, - "expected": { - "severity": "critical", - "log.cefSeverity": "10" - }, - "absent": [], + "expected": {}, + "absent": [ + "log.BitdefenderGZModule", + "log.act", + "log.endpointKey", + "log.correlationCandidate" + ], "rules": { - "rules/antivirus/bitdefender_gz/network_threat_detection.yml": true + "rules/antivirus/bitdefender_gz/antivirus_service_stopped.yml": false, + "rules/antivirus/bitdefender_gz/apt_detection.yml": false, + "rules/antivirus/bitdefender_gz/av_console_lateral_movement.yml": false, + "rules/antivirus/bitdefender_gz/av_policy_override.yml": false, + "rules/antivirus/bitdefender_gz/bootkit_detection.yml": false, + "rules/antivirus/bitdefender_gz/crypto_mining_detection.yml": false, + "rules/antivirus/bitdefender_gz/email_threat_spreading.yml": false, + "rules/antivirus/bitdefender_gz/fileless_malware_detection.yml": false, + "rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml": false, + "rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml": false, + "rules/antivirus/bitdefender_gz/memory_threat_detection.yml": false, + "rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml": false, + "rules/antivirus/bitdefender_gz/network_threat_detection.yml": false, + "rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml": false, + "rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml": false, + "rules/antivirus/bitdefender_gz/ransomware_behavior_detection.yml": false, + "rules/antivirus/bitdefender_gz/realtime_protection_disabled.yml": false, + "rules/antivirus/bitdefender_gz/rootkit_detection.yml": false, + "rules/antivirus/bitdefender_gz/suspicious_exclusions_added.yml": false, + "rules/antivirus/bitdefender_gz/usb_malware_propagation.yml": false, + "rules/antivirus/bitdefender_gz/zero_day_malware_detection.yml": false } } ] diff --git a/rules/antivirus/bitdefender_gz/antivirus_service_stopped.yml b/rules/antivirus/bitdefender_gz/antivirus_service_stopped.yml index 9bf06fe07..fd658575c 100644 --- a/rules/antivirus/bitdefender_gz/antivirus_service_stopped.yml +++ b/rules/antivirus/bitdefender_gz/antivirus_service_stopped.yml @@ -1,41 +1,28 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Bitdefender GravityZone Antivirus Service Stopped or Tampered +- antivirus-bitdefender-gz +name: Bitdefender GravityZone Antitampering Detection impact: confidentiality: 2 integrity: 3 availability: 3 category: Defense Evasion -technique: "T1562.001 - Impair Defenses: Disable or Modify Tools" +technique: 'T1562.001 - Impair Defenses: Disable or Modify Tools' adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1562/001/ -description: | - Detects when the Bitdefender security agent is disabled, stopped, or under an integrity attack. Three independent signals, all directly emitted by the vendor: +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1562/001/ +description: 'GravityZone reported an antitampering event. This identifies attempted interference with protection; it does not by itself + prove that a service stopped or that the attempt succeeded. Review the vendor event details, endpoint state and authorized maintenance + before containment. - - Antitampering event with detection_technique "Callback Evasion". Bitdefender's own definition: a post-tampering incident where critical protection mechanisms have been disabled. This is the strongest possible signal that the agent is compromised. - - Antitampering event with detection_technique "Vulnerable Drivers". A driver exploitable against the security agent is present on the endpoint, which is the precursor to tampering. - - Product Modules Status event reporting that the Antimalware (malware_status) or Advanced Threat Control (avc_status) module has flipped to disabled (value 0). - - Next Steps: - 1. Isolate the endpoint from the network. The agent is either disabled or being actively targeted, and normal telemetry from this host is now untrusted. - 2. Identify what changed: - - For antitampering events, log.BitdefenderGZDriverName / log.driverName names the driver and log.BitdefenderGZDetectionAction shows whether Bitdefender blocked it (deny/kill) or only observed it (reportOnly). reportOnly means the tampering succeeded. - - For modules events, log.BitdefenderGZMalwareStatus and log.BitdefenderGZAvcStatus show which protection was turned off. - 3. Correlate with logon and admin activity on the same target.host in the surrounding minutes. Legitimate module changes are rare and normally come from a scheduled policy push; ad-hoc changes deserve attention. - 4. Assume the endpoint is compromised until proven otherwise. Any threats that arrived while protection was down would not have been detected. - 5. Re-enable protection through GravityZone Control Center, do not rely on the local agent. If the module refuses to come back up, treat as a reimage candidate. -where: | - equals("log.BitdefenderGZModule", "antitampering") || - (equals("log.BitdefenderGZModule", "modules") && - (equals("log.malware_status", "0") || - equals("log.avc_status", "0") || - equals("log.BitdefenderGZMalwareStatus", "0") || - equals("log.BitdefenderGZAvcStatus", "0"))) + ' +where: equals("log.BitdefenderGZModule", "antitampering") groupBy: - - target.host - - lastEvent.log.BitdefenderGZModule +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- lastEvent.log.BitdefenderGZModule diff --git a/rules/antivirus/bitdefender_gz/apt_detection.yml b/rules/antivirus/bitdefender_gz/apt_detection.yml index 5f30c2629..319e8996c 100644 --- a/rules/antivirus/bitdefender_gz/apt_detection.yml +++ b/rules/antivirus/bitdefender_gz/apt_detection.yml @@ -1,52 +1,30 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Advanced Persistent Threat (APT) Detection +- antivirus-bitdefender-gz +name: Bitdefender GravityZone High Severity Targeted or Sandbox Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Execution -technique: "T1204.002 - User Execution: Malicious File" +technique: 'T1204.002 - User Execution: Malicious File' adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1204/002/ - - https://attack.mitre.org/techniques/T1110/ -description: | - Detects high-severity threats that Bitdefender itself classifies as targeted or sophisticated, rather than commodity malware. Three independent signals, all requiring CEF severity 8 or above: +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1204/002/ +- https://attack.mitre.org/techniques/T1110/ +description: 'A high-severity vendor detection indicates a targeted-attack label, sandbox finding or credential-access technique. These + indicators do not establish an advanced persistent actor. Review the detection and endpoint evidence before attributing a campaign. - - HyperDetect reporting attack_type "targeted attack". HyperDetect is Bitdefender's machine-learning layer tuned specifically for targeted attacks and advanced threats, and it labels the detection itself. - - Sandbox Analyzer detection. A verdict here means an unknown payload was detonated and found malicious after evading signature-based detection, which is characteristic of tooling built for a specific target. - - Network Attack Defense reporting attack technique credentialAccess. Credential access is a core phase of a persistent intrusion, and covers Kerberos brute force and password-stealer traffic. - - Per-incident MITRE technique identifiers are carried by the vendor in log.BitdefenderGZAttCkId as an array, and are more precise than this rule's single technique tag. - - Next Steps: - 1. Determine the affected endpoint from target.host and the attacker from log.BitdefenderGZDetectionAttackerIp when the source is Network Attack Defense - 2. Read the vendor's own classification before assuming scope: - - log.BitdefenderGZAttCkId lists the MITRE techniques Bitdefender attributed to the incident - - log.BitdefenderGZDetectionName names the detection, for example Attack.Bruteforce.KERBEROS or PrivacyThreat.PasswordStealer - 3. For credentialAccess detections, treat the credentials as compromised: - - Identify the targeted accounts and force a password reset - - Review authentication logs from the attacker IP for any successful logon - - Check for Kerberos ticket anomalies if the detection names KERBEROS - 4. Establish persistence and lateral movement: - - Review process ancestry via log.BitdefenderGZDetectionName and target.path - - Correlate other events from the same target.host in the surrounding hours - - Search the environment for the same detection name on other hosts - 5. Collect forensic artifacts before remediating - memory image and endpoint logs - since a targeted intrusion warrants attribution work - 6. Isolate the endpoint if the detection action shows the threat was not blocked, then hunt for what ran while it was active -where: | - (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) && - ( - (equals("log.BitdefenderGZModule", "hd") && - regexMatch("log.BitdefenderGZAttackTypes", "(?i)targeted attack")) || - equals("log.BitdefenderGZModule", "network-sandboxing") || - equals("log.BitdefenderGZDetectionAttackTechnique", "credentialAccess") - ) + ' +where: "greaterOrEqual(\"log.cefSeverity\", 8) &&\n(\n (equals(\"log.BitdefenderGZModule\", \"hd\") &&\n regexMatch(\"log.BitdefenderGZAttackTypes\"\ + , \"(?i)targeted attack\")) ||\n equals(\"log.BitdefenderGZModule\", \"network-sandboxing\") ||\n equals(\"log.BitdefenderGZDetectionAttackTechnique\"\ + , \"credentialAccess\")\n)\n" groupBy: - - target.host - - lastEvent.log.BitdefenderGZDetectionName \ No newline at end of file +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- lastEvent.log.BitdefenderGZDetectionName diff --git a/rules/antivirus/bitdefender_gz/av_console_lateral_movement.yml b/rules/antivirus/bitdefender_gz/av_console_lateral_movement.yml index 4fc15885e..f24788256 100644 --- a/rules/antivirus/bitdefender_gz/av_console_lateral_movement.yml +++ b/rules/antivirus/bitdefender_gz/av_console_lateral_movement.yml @@ -1,44 +1,55 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Bitdefender GravityZone Task Burst Across Endpoints +- antivirus-bitdefender-gz +name: Bitdefender GravityZone Sensitive Task Activity Across Endpoints impact: - confidentiality: 3 - integrity: 3 - availability: 3 + confidentiality: 1 + integrity: 2 + availability: 1 category: Lateral Movement -technique: "T1072 - Software Deployment Tools" +technique: T1072 - Software Deployment Tools adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1072/ -description: | - Detects a burst of Bitdefender GravityZone task deployments across many endpoints in a short window, which is the observable trace of a compromised admin console being used to push activity to managed machines. Bitdefender's own management console is a first-class software deployment tool by MITRE's definition, so its abuse is T1072. +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1072/ +description: 'A successful task with a name indicating uninstall, quarantine restoration, script execution or command execution is accompanied + by at least three similarly classified records of the same task type and creator on other endpoints within one hour. This is an event + count, not three distinct endpoints. Normal scan/update status events do not count. Task labels and bursts alone do not prove console + compromise or lateral movement. Check the creator, actual task operation and approved change record before containment. - A single task event is normal operational activity. Three or more distinct endpoints receiving tasks from the same admin identity within an hour is the shape of console-driven lateral movement. - - Next Steps: - 1. Identify the admin identity via log.BitdefenderGZUserName or the user object in the task. Correlate with authentication-audit events from the same identity for logon location and browser. - 2. Read log.BitdefenderGZTaskName and log.BitdefenderGZTaskType. Scan and update tasks are routine; script execution, arbitrary install, and uninstall tasks deserve scrutiny. - 3. List every target.host that received a task in this window and treat the set as the potentially-affected estate, not each host individually. - 4. Cross-check against the change control system. A legitimate task burst normally has a ticket; an ad-hoc burst outside a change window is the strongest indicator. - 5. Suspend the admin account and rotate its credentials before deciding whether it was compromised or misused. Then audit every endpoint that received a task for signs of the tasks having succeeded. - 6. Review Control Center authentication logs for the source IP behind the admin session. External or unusual geographies for the admin login argue for an account compromise rather than an insider. -where: | - equals("log.BitdefenderGZModule", "task-status") + ' +where: equals("log.correlationCandidate.av_console_lateral_movement","match") correlation: - - indexPattern: v11-log-antivirus-bitdefender-gz-* - within: 1h - count: 3 - with: - - field: log.BitdefenderGZModule - operator: filter_term - value: "task-status" - - field: target.host - operator: must_not_term - value: "{{.target.host}}" +- indexPattern: v11-log-antivirus-bitdefender-gz-* + within: 1h + count: 3 + with: + - field: log.correlationCandidate.av_console_lateral_movement + operator: filter_term + value: match + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.BitdefenderGZCompanyId + operator: filter_term + value: '{{.log.BitdefenderGZCompanyId}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: must_not_term + value: '{{.log.endpointKey}}' + - field: log.suid + operator: filter_term + value: '{{.log.suid}}' + - field: log.BitdefenderGZTaskType + operator: filter_term + value: '{{.log.BitdefenderGZTaskType}}' groupBy: - - target.host - - lastEvent.log.BitdefenderGZTaskName +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- lastEvent.log.BitdefenderGZTaskName diff --git a/rules/antivirus/bitdefender_gz/av_policy_override.yml b/rules/antivirus/bitdefender_gz/av_policy_override.yml index 60f4161be..3182dfa6d 100644 --- a/rules/antivirus/bitdefender_gz/av_policy_override.yml +++ b/rules/antivirus/bitdefender_gz/av_policy_override.yml @@ -1,47 +1,30 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz +- antivirus-bitdefender-gz name: Bitdefender GravityZone Protection Module Disabled impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1562.001 - Impair Defenses: Disable or Modify Tools" +technique: 'T1562.001 - Impair Defenses: Disable or Modify Tools' adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1562/001/ -description: | - Detects a Bitdefender GravityZone protection module transitioning to disabled on an endpoint. The vendor reports this in two ways, both of which fire this rule: +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1562/001/ +description: 'A protection-module status event reports a disabled behavioral, power-user, data-protection or application-control module. + Verify the applied policy and expected licensing or maintenance state. The status does not identify who changed the configuration + or establish a malicious override. Real-time antimalware status has a separate rule. - - Product Modules Status (module=modules) with any of the protection flags set to 0. The doc-defined status keys are malware_status (Antimalware on-access), avc_status (Advanced Threat Control), pu_status (Anti-Phishing), dlp_status (Data Protection), app_control_status (Application Control), patch_management, and the exchange_* variants. A 0 means the module is currently off. - - Antitampering (Callback Evasion) fires when critical protection mechanisms have been disabled by a post-tampering incident. - - This does not catch every policy edit - adding scan exclusions or narrowing content filtering categories only reaches the Control Center audit log. It catches the highest-impact edits: turning a whole protection module off. - - Next Steps: - 1. Identify who was authorised to change the policy. Cross-check the modules-status timestamp against the Control Center audit log for policy pushes to this endpoint's group. - 2. Read which module was disabled: log.BitdefenderGZModule tells you the event, and the specific status key (log.malware_status, log.avc_status, etc.) tells you what turned off. Antimalware and ATC being off is a live-fire situation; the others are lower-severity but still deserve verification. - 3. If antitampering fired, treat the endpoint as actively targeted rather than mis-configured. detection_technique will be Callback Evasion or Vulnerable Drivers. - 4. Restore the previous policy configuration from the Control Center if the change was unauthorised. Do not rely on the local agent to accept a re-enable; verify with the next modules-status event. - 5. Review activity on the endpoint during the disabled window - anything that arrived while protection was off would not have been detected. - 6. Rotate the credentials of any admin account that could have pushed the change. -where: | - equals("log.BitdefenderGZModule", "antitampering") || - (equals("log.BitdefenderGZModule", "modules") && - (equals("log.malware_status", "0") || - equals("log.avc_status", "0") || - equals("log.pu_status", "0") || - equals("log.dlp_status", "0") || - equals("log.app_control_status", "0") || - equals("log.BitdefenderGZMalwareStatus", "0") || - equals("log.BitdefenderGZAvcStatus", "0") || - equals("log.BitdefenderGZPuStatus", "0") || - equals("log.BitdefenderGZDlpStatus", "0") || - equals("log.BitdefenderGZAppControlStatus", "0"))) + ' +where: equals("log.BitdefenderGZModule", "modules") && (oneOf("log.avcstatus",["0"]) || equals("log.BitdefenderGZAvcStatus","0") || + equals("log.pustatus","0") || equals("log.BitdefenderGZPuStatus","0") || equals("log.dlpstatus","0") || equals("log.BitdefenderGZDlpStatus","0") + || equals("log.appcontrolstatus","0") || equals("log.BitdefenderGZAppControlStatus","0")) groupBy: - - target.host - - lastEvent.log.BitdefenderGZModule +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- lastEvent.log.BitdefenderGZModule diff --git a/rules/antivirus/bitdefender_gz/bootkit_detection.yml b/rules/antivirus/bitdefender_gz/bootkit_detection.yml index e2bf1a82d..98e36dcfd 100644 --- a/rules/antivirus/bitdefender_gz/bootkit_detection.yml +++ b/rules/antivirus/bitdefender_gz/bootkit_detection.yml @@ -1,47 +1,33 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Bitdefender GravityZone Bootkit or UEFI Threat Detected +- antivirus-bitdefender-gz +name: Bitdefender GravityZone Boot-Related Malware Detection impact: confidentiality: 3 integrity: 3 availability: 3 category: Defense Evasion -technique: "T1542.001 - Pre-OS Boot: System Firmware" +technique: 'T1542.001 - Pre-OS Boot: System Firmware' adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1542/001/ -description: | - Detects a threat classified by Bitdefender as living at the boot layer: boot sector, UEFI/BIOS, or a kernel-mode rootkit. These persist across reinstalls and survive credential rotation, so a single hit is a full-endpoint incident. +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1542/001/ +description: 'An antimalware detection identifies a boot object, a boot/rootkit-related signature, or an artifact in a boot-related + path. The signature or path does not establish successful persistence or firmware compromise. Review log.BitdefenderGZMalwareType + (the infected-object category), target.malware, log.filePath and the vendor action. Use trusted forensic methods if the evidence supports + boot-level compromise. - Three complementary signals, from any of the antimalware engines (Antimalware, Advanced Threat Control, HyperDetect): - - malwareType classified as "boot", which is a vendor-defined value for boot-sector threats. - - Detection name in a bootkit family: Bootkit.*, Rootkit.*, Trojan.Boot.*, TDSS.*, Alureon.*, ZeroAccess.*, Necurs.*, Sinowal.*. - - The artefact sits in a boot region: \EFI\, /EFI/, \boot\, /boot/, or references the Master Boot Record. - - Next Steps: - 1. Isolate the endpoint immediately. Bootkits control the boot process; the running OS cannot be trusted to report accurately. - 2. Do not reboot into the local OS. If forensic capture is required, boot from external media. - 3. Determine what fired the detection: - - target.malware and log.BitdefenderGZDetectionName give the family name and align with public IOCs. - - target.path names the affected boot region. - - actionResult tells you whether Bitdefender removed the artefact. Boot region cleanup often fails from within the OS - actionResult "failed" is the expected outcome and does not mean the detection was wrong. - 4. Verify firmware integrity independently: dump BIOS/UEFI, compare against a known-good, check Secure Boot state. - 5. Reimage from trusted media and reflash firmware. Do not try to disinfect the running OS. - 6. Enable Secure Boot for all hosts with the same hardware baseline if it was not already on. Bootkit persistence relies on Secure Boot being off or being defeated. - 7. Hunt the estate for target.sha256 - any other endpoint reporting the same hash without firing this rule is a coverage gap, not proof of cleanliness. -where: | - oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - (equals("log.BitdefenderGZMalwareType", "boot") || - regexMatch("log.BitdefenderGZDetectionName", - "(?i)(bootkit|rootkit|uefi|tdss|alureon|zeroaccess|necurs|rustock|sinowal|trojan\\.boot|master.?boot)") || - regexMatch("target.malware", - "(?i)(bootkit|rootkit|uefi|tdss|alureon|zeroaccess|necurs|rustock|sinowal|trojan\\.boot|master.?boot)") || - regexMatch("target.path", - "(?i)(\\\\efi\\\\|/efi/|\\\\boot\\\\|/boot/|master\\.?boot\\.?record|mbr)")) + ' +where: "oneOf(\"log.BitdefenderGZModule\", [\"av\", \"avc\", \"hd\"]) &&\n(equals(\"log.BitdefenderGZMalwareType\", \"boot\") ||\n regexMatch(\"\ + log.BitdefenderGZDetectionName\",\n \"(?i)(bootkit|rootkit|uefi|tdss|alureon|zeroaccess|necurs|rustock|sinowal|trojan\\\\.boot|master.?boot)\"\ + ) ||\n regexMatch(\"target.malware\",\n \"(?i)(bootkit|rootkit|uefi|tdss|alureon|zeroaccess|necurs|rustock|sinowal|trojan\\\\.boot|master.?boot)\"\ + ) ||\n regexMatch(\"log.filePath\",\n \"(?i)(\\\\\\\\efi\\\\\\\\|/efi/|\\\\\\\\boot\\\\\\\\|/boot/|master\\\\.?boot\\\\.?record|mbr)\"\ + ))\n" groupBy: - - target.host - - target.malware +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- target.malware diff --git a/rules/antivirus/bitdefender_gz/crypto_mining_detection.yml b/rules/antivirus/bitdefender_gz/crypto_mining_detection.yml index 1f1d359f3..e1c88b5ec 100644 --- a/rules/antivirus/bitdefender_gz/crypto_mining_detection.yml +++ b/rules/antivirus/bitdefender_gz/crypto_mining_detection.yml @@ -1,41 +1,31 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Bitdefender GravityZone Crypto Mining Detected +- antivirus-bitdefender-gz +name: Bitdefender GravityZone Mining-Related Threat Detection impact: confidentiality: 2 integrity: 2 availability: 3 category: Impact -technique: "T1496 - Resource Hijacking" +technique: T1496 - Resource Hijacking adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1496/ -description: | - Detects cryptocurrency mining that Bitdefender itself classified as such, from any of three signals: +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1496/ +description: 'An antimalware signature names mining software, or a Content Control URL matches a mining-related indicator. Detection + or blocking does not establish that a miner ran. Check authorization, the exact hostname or URL (a substring can be coincidental), + vendor action, process activity and resource usage before attributing cryptojacking. - - An antimalware, ATC or HyperDetect detection whose family name calls it a coin miner: CoinMiner, CryptoMiner, XMRig, Monero, Cryptonight, JsCoinminer, Coinhive. - - A HyperDetect verdict with attack_type "grayware" and a name matching the miner families above. HyperDetect's grayware label is where cryptojacking often lands because miners are technically not classical malware. - - Content Control blocking a URL for a known mining infrastructure host (coinhive, xmrig pools, minepool, nicehash, monerohash). - - Cryptojacking is not just theft of CPU. A running miner is confirmation that arbitrary code executed on the endpoint, so the operator can equally choose to run anything else next. - - Next Steps: - 1. Identify the process: target.process and target.path if the antimalware engine caught it, or origin.url if Content Control blocked the pool. - 2. Kill the miner process only after you have grabbed its command line - many droppers restart the miner under a new name. - 3. Look for persistence: scheduled tasks, run keys, services, cron jobs and startup items on the endpoint. The miner did not install itself; something else placed it there. - 4. Check every host the same target.user touched. Cryptojacking is opportunistic and often distributed via user-mode delivery. - 5. Verify that no data was staged for exfiltration in the same session. A "just mining" verdict without checking is unsafe. - 6. Block the mining pool host at the perimeter and add its hash to blocklists across the estate before closing. -where: | - oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - (regexMatch("log.BitdefenderGZDetectionName", "(?i)(coin.?miner|crypto.?miner|xmrig|monero|cryptonight|coinhive|jscoinminer|bitcoin.?miner)") || - regexMatch("target.malware", "(?i)(coin.?miner|crypto.?miner|xmrig|monero|cryptonight|coinhive|jscoinminer|bitcoin.?miner)")) || - (equals("log.BitdefenderGZModule", "uc") && - regexMatch("origin.url", "(?i)(coinhive|xmrig|minepool|nicehash|monerohash|nanopool|supportxmr)")) + ' +where: "oneOf(\"log.BitdefenderGZModule\", [\"av\", \"avc\", \"hd\"]) &&\n(regexMatch(\"log.BitdefenderGZDetectionName\", \"(?i)(coin.?miner|crypto.?miner|xmrig|monero|cryptonight|coinhive|jscoinminer|bitcoin.?miner)\"\ + ) ||\n regexMatch(\"target.malware\", \"(?i)(coin.?miner|crypto.?miner|xmrig|monero|cryptonight|coinhive|jscoinminer|bitcoin.?miner)\"\ + )) ||\n(equals(\"log.BitdefenderGZModule\", \"uc\") &&\n regexMatch(\"origin.domain\", \"(?i)(coinhive|xmrig|minepool|nicehash|monerohash|nanopool|supportxmr)\"\ + ))\n" groupBy: - - target.host - - target.malware +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- target.malware diff --git a/rules/antivirus/bitdefender_gz/email_threat_spreading.yml b/rules/antivirus/bitdefender_gz/email_threat_spreading.yml index 870e5abfb..0be4af651 100644 --- a/rules/antivirus/bitdefender_gz/email_threat_spreading.yml +++ b/rules/antivirus/bitdefender_gz/email_threat_spreading.yml @@ -1,38 +1,30 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Bitdefender GravityZone Malware Detected on Exchange Server +- antivirus-bitdefender-gz +name: Bitdefender GravityZone Exchange Malware Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Initial Access -technique: "T1566.001 - Phishing: Spearphishing Attachment" +technique: 'T1566.001 - Phishing: Spearphishing Attachment' adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1566/001/ -description: | - Detects malware found by Bitdefender on an Exchange server. A mail server detection is more urgent than the same malware on a workstation for two reasons: the message may already have been delivered to other recipients, and a mail server is a high-value target whose compromise exposes the organisation's correspondence. +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1566/001/ +description: 'The Exchange antimalware module reports a detection. Determine whether the affected object was quarantined, removed, blocked + or delivered using the original vendor details and mail-system records. A matching endpoint hash is a correlation lead, not proof + that a specific user opened a message. Investigate affected messages and recipients before choosing containment. - Next Steps: - 1. Determine the blast radius before cleaning anything: - - Identify the message and every recipient it reached, not just the one that triggered the detection - - A single detection on the server usually means the same attachment sits in several mailboxes - 2. Check whether the message was delivered or quarantined at the gateway - actionResult tells you whether Bitdefender stopped it - 3. Pull the message back from every mailbox it reached before notifying users, so nobody opens it while you are still working - 4. Identify who already opened it: - - Correlate endpoint AntiMalware detections for the same target.sha256 across the estate - - Any endpoint that reported the same hash is a confirmed opener and needs its own investigation - 5. Look at the sender and the campaign: - - A spoofed internal sender means someone's mailbox is already compromised - - Check whether other messages from the same source reached the organisation - 6. Review the Exchange server itself - a detection on the server is a reason to verify the server's own integrity, not only the message - 7. Feed the sender, subject and hash into mail gateway blocking before closing -where: | - equals("log.BitdefenderGZModule", "exchange-malware") + ' +where: 'equals("log.BitdefenderGZModule", "exchange-malware") + + ' groupBy: - - target.host - - target.malware +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- target.malware diff --git a/rules/antivirus/bitdefender_gz/fileless_malware_detection.yml b/rules/antivirus/bitdefender_gz/fileless_malware_detection.yml index df466761e..a0edce26f 100644 --- a/rules/antivirus/bitdefender_gz/fileless_malware_detection.yml +++ b/rules/antivirus/bitdefender_gz/fileless_malware_detection.yml @@ -1,46 +1,30 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Bitdefender GravityZone Fileless Attack Detected +- antivirus-bitdefender-gz +name: Bitdefender GravityZone Potential Fileless Attack impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1055 - Process Injection" +technique: T1055 - Process Injection adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1055/ - - https://www.bitdefender.com/en-us/business/gravityzone-platform/fileless-attack-defense -description: | - Detects fileless attacks that Bitdefender identified as such, from any of three signals: +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1055/ +- https://www.bitdefender.com/en-us/business/gravityzone-platform/fileless-attack-defense +description: 'A HyperDetect event explicitly marks a fileless attack, or a behavioral detection includes a command commonly used in + fileless execution. A suspicious-file or exploit label alone is insufficient. Review the full vendor command line and process ancestry; + a command pattern is an investigation lead, not proof of successful fileless execution. - - HyperDetect (hd) with is_fileless_attack = true. This is a vendor-authoritative label. HyperDetect specifically inspects in-memory behaviour and the boolean is the flag it sets when memory-resident execution was observed. - - HyperDetect (hd) with attack_type in {exploits, targeted attack}. These categories cover memory injection, ROP chains and reflective loading even without the fileless boolean. - - Advanced Threat Control (avc) firing on a process whose command line invokes a living-off-the-land binary: powershell -enc, mshta, wscript/cscript, regsvr32, rundll32, Invoke- / IEX / DownloadString cmdlets. avc is behavioural, so a match on the extracted command line means the LOLBin actually ran. - - A fileless verdict is materially harder to remediate than a file-based one because there is nothing on disk to quarantine. The endpoint has to be treated as though the payload might still be resident until proven otherwise. - - Next Steps: - 1. Capture a memory image before rebooting or reconnecting. On reboot the residency is lost, and with it most of the forensic value. - 2. Read the vendor's classification: - - log.BitdefenderGZAttackType tells you how HyperDetect classified the payload. - - log.BitdefenderGZProcessCommandLine and log.BitdefenderGZProcessInfoPath show what actually ran. - - target.process is the caught process; log.BitdefenderGZParentProcessPath is where it came from. - 3. Look upstream from the parent process. Fileless payloads are almost always the second stage - the initial delivery is either a phishing document, an exploit, or credential-based access. - 4. Isolate the endpoint if the detection came from avc, since a behavioural verdict means the code actually executed. - 5. Search other endpoints for the same parent process ancestry, not the same hash. Fileless payloads reuse infrastructure and technique, not artefacts. - 6. Do not close on a clean scan alone. A signature-less on-disk scan does not disprove a memory-only threat. Verify with the memory image and confirm no anomalous processes remain on reboot. -where: | - (equals("log.BitdefenderGZModule", "hd") && - (equals("log.is_fileless_attack", "true") || - equals("log.BitdefenderGZIsFilelessAttack", "true") || - regexMatch("log.BitdefenderGZAttackType", "(?i)(exploits|targeted attack|suspicious files)"))) || - (equals("log.BitdefenderGZModule", "avc") && - regexMatch("log.BitdefenderGZProcessCommandLine", "(?i)(powershell.*-e(nc|ncodedcommand)|mshta\\s|wscript\\s|cscript\\s|regsvr32\\s|rundll32\\s|invoke-|iex\\s|downloadstring|frombase64string)")) + ' +where: (equals("log.BitdefenderGZModule","hd") && (equals("log.isfilelessattack","true") || equals("log.BitdefenderGZIsFilelessAttack","true"))) + || (equals("log.BitdefenderGZModule","avc") && regexMatch("log.BitdefenderGZProcessCommandLine", "(?i)(powershell.*-e(nc|ncodedcommand)|mshta\\s|wscript\\s|cscript\\s|regsvr32\\s|rundll32\\s|invoke-|iex\\s|downloadstring|frombase64string)")) groupBy: - - target.host - - target.process +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- target.process diff --git a/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml b/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml index 524628e6b..e7d238902 100644 --- a/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml +++ b/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml @@ -1,44 +1,33 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz +- antivirus-bitdefender-gz name: Bitdefender GravityZone High Severity Malware Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Execution -technique: "T1204.002 - User Execution: Malicious File" +technique: 'T1204.002 - User Execution: Malicious File' adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1204/002/ -description: | - Detects a high-severity malware finding from any of Bitdefender's three antimalware engines: Antimalware (signature and on-access), Advanced Threat Control (behavioural) and HyperDetect (machine learning tuned for advanced threats). +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1204/002/ +description: 'GravityZone reports an antimalware, behavioral or HyperDetect event with CEF severity at least 8. This is the vendor event + priority, not proof of compromise. actionResult=denied means the recorded action explicitly blocked or quarantined; success is qualified + remediation, and failure means the vendor reports the threat still present. An absent result is unknown. Review target.malware, hashes, + log.filePath and log.BitdefenderGZMalwareType; the latter describes the infected-object category, not the SDK malware type. - This is the baseline malware alert for the data source. Whether the threat was actually neutralised is a separate question, answered by actionResult: a value of "failed" means it is still on the endpoint, and that case has its own rule with a higher impact. + ' +where: 'oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - Next Steps: - 1. Read the outcome before deciding how urgent this is: - - actionResult "success" with action blocked, deleted, quarantined or disinfected means the control worked; treat this as an exposure and hygiene finding - - actionResult "failed" means the threat is live and the endpoint should be isolated immediately - 2. Identify what was found: - - target.malware and target.malwareType give the threat name and whether it is a file or a process - - target.sha256 and target.md5 let you pivot to threat intelligence and across the estate - - target.path is the artefact, target.process is the process that touched it - 3. Establish how it arrived: - - origin.url is populated when the threat came in over the web - - target.user identifies who was logged on, which usually explains the delivery path - - Correlate with Antiphishing events on the same target.host around deviceTime - 4. Look for related activity on the same host in the surrounding hours - a single detection is often one step of a longer chain - 5. Hunt the estate for the same target.sha256 to find endpoints where nothing was reported - 6. Follow up on the endpoint: - - Run a full scan and confirm it comes back clean - - Check that signatures were current at deviceTime, using log.BitdefenderGZSignaturesNumber -where: | - oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) + greaterOrEqual("log.cefSeverity", 8) + + ' groupBy: - - target.host - - target.malware +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- target.malware diff --git a/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml b/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml index e26dcc8ce..33468260f 100644 --- a/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml +++ b/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml @@ -1,50 +1,51 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Bitdefender GravityZone Malware Outbreak Across Endpoints +- antivirus-bitdefender-gz +name: Bitdefender GravityZone High Severity Malware on Multiple Endpoints impact: confidentiality: 3 integrity: 3 availability: 3 category: Lateral Movement -technique: "T1080 - Taint Shared Content" +technique: T1080 - Taint Shared Content adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1080/ -description: | - Detects the same malware appearing on more than one endpoint within a day, which is the signature of something spreading rather than an isolated infection: a shared network location, a mass phishing campaign, tainted removable media, or genuine lateral movement. +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1080/ +description: 'A high-severity antimalware detection has at least two other high-severity records of the same malware name on endpoints + other than the current one within 24 hours, scoped to the same collector and GravityZone company. This establishes activity on multiple + endpoints, not a distinct-host count of three. Review remediation and common delivery paths. - The correlation deliberately excludes the originating host, so repeated detections of one threat on a single machine do not look like an outbreak - that case belongs to multiple_malware_from_single_source. - - Next Steps: - 1. List every target.host reporting this target.malware and treat it as one incident, not several - 2. Identify the common vector by comparing the affected endpoints: - - A shared target.path, especially a UNC or mapped drive, points at a tainted network share that must be taken offline - - The same target.user across hosts points at a compromised account - - origin.url in common points at a web or phishing campaign - - Different departments with no shared resource suggests removable media or lateral movement - 3. Check the remediation state on each host: any event with actionResult "failed" is a live foothold and gets priority - 4. Contain the vector before cleaning the endpoints. Cleaning machines while the source stays reachable just resets the clock - 5. Hunt for the target.sha256 across the whole estate, including hosts that reported nothing - absence of a detection is not absence of the file - 6. Reassess coverage: if some endpoints detected it and others did not, check agent health and signature currency on the silent ones - 7. Keep the incident open until a full day passes with no new host reporting the same malware -where: | - oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) && - exists("target.malware") + ' +where: equals("log.correlationCandidate.malware_outbreak_multiple_hosts","match") correlation: - - indexPattern: v11-log-antivirus-bitdefender-gz-* - within: 24h - count: 2 - with: - - field: target.malware - operator: filter_term - value: "{{.target.malware}}" - - field: target.host - operator: must_not_term - value: "{{.target.host}}" +- indexPattern: v11-log-antivirus-bitdefender-gz-* + within: 24h + count: 2 + with: + - field: log.correlationCandidate.malware_outbreak_multiple_hosts + operator: filter_term + value: match + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.BitdefenderGZCompanyId + operator: filter_term + value: '{{.log.BitdefenderGZCompanyId}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: must_not_term + value: '{{.log.endpointKey}}' + - field: target.malware + operator: filter_term + value: '{{.target.malware}}' groupBy: - - target.malware +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- target.malware diff --git a/rules/antivirus/bitdefender_gz/memory_threat_detection.yml b/rules/antivirus/bitdefender_gz/memory_threat_detection.yml index efabbe4c3..8e7245f8b 100644 --- a/rules/antivirus/bitdefender_gz/memory_threat_detection.yml +++ b/rules/antivirus/bitdefender_gz/memory_threat_detection.yml @@ -1,44 +1,30 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Bitdefender GravityZone Memory Exploit Detected +- antivirus-bitdefender-gz +name: Bitdefender GravityZone Memory or Exploit-Related Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1055 - Process Injection" +technique: T1055 - Process Injection adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1055/ -description: | - Detects an in-memory exploit or memory-manipulation threat caught by Bitdefender, from two independent signals: +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1055/ +description: 'GravityZone reports an Anti-Exploit event, a HyperDetect exploit classification, or antimalware against a process object. + These signals do not prove successful exploitation or the absence of an on-disk artifact. Review the vendor exploit classification, + process command and parent information, action and endpoint evidence before containment. - - Advanced Anti-Exploit (antiexploit) module firing on any exploit technique. The vendor's per-event fields (detection_exploitTechnique, detection_pid, detection_path, detection_cve) name the vulnerability class targeted. - - HyperDetect (hd) with attack_type "exploits". HyperDetect's ML flags in-memory exploit chains that pass the on-disk scanners. - - Memory exploitation implies that the attacker got code execution in an existing process. This is more severe than a file-based detection because there is no artefact on disk to quarantine and the caught process may have already touched other systems. - - Next Steps: - 1. Capture the process memory before reboot. In-memory forensic value is time-critical. - 2. Read the vendor's classification: - - log.BitdefenderGZExploitType or the antiexploit detection_exploitTechnique names the class of exploit (ROP, process creation, obsolete child process, etc.). - - log.BitdefenderGZDetectionCve names the vulnerability if known. - - target.process and log.BitdefenderGZDetectionPath show the affected process. - - log.BitdefenderGZParentPid and log.BitdefenderGZParentPath show the delivery process. - 3. Patch or isolate the vulnerable software identified by the CVE across the estate. Other endpoints running the same version are exposed even if they have not fired the rule yet. - 4. Look for post-exploitation on the same target.host: process launches with anomalous ancestry, network connections from unusual processes, credential access artefacts. - 5. If actionResult is "failed" or the vendor detection_action was "reportOnly", treat the exploit as successful and hunt for the payload it delivered. - 6. Correlate with New Incident events on the same target.host - the antiexploit hit is often one node in a larger incident graph. -where: | - equals("log.BitdefenderGZModule", "antiexploit") || - (equals("log.BitdefenderGZModule", "hd") && - regexMatch("log.BitdefenderGZAttackType", "(?i)exploits")) || - (equals("log.BitdefenderGZMalwareType", "process") && - oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"])) + ' +where: "equals(\"log.BitdefenderGZModule\", \"antiexploit\") ||\n(equals(\"log.BitdefenderGZModule\", \"hd\") &&\n regexMatch(\"log.BitdefenderGZAttackType\"\ + , \"(?i)exploits\")) ||\n(equals(\"log.BitdefenderGZMalwareType\", \"process\") &&\n oneOf(\"log.BitdefenderGZModule\", [\"av\", \"\ + avc\", \"hd\"]))\n" groupBy: - - target.host - - target.process +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- target.process diff --git a/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml b/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml index 4bb04a2a7..0bda04ad0 100644 --- a/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml +++ b/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml @@ -1,48 +1,47 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz +- antivirus-bitdefender-gz name: Bitdefender GravityZone Repeated Malware Detections on One Endpoint impact: confidentiality: 3 integrity: 3 availability: 2 category: Execution -technique: "T1204.002 - User Execution: Malicious File" +technique: 'T1204.002 - User Execution: Malicious File' adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1204/002/ -description: | - Detects an endpoint accumulating malware detections quickly: three or more antimalware findings on the same host within an hour. One detection is routine, several in an hour is not - it usually means an active infection dropping payloads, a user repeatedly running the same malicious file, or an already-compromised machine being used as a staging point. +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1204/002/ +description: 'At least three high-severity antimalware or behavioral detection records occurred on the same managed endpoint within + one hour. Only matching detection classes count; task, inventory and unrelated endpoint events are excluded. Repeated records need + not represent different malware families. Review vendor actions and remediation. - This looks at the endpoint as the unit of compromise, unlike the outbreak rule which follows one malware name across hosts. - - Next Steps: - 1. Treat the endpoint as compromised rather than triaging each detection separately - 2. Build the picture across the grouped events: - - Collect every target.malware seen on this target.host in the window - several distinct families point at a dropper or a downloader rather than one bad file - - Check target.path and target.process for a common parent, which is usually the delivery mechanism - - Look at target.user to see whether one account is behind all of them - 3. Check whether any of them failed to remediate: an actionResult of "failed" on any event in the group means something is still live, and that changes the urgency - 4. Isolate the endpoint if detections are still arriving or if any remain unremediated - 5. Find the entry point: - - origin.url on related events shows web delivery - - Correlate with Antiphishing events for the same host and user - - Check whether removable media was involved via Device Control events - 6. Do not close on a clean scan alone. Confirm no new detections for a full day, and review persistence - scheduled tasks, run keys, services - 7. Reimage if the same host keeps reappearing in this rule across days -where: | - oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) + ' +where: equals("log.correlationCandidate.multiple_malware_from_single_source","match") correlation: - - indexPattern: v11-log-antivirus-bitdefender-gz-* - within: 1h - count: 3 - with: - - field: target.host - operator: filter_term - value: "{{.target.host}}" +- indexPattern: v11-log-antivirus-bitdefender-gz-* + within: 1h + count: 3 + with: + - field: log.correlationCandidate.multiple_malware_from_single_source + operator: filter_term + value: match + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.BitdefenderGZCompanyId + operator: filter_term + value: '{{.log.BitdefenderGZCompanyId}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: filter_term + value: '{{.log.endpointKey}}' groupBy: - - target.host +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey diff --git a/rules/antivirus/bitdefender_gz/network_threat_detection.yml b/rules/antivirus/bitdefender_gz/network_threat_detection.yml index 7153b51ff..d0201e877 100644 --- a/rules/antivirus/bitdefender_gz/network_threat_detection.yml +++ b/rules/antivirus/bitdefender_gz/network_threat_detection.yml @@ -1,51 +1,51 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz +- antivirus-bitdefender-gz name: Bitdefender GravityZone Network Attack Blocked impact: confidentiality: 3 integrity: 2 availability: 2 category: Command and Control -technique: "T1071 - Application Layer Protocol" +technique: T1071 - Application Layer Protocol adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1071/ -description: | - Detects network-layer attacks that Bitdefender blocked at the endpoint, from either Network Attack Defense (exploit attempts, brute force, credential-stealing traffic) or the endpoint Firewall (port scans and blocked connections). +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1071/ +description: 'At least five high-severity firewall or Network Attack Defense records explicitly report blocking from the same source + address to the same managed endpoint within two hours. Unrelated activity and report-only events do not count. This is blocked attack + activity, not a successful connection or confirmed compromise. Investigate repeated targeting and the protection outcome. - The rule fires on a single high-severity event, and the correlation step raises it when the same source keeps coming back: five or more events from the same origin.ip within two hours is a persistent adversary rather than a stray packet. - - origin.ip is the attacker and target.ip is the endpoint being attacked. target.port tells you what was targeted, which is often the fastest way to read intent - 88 is Kerberos, 445 is SMB, 3389 is RDP. - - Next Steps: - 1. Read the two sides: origin.ip is the source, target.host and target.ip are what it reached - 2. Decide whether the source is internal or external: - - An external origin.ip means perimeter exposure - check why that traffic reached the endpoint at all - - An internal origin.ip means an already-compromised host is attacking laterally, which is more urgent - 3. Read the intent from the detection: - - log.BitdefenderGZDetectionName names the attack, for example Attack.Bruteforce.KERBEROS - - log.BitdefenderGZDetectionAttackTechnique gives the MITRE-aligned phase, such as credentialAccess - - target.port identifies the targeted service - 4. For credential access, assume the credentials are at risk: - - Force a reset on the targeted accounts and review authentication logs from origin.ip for any success - - If the target is a domain controller, treat it as a domain-wide incident - 5. Check whether the same origin.ip reached other endpoints - the correlation step groups by source for exactly this reason - 6. Block the source at the perimeter, and only then close the alert. A blocked attempt means this attack failed, not that the attacker stopped -where: | - oneOf("log.BitdefenderGZModule", ["network-monitor", "fw"]) && - (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) + ' +where: equals("log.correlationCandidate.network_threat_detection","match") correlation: - - indexPattern: v11-log-antivirus-bitdefender-gz-* - within: 2h - count: 5 - with: - - field: origin.ip - operator: filter_term - value: "{{.origin.ip}}" +- indexPattern: v11-log-antivirus-bitdefender-gz-* + within: 2h + count: 5 + with: + - field: log.correlationCandidate.network_threat_detection + operator: filter_term + value: match + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.BitdefenderGZCompanyId + operator: filter_term + value: '{{.log.BitdefenderGZCompanyId}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: filter_term + value: '{{.log.endpointKey}}' + - field: origin.ip + operator: filter_term + value: '{{.origin.ip}}' groupBy: - - target.host - - adversary.ip +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- adversary.ip diff --git a/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml b/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml index 3fa3b1d69..0ea6dfc83 100644 --- a/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml +++ b/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml @@ -1,43 +1,35 @@ -# Rule version v1.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Bitdefender GravityZone Phishing Page Loaded Without Blocking +- antivirus-bitdefender-gz +name: Bitdefender GravityZone Phishing Detection in Report-Only Mode impact: confidentiality: 3 integrity: 1 availability: 0 category: Credential Access -technique: "T1566.002 - Phishing: Spearphishing Link" +technique: 'T1566.002 - Phishing: Spearphishing Link' adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1566/002/ -description: | - Detects a user actually loading a page Bitdefender classifies as phishing, fraud or untrusted, because Web Protection was in report-only mode rather than blocking. The URL is in origin.url, the user in target.user and the endpoint in target.host. +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1566/002/ +description: 'The antiphishing module reported a phishing, fraud or untrusted URL in report-only mode. This event does not establish + that a page loaded, that a user submitted credentials, or that an account was compromised. Blocked detections do not satisfy this + rule. - This deliberately excludes blocked phishing. When Bitdefender blocks the page the user never sees it, there is no credential exposure, and an alert would only compete with real incidents. Blocked attempts are worth a periodic awareness report, not an alert. - A page that loaded is different: the user reached a credential-harvesting form and may have filled it in. Treat it as a possible credential compromise, not as a web-filtering event. + Review origin.domain, origin.url when complete, and the original log.request value, target.user and the managed endpoint. Confirm + the protection policy and investigate subsequent browser, authentication and malware activity. Escalate credential containment when + that investigation supports exposure. - Next Steps: - 1. Treat the account in target.user as potentially compromised and reset the credentials, before investigating anything else - 2. Read the URL in origin.url: - - A brand-impersonating domain, especially a bank or the organisation's own name, indicates a targeted campaign rather than random adware - - log.BitdefenderGZEventType separates phishing from fraud and untrust, which carry different intent - 3. Establish how the user got there: - - target.process shows the browser or client that made the request - - A link in email means the message got past the mail gateway, which is its own finding - 4. Check whether other users hit the same origin.url - a shared URL across users is a campaign against the organisation - 5. Look for what followed on the same target.host: - - AntiMalware detections shortly after suggest the page also served a payload - - Network Attack Defense events with credentialAccess suggest the credentials were used - 6. Fix the policy that allowed it: find why Web Protection was in report-only mode for this endpoint or group, since every other user under that policy is equally exposed - 7. Submit the URL for blocking at the perimeter so the rest of the estate is covered -where: | - equals("log.BitdefenderGZModule", "aph") && - equals("action", "reportOnly") + ' +where: equals("log.BitdefenderGZModule", "aph") && equalsIgnoreCase("action", "reportOnly") groupBy: - - target.user - - adversary.url +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- target.user +- adversary.url +- adversary.domain diff --git a/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml b/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml index f5cb7762d..39accfbab 100644 --- a/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml +++ b/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml @@ -1,52 +1,31 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz +- antivirus-bitdefender-gz name: Bitdefender GravityZone Malware Not Remediated impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1562.001 - Impair Defenses: Disable or Modify Tools" +technique: 'T1562.001 - Impair Defenses: Disable or Modify Tools' adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1562/001/ -description: | - Detects malware that Bitdefender identified but did not remediate: the file or process is still present on the endpoint after the detection. The endpoint should be treated as compromised, not as protected. +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1562/001/ +description: 'An AntiMalware event reports failure to remediate (still present), or reports a positive present-malware counter while + quarantine and cleaned counters are zero. This establishes a remediation concern, not that a process executed or the endpoint is compromised. + Ignored and report-only actions alone are not mapped to failure. Review the complete counters and vendor final action, object type, + file/process and endpoint state; prioritize containment if active compromise is confirmed or strongly supported. - Two equivalent signals, either of which fires the rule: - - actionResult "failed", derived by the filter from the vendor's final_status values "still present" and "ignored". - - The malware counters saying the same thing structurally: at least one threat still present, none quarantined and none cleaned. - - This is materially different from a normal detection. A blocked, deleted, quarantined or disinfected threat is a control that worked. A threat still present means the control detected and then failed to act, which usually points at insufficient agent privileges, a locked file, an active process defending itself, or quarantine storage problems. - - Next Steps: - 1. Isolate the endpoint from the network before anything else - the threat is live, not contained - 2. Identify what is still running: - - target.malware and target.malwareType name the threat and whether it is a file or a process - - target.path and target.process point at the artefact and the process involved - - target.sha256 lets you pivot across the estate and to threat intelligence - 3. Establish why remediation failed: - - A process-type detection that is still present usually means the process resisted termination - - Check whether the agent has the privileges it needs, and whether the path is on a locked or network volume - - Review quarantine storage capacity in the GravityZone console - 4. Remediate manually and verify: - - Terminate the process, then remove the artefact and confirm a follow-up scan reports it gone - - Do not close the alert on the detection alone: confirm the counters changed - 5. Assess the exposure window - the endpoint ran with known live malware from deviceTime until remediation, so review what else it reported in that period - 6. Hunt the same threat elsewhere using target.sha256 and target.malware, since a failure to remediate on one host says nothing about the others - 7. If remediation keeps failing on the same endpoint, treat it as tampering rather than a product fault and consider reimaging -where: | - equals("log.eventType", "AntiMalware") && - ( - oneOf("actionResult", ["failure", "failed"]) || - (greaterOrEqual("log.BitdefenderGZPresentMalwareCnt", 1) && - equals("log.BitdefenderGZQuarantinedMalwareCnt", 0) && - equals("log.BitdefenderGZCleanedMalwareCnt", 0)) - ) + ' +where: "equals(\"log.eventType\", \"AntiMalware\") &&\n(\n oneOf(\"actionResult\", [\"failure\", \"failed\"]) ||\n (greaterOrEqual(\"\ + log.BitdefenderGZPresentMalwareCnt\", 1) &&\n equals(\"log.BitdefenderGZQuarantinedMalwareCnt\", 0) &&\n equals(\"log.BitdefenderGZCleanedMalwareCnt\"\ + , 0))\n)\n" groupBy: - - target.host - - target.malware +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- target.malware diff --git a/rules/antivirus/bitdefender_gz/ransomware_behavior_detection.yml b/rules/antivirus/bitdefender_gz/ransomware_behavior_detection.yml index f37fbee5a..0e671000c 100644 --- a/rules/antivirus/bitdefender_gz/ransomware_behavior_detection.yml +++ b/rules/antivirus/bitdefender_gz/ransomware_behavior_detection.yml @@ -1,46 +1,37 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz +- antivirus-bitdefender-gz name: Bitdefender GravityZone Ransomware Detected impact: confidentiality: 3 integrity: 3 availability: 3 category: Impact -technique: "T1486 - Data Encrypted for Impact" +technique: T1486 - Data Encrypted for Impact adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1486/ -description: | - Detects ransomware that Bitdefender classified as such, from any of three independent signals: - - An incident whose attack types include Ransomware, which is GravityZone's own classification of the attack chain. - - The Ransomware Mitigation module reporting activity, which is the vendor's dedicated anti-ransomware component. - - A threat or detection name containing Ransom, which covers signature-based identification. +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1486/ +description: 'The vendor attack classification, ransomware-mitigation module or detection name identifies ransomware-related activity. + The finding may describe an attempt that was blocked or remediated; it does not establish that files were encrypted. Promptly review + the vendor action, affected files, processes and accessible shares, and contain supported active activity according to the incident + response plan. - There is no severity floor and no correlation threshold on purpose. These events arrive at CEF severity 3 because GravityZone assigns severity by event type, not by how dangerous the threat is, and a single confirmed ransomware classification already justifies the highest-impact response. + ' +where: 'regexMatch("log.BitdefenderGZAttackTypes", "(?i)Ransomware") || - Next Steps: - 1. Isolate the endpoint immediately - before triage, before collecting anything. Every minute of network access is more files encrypted, potentially on shares - 2. Determine whether encryption already started: - - Check the file server and any shares the target.user could reach for recently modified files with unusual extensions - - log.BitdefenderGZAttackTypes and log.BitdefenderGZAttCkId show what stage GravityZone attributed - - actionResult tells you whether Bitdefender stopped it or only observed it - 3. Identify the entry point, because ransomware is the last stage of an intrusion, not the first: - - origin.url and target.process show how it arrived and what executed it - - target.user is the account whose access the malware inherited - - Look for credential access or lateral movement on this host in the preceding days - 4. Check whether Ransomware Mitigation restored anything - the module keeps copies of files it saw being encrypted - 5. Assume the whole estate is in scope until proven otherwise: hunt target.sha256 and check every host the same account touched - 6. Preserve evidence before reimaging - the ransom note, the sample and the memory image determine the family and whether a decryptor exists - 7. Escalate to whoever owns incident response and legal notification. Do not close this alert on a clean scan -where: | - regexMatch("log.BitdefenderGZAttackTypes", "(?i)Ransomware") || equals("log.BitdefenderGZModule", "ransomware-mitigation") || + contains("target.malware", "Ransom") || + contains("log.BitdefenderGZDetectionName", "Ransom") + + ' groupBy: - - target.host - - lastEvent.log.BitdefenderGZIncidentId +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- lastEvent.log.BitdefenderGZIncidentId diff --git a/rules/antivirus/bitdefender_gz/realtime_protection_disabled.yml b/rules/antivirus/bitdefender_gz/realtime_protection_disabled.yml index 3fef53f59..7e695308b 100644 --- a/rules/antivirus/bitdefender_gz/realtime_protection_disabled.yml +++ b/rules/antivirus/bitdefender_gz/realtime_protection_disabled.yml @@ -1,33 +1,28 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz +- antivirus-bitdefender-gz name: Bitdefender GravityZone Real-Time Antimalware Disabled impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1562.001 - Impair Defenses: Disable or Modify Tools" +technique: 'T1562.001 - Impair Defenses: Disable or Modify Tools' adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1562/001/ -description: | - Detects real-time (on-access) Antimalware protection being disabled on a Bitdefender GravityZone endpoint, from the vendor's own Product Modules Status event: malware_status = 0 means the on-access scanner is currently off. +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1562/001/ +description: 'A Product Modules Status event reports real-time antimalware disabled. Confirm the applied policy, licensing, maintenance + state and actual endpoint protection status. The event does not identify the person responsible or establish tampering. Review related + protection events and restore expected coverage through the approved management process. - This is a narrow, high-fidelity signal. On-access scanning is Bitdefender's core defence; a machine running with it off cannot detect malware at execution time, only at scheduled scans. - - Next Steps: - 1. Correlate the deviceTime against the Control Center audit log for this endpoint's policy group. A legitimate change comes from a policy push; an unattributed change comes from local tampering. - 2. Look for any threats that arrived during the disabled window: nothing between the disable event and the next enable event will have been caught by real-time scanning. Trigger a full on-demand scan through the console before trusting the endpoint again. - 3. Check for concurrent antitampering events on the same target.host. A modules-status change plus an antitampering (Callback Evasion) event within minutes is the shape of an attacker with local admin trying to silence the agent. - 4. Restore the module through GravityZone Control Center. Do not rely on the local agent to accept the re-enable; verify with the next modules-status event. - 5. If the on-access module keeps going off despite a re-enable, treat the endpoint as tampered and consider reimaging. -where: | - equals("log.BitdefenderGZModule", "modules") && - (equals("log.malware_status", "0") || - equals("log.BitdefenderGZMalwareStatus", "0")) + ' +where: "equals(\"log.BitdefenderGZModule\", \"modules\") &&\n(equals(\"log.malwarestatus\", \"0\") ||\n equals(\"log.BitdefenderGZMalwareStatus\"\ + , \"0\"))\n" groupBy: - - target.host +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey diff --git a/rules/antivirus/bitdefender_gz/rootkit_detection.yml b/rules/antivirus/bitdefender_gz/rootkit_detection.yml index b02323b95..26de9cae0 100644 --- a/rules/antivirus/bitdefender_gz/rootkit_detection.yml +++ b/rules/antivirus/bitdefender_gz/rootkit_detection.yml @@ -1,39 +1,30 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz +- antivirus-bitdefender-gz name: Bitdefender GravityZone Rootkit Detected impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1014 - Rootkit" +technique: T1014 - Rootkit adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1014/ -description: | - Detects a rootkit or kernel-level threat that Bitdefender identified by name. Signal is a match on the vendor's own detection classification (log.BitdefenderGZDetectionName or target.malware) against known rootkit families - Rootkit, Trojan.Rootkit, TDSS, ZeroAccess, Necurs, Alureon, Rustock, Sinowal - from any antimalware engine (Antimalware, ATC, HyperDetect). +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1014/ +description: 'An antimalware detection name matches a rootkit-related family or indicator. Review the exact vendor classification, affected + object and remediation outcome. A signature match does not prove that the rootkit installed or that all endpoint evidence is untrustworthy. + Use the incident response process and trusted forensic collection when the evidence supports compromise. - A rootkit detection means the vendor believes something is hiding at or below the OS layer. The running OS on the endpoint cannot be trusted to report accurately after a rootkit verdict, so investigation from within the endpoint is unreliable. - - Next Steps: - 1. Isolate the endpoint before doing anything else. Rootkits control what other tools see; every second of network access is another opportunity for lateral movement or exfiltration from a position where forensic tools lie about the state. - 2. Do not investigate from the running OS. Boot from external media for imaging and forensic capture. - 3. Read the vendor's classification: - - log.BitdefenderGZDetectionName or target.malware names the family and aligns with public IOCs. - - log.BitdefenderGZMalwareType tells you whether it caught a file, a process (still resident), or a boot artefact. - - actionResult tells you whether Bitdefender removed it; rootkit removal from within the OS often fails and that is not the vendor's fault. - 4. Check for privilege escalation and driver installation events on the same target.host in the preceding hours - rootkits normally arrive through an existing privilege escalation. - 5. Reimage from trusted media. Do not try to disinfect a running rootkit-infected OS. - 6. Hunt the estate for target.sha256, the family name, and any driver names identified during forensics. A rootkit is a targeted delivery; the same delivery vector likely touched other endpoints. - 7. Rotate credentials that could have been captured while the rootkit was resident. Every credential the affected endpoint touched is potentially compromised. -where: | - oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - (regexMatch("log.BitdefenderGZDetectionName", "(?i)(rootkit|tdss|zeroaccess|necurs|alureon|rustock|sinowal|trojan\\.rootkit)") || - regexMatch("target.malware", "(?i)(rootkit|tdss|zeroaccess|necurs|alureon|rustock|sinowal|trojan\\.rootkit)")) + ' +where: "oneOf(\"log.BitdefenderGZModule\", [\"av\", \"avc\", \"hd\"]) &&\n(regexMatch(\"log.BitdefenderGZDetectionName\", \"(?i)(rootkit|tdss|zeroaccess|necurs|alureon|rustock|sinowal|trojan\\\ + \\.rootkit)\") ||\n regexMatch(\"target.malware\", \"(?i)(rootkit|tdss|zeroaccess|necurs|alureon|rustock|sinowal|trojan\\\\.rootkit)\"\ + ))\n" groupBy: - - target.host - - target.malware +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- target.malware diff --git a/rules/antivirus/bitdefender_gz/suspicious_exclusions_added.yml b/rules/antivirus/bitdefender_gz/suspicious_exclusions_added.yml index b2bd7706e..07eaf9f69 100644 --- a/rules/antivirus/bitdefender_gz/suspicious_exclusions_added.yml +++ b/rules/antivirus/bitdefender_gz/suspicious_exclusions_added.yml @@ -1,35 +1,27 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Bitdefender GravityZone Exclusion-Related Task Executed +- antivirus-bitdefender-gz +name: Bitdefender GravityZone Exclusion-Related Task Succeeded impact: - confidentiality: 3 - integrity: 3 - availability: 1 + confidentiality: 1 + integrity: 1 + availability: 0 category: Defense Evasion -technique: "T1562.001 - Impair Defenses: Disable or Modify Tools" +technique: 'T1562.001 - Impair Defenses: Disable or Modify Tools' adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1562/001/ -description: | - Detects a Bitdefender GravityZone task whose name or type identifies it as an exclusion-related administrative action. Attackers with admin console access add scan exclusions to blind the antivirus to their tools before running them. +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1562/001/ +description: 'GravityZone reports success for a task whose vendor-provided name mentions an exclusion or allowlist. Task names are labels, + not a configuration diff; confirm what changed and whether it was authorized before attributing defense evasion. Generic policy, scan + and configuration tasks do not match. The full label is retained in log.taskName and log.msg. - Coverage is partial by design: fine-grained exclusion changes (a single path added without a task) live in the Control Center's audit log (getAuditLog API) rather than in syslog. This rule catches the exclusion-related tasks that reach the endpoint event stream. For complete coverage, ingest the audit log via the API. - - Next Steps: - 1. Identify who triggered the task via log.BitdefenderGZUserName / user object. Cross-check against change control - a legitimate exclusion change has a ticket. - 2. Read what changed: log.BitdefenderGZTaskName and log.BitdefenderGZTaskType. Task types related to exclusion, scanning behaviour and policy application are the ones that matter. - 3. Pull the current exclusion list from GravityZone Control Center and diff against your baseline. Any newly-excluded path is a candidate for containing attacker tools. - 4. Scan the excluded paths from a machine with a policy that does NOT include the exclusion, to check for hidden artefacts. - 5. If the admin identity or session looks anomalous (unusual source IP, off-hours, not a scheduled change window), rotate that admin's credentials before doing anything else. - 6. Restore the previous exclusion policy if unauthorised. Then re-scan every endpoint that had the weakened policy applied. -where: | - equals("log.BitdefenderGZModule", "task-status") && - (regexMatch("log.BitdefenderGZTaskName", "(?i)(exclusion|exclude|policy|whitelist|allowlist|configuration)") || - regexMatch("log.taskName", "(?i)(exclusion|exclude|policy|whitelist|allowlist|configuration)")) + ' +where: equals("log.BitdefenderGZModule","task-status") && equals("actionResult","success") && regexMatch("log.taskName","(?i)(exclusion|exclude|whitelist|allowlist)") groupBy: - - target.host - - lastEvent.log.BitdefenderGZTaskName +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKey +- lastEvent.log.taskName diff --git a/rules/antivirus/bitdefender_gz/usb_malware_propagation.yml b/rules/antivirus/bitdefender_gz/usb_malware_propagation.yml index caf5ce4b8..ad89dde21 100644 --- a/rules/antivirus/bitdefender_gz/usb_malware_propagation.yml +++ b/rules/antivirus/bitdefender_gz/usb_malware_propagation.yml @@ -1,51 +1,46 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz +- antivirus-bitdefender-gz name: Bitdefender GravityZone USB-Borne Threat Activity impact: confidentiality: 3 integrity: 3 availability: 2 category: Initial Access -technique: "T1091 - Replication Through Removable Media" +technique: T1091 - Replication Through Removable Media adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1091/ -description: | - Detects USB or removable-media threat activity from two independent signals: +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1091/ +description: 'At least three antimalware or behavioral records with autorun/removable-media indicators occurred on the same managed + endpoint within 30 minutes. Ordinary blocked or read-only device-control events do not count as malware. File-path indicators alone + do not prove USB provenance or propagation; verify the device and malware evidence. - - Device Control (device-control) with action "blocked" or "readonly". Bitdefender enforced the removable-media policy against a device. A single event is normal enforcement; a cluster of them across hosts is the shape of a campaign. - - Antimalware detection (av/avc/hd) where the artefact path indicates removable media: an autorun.inf reference, a removable-drive root, or a recycle bin on a non-fixed drive. - - The correlation raises the finding when three or more removable-media-related events reach the same target.host within 30 minutes, which is the shape of an active infection attempt rather than a stray insertion. - - Next Steps: - 1. Identify the device: log.BitdefenderGZDeviceName, log.BitdefenderGZDeviceId, log.BitdefenderGZVendorId. If the vendor/product IDs recur across hosts, the same physical device is moving between them. - 2. Read the block reason: action ("blocked" vs "readonly") tells you whether the endpoint policy rejected the device or only demoted it. - 3. If the second signal fires (antimalware detection on removable media), also read target.malware and log.BitdefenderGZDetectionName - autorun worms carry family names like Worm.Autoruner.*, INF/Autorun.*. - 4. Locate the device and, if possible, image it before returning it. The autorun payload is evidence. - 5. Check every endpoint the same device touched. Device Control keeps the device history in Control Center; use it to build the affected host list rather than guessing. - 6. Scan file shares reachable by the affected user account - USB worms that got in often propagate to shared folders next. - 7. Update Device Control policy to whitelist rather than blacklist for high-value hosts if this keeps happening. Blocking is downstream of an already-worse policy that allowed the device in the first place. -where: | - (equals("log.BitdefenderGZModule", "device-control") && - oneOf("log.BitdefenderGZAction", ["blocked", "readonly"])) || - (equals("log.BitdefenderGZModule", "device-control") && - oneOf("action", ["blocked", "readonly"])) || - (oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - (regexMatch("target.path", "(?i)(autorun\\.inf|\\\\\\$recycle\\.bin\\\\.*\\.(exe|scr|vbs|bat|cmd))") || - regexMatch("log.BitdefenderGZDetectionName", "(?i)(autorun|worm\\.autoruner|inf/autorun|usb\\.worm)"))) + ' +where: equals("log.correlationCandidate.usb_malware_propagation","match") correlation: - - indexPattern: v11-log-antivirus-bitdefender-gz-* - within: 30m - count: 3 - with: - - field: target.host - operator: filter_term - value: "{{.target.host}}" +- indexPattern: v11-log-antivirus-bitdefender-gz-* + within: 30m + count: 3 + with: + - field: log.correlationCandidate.usb_malware_propagation + operator: filter_term + value: match + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.BitdefenderGZCompanyId + operator: filter_term + value: '{{.log.BitdefenderGZCompanyId}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: filter_term + value: '{{.log.endpointKey}}' groupBy: - - target.host - - lastEvent.log.BitdefenderGZDeviceName +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKey diff --git a/rules/antivirus/bitdefender_gz/zero_day_malware_detection.yml b/rules/antivirus/bitdefender_gz/zero_day_malware_detection.yml index 1e1d4409b..784e90a95 100644 --- a/rules/antivirus/bitdefender_gz/zero_day_malware_detection.yml +++ b/rules/antivirus/bitdefender_gz/zero_day_malware_detection.yml @@ -1,42 +1,34 @@ -# Rule version v2.0.0 +# Rule version v3.0.0 dataTypes: - - antivirus-bitdefender-gz -name: Bitdefender GravityZone Threat Detected Without a Signature +- antivirus-bitdefender-gz +name: Bitdefender GravityZone Behavioral or Heuristic Threat Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Execution -technique: "T1027 - Obfuscated Files or Information" +technique: T1027 - Obfuscated Files or Information adversary: origin references: - - https://www.bitdefender.com/business/support/en/77212-237089-event-types.html - - https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html - - https://attack.mitre.org/techniques/T1027/ -description: | - Detects threats caught without a specific signature, which is what catches a new or repacked sample that signature matching would miss. +- https://www.bitdefender.com/business/support/en/77212-237089-event-types.html +- https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html +- https://attack.mitre.org/techniques/T1027/ +description: 'GravityZone reports a behavioral, sandbox or heuristic detection. These engines can detect known threats and policy-defined + behavior as well as new threats; this event does not prove a zero-day vulnerability. Review the signature, vendor action and endpoint + evidence. - Two kinds of signal: - - Bitdefender's signature-less engines: HyperDetect (machine learning tuned for targeted attacks), Sandbox Analyzer (detonation of unknown files) and Advanced Threat Control (runtime behaviour). - - Heuristic detection names, where the Heur. or GT: prefix says the verdict came from heuristics rather than a specific signature. + ' +where: 'oneOf("log.BitdefenderGZModule", ["hd", "network-sandboxing", "avc"]) || - A detection here deserves more attention than a routine signature hit, not less: the sample was unknown enough to require behavioural or machine-learning analysis, so there is no established remediation guidance for it and its capabilities are unverified. - - Next Steps: - 1. Note which engine fired, from log.BitdefenderGZModule - hd, network-sandboxing and avc each mean something different: - - hd: machine learning flagged it before execution - - network-sandboxing: it was detonated and found malicious, so it evaded everything upstream - - avc: it was already running and its behaviour gave it away, which means execution occurred - 2. Treat the endpoint as potentially compromised when the engine was avc, since a behavioural verdict implies the code ran - 3. Collect the sample for analysis using target.sha256 and target.path - a signature-less detection is exactly what threat intelligence needs - 4. Check the outcome: actionResult "failed" plus a signature-less detection is the worst combination, an unknown threat still live - 5. Hunt the estate for target.sha256. Endpoints without HyperDetect or Sandbox Analyzer enabled would not have caught this at all, so absence of alerts there proves nothing - 6. Review coverage after the fact: if this fired on one endpoint, check which others have the same engines enabled -where: | - oneOf("log.BitdefenderGZModule", ["hd", "network-sandboxing", "avc"]) || startsWith("target.malware", ["Heur.", "GT:"]) || + contains("target.malware", ":Heur.") + + ' groupBy: - - target.host - - target.malware +- lastEvent.dataSource +- lastEvent.log.BitdefenderGZCompanyId +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- target.malware From 72c77143c710e228293bade95d5f05fbf033568d Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Thu, 17 Sep 2026 18:16:56 -0400 Subject: [PATCH 3/3] fix(bitdefender): keep reporting identity on its physical endpoint --- filters/antivirus/bitdefender_gz.yml | 79 +++++++++++++++----- filters/audits/bitdefender.md | 7 +- plugins/alerts/testdata/bitdefender_raw.json | 62 ++++++++++++++- 3 files changed, 125 insertions(+), 23 deletions(-) diff --git a/filters/antivirus/bitdefender_gz.yml b/filters/antivirus/bitdefender_gz.yml index 557f3f143..271026b1f 100644 --- a/filters/antivirus/bitdefender_gz.yml +++ b/filters/antivirus/bitdefender_gz.yml @@ -1199,13 +1199,15 @@ pipeline: patterns: - fieldName: target.host pattern: (?s)^.+$ - where: exists("log.dvchost") && !oneOf("log.dvchost",["","-","unknown"]) + where: (exists("log.dvchost") && !oneOf("log.dvchost",["","-","unknown"])) && (!equals("log.BitdefenderGZModule","network-monitor") + || (exists("target.ip") && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.BitdefenderGZComputerFQDN patterns: - fieldName: target.domain pattern: (?s)^.+$ - where: exists("log.BitdefenderGZComputerFQDN") && !oneOf("log.BitdefenderGZComputerFQDN",["","-","unknown"]) + where: (exists("log.BitdefenderGZComputerFQDN") && !oneOf("log.BitdefenderGZComputerFQDN",["","-","unknown"])) && (!equals("log.BitdefenderGZModule","network-monitor") + || (exists("target.ip") && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.deviceExternalId patterns: @@ -1231,11 +1233,11 @@ pipeline: value: endpoint-id where: exists("log.endpointKey") && !exists("log.endpointKeyType") - grok: - source: target.host + source: log.dvchost patterns: - fieldName: log.endpointKey pattern: (?s)^.+$ - where: exists("target.host") && !oneOf("target.host",["","-","unknown"]) && !exists("log.endpointKey") + where: exists("log.dvchost") && !oneOf("log.dvchost",["","-","unknown"]) && !exists("log.endpointKey") - add: function: string params: @@ -1243,11 +1245,12 @@ pipeline: value: host where: exists("log.endpointKey") && !exists("log.endpointKeyType") - grok: - source: target.ip + source: log.dvc patterns: - fieldName: log.endpointKey pattern: (?s)^.+$ - where: exists("target.ip") && !oneOf("target.ip",["","-","unknown"]) && !exists("log.endpointKey") + where: '!exists("log.endpointKey") && (inCIDR("log.dvc","0.0.0.0/0") || inCIDR("log.dvc","::/0")) && !inCIDR("log.dvc","0.0.0.0/32") + && !inCIDR("log.dvc","::/128")' - add: function: string params: @@ -1259,7 +1262,8 @@ pipeline: patterns: - fieldName: target.user pattern: (?s)^.+$ - where: regexMatch("log.suser","^[^\\\\\\r\\n]+$") && !equals("log.BitdefenderGZModule","task-status") + where: (regexMatch("log.suser","^[^\\\\\\r\\n]+$") && !equals("log.BitdefenderGZModule","task-status")) && (!equals("log.BitdefenderGZModule","network-monitor") + || (exists("target.ip") && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.suser patterns: @@ -1277,7 +1281,8 @@ pipeline: patterns: - fieldName: target.user pattern: (?s)^.+$ - where: '!equals("log.BitdefenderGZModule","task-status") && !exists("target.user") && exists("log.suid") && !oneOf("log.suid",["","-"])' + where: (!equals("log.BitdefenderGZModule","task-status") && !exists("target.user") && exists("log.suid") && !oneOf("log.suid",["","-"])) + && (!equals("log.BitdefenderGZModule","network-monitor") || (exists("target.ip") && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.BitdefenderGZMalwareName patterns: @@ -1297,6 +1302,7 @@ pipeline: pattern: ^(?:.*[/\\])? - fieldName: target.process pattern: '[^/\\\r\n]+$' + where: (true) && (!equals("log.BitdefenderGZModule","network-monitor") || (exists("target.ip") && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.filePath patterns: @@ -1304,19 +1310,22 @@ pipeline: pattern: ^(?:.*[/\\])? - fieldName: target.process pattern: '[^/\\\r\n]+$' - where: equals("log.BitdefenderGZModule","avc") && !exists("target.process") + where: (equals("log.BitdefenderGZModule","avc") && !exists("target.process")) && (!equals("log.BitdefenderGZModule","network-monitor") + || (exists("target.ip") && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.BitdefenderGZProcessCommandLine patterns: - fieldName: target.command pattern: (?s)^.+$ - where: regexMatch("log.BitdefenderGZProcessCommandLine","^[^\\\\\\r\\n]+$") + where: (regexMatch("log.BitdefenderGZProcessCommandLine","^[^\\\\\\r\\n]+$")) && (!equals("log.BitdefenderGZModule","network-monitor") + || (exists("target.ip") && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.fname patterns: - fieldName: target.filename pattern: (?s)^.+$ - where: regexMatch("log.fname","^[^\\\\\\r\\n]+$") + where: (regexMatch("log.fname","^[^\\\\\\r\\n]+$")) && (!equals("log.BitdefenderGZModule","network-monitor") || (exists("target.ip") + && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.filePath patterns: @@ -1324,7 +1333,8 @@ pipeline: pattern: ^.*[/\\] - fieldName: target.filename pattern: '[^/\\\r\n]+$' - where: '!exists("target.filename")' + where: (!exists("target.filename")) && (!equals("log.BitdefenderGZModule","network-monitor") || (exists("target.ip") && safe("log.dvc","") + == safe("target.ip",""))) - grok: source: log.filePath patterns: @@ -1332,25 +1342,29 @@ pipeline: pattern: ^/.*/ - fieldName: '' pattern: '[^/]+$' - where: regexMatch("log.filePath","^[^\\\\\\r\\n]+$") + where: (regexMatch("log.filePath","^[^\\\\\\r\\n]+$")) && (!equals("log.BitdefenderGZModule","network-monitor") || (exists("target.ip") + && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.BitdefenderGZMalwareHash patterns: - fieldName: target.sha256 pattern: (?s)^.+$ - where: regexMatch("log.BitdefenderGZMalwareHash","^[0-9A-Fa-f]{64}$") + where: (regexMatch("log.BitdefenderGZMalwareHash","^[0-9A-Fa-f]{64}$")) && (!equals("log.BitdefenderGZModule","network-monitor") + || (exists("target.ip") && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.BitdefenderGZFileHashSha256 patterns: - fieldName: target.sha256 pattern: (?s)^.+$ - where: regexMatch("log.BitdefenderGZFileHashSha256","^[0-9A-Fa-f]{64}$") + where: (regexMatch("log.BitdefenderGZFileHashSha256","^[0-9A-Fa-f]{64}$")) && (!equals("log.BitdefenderGZModule","network-monitor") + || (exists("target.ip") && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.fileHash patterns: - fieldName: target.md5 pattern: (?s)^.+$ - where: regexMatch("log.fileHash","^[0-9A-Fa-f]{32}$") + where: (regexMatch("log.fileHash","^[0-9A-Fa-f]{32}$")) && (!equals("log.BitdefenderGZModule","network-monitor") || (exists("target.ip") + && safe("log.dvc","") == safe("target.ip",""))) - grok: source: log.request patterns: @@ -1372,8 +1386,9 @@ pipeline: patterns: - fieldName: target.port pattern: (?s)^.+$ - where: equals("log.BitdefenderGZModule","network-monitor") && regexMatch("log.BitdefenderGZDetectionLocalPort","^[0-9]+$") && - greaterThan("log.BitdefenderGZDetectionLocalPort",0) && lessOrEqual("log.BitdefenderGZDetectionLocalPort",65535) + where: (equals("log.BitdefenderGZModule","network-monitor") && regexMatch("log.BitdefenderGZDetectionLocalPort","^[0-9]+$") && + greaterThan("log.BitdefenderGZDetectionLocalPort",0) && lessOrEqual("log.BitdefenderGZDetectionLocalPort",65535)) && (!equals("log.BitdefenderGZModule","network-monitor") + || (exists("target.ip") && safe("log.dvc","") == safe("target.ip",""))) - cast: fields: - target.port @@ -1494,6 +1509,34 @@ pipeline: value: critical where: (greaterOrEqual("log.cefSeverity",9) && lessOrEqual("log.cefSeverity",10)) || oneOf("log.cefSeverity",["Very-High","Very High","very-high"]) + - grok: + source: log.dvchost + patterns: + - fieldName: origin.host + pattern: (?s)^.+$ + where: equals("log.BitdefenderGZModule","network-monitor") && exists("origin.ip") && safe("log.dvc","") == safe("origin.ip","") + && !exists("origin.host") && !oneOf("log.dvchost",["","-"]) + - grok: + source: log.BitdefenderGZComputerFQDN + patterns: + - fieldName: origin.domain + pattern: (?s)^.+$ + where: equals("log.BitdefenderGZModule","network-monitor") && exists("origin.ip") && safe("log.dvc","") == safe("origin.ip","") + && !exists("origin.domain") && !oneOf("log.BitdefenderGZComputerFQDN",["","-"]) + - grok: + source: log.suser + patterns: + - fieldName: origin.user + pattern: (?s)^.+$ + where: equals("log.BitdefenderGZModule","network-monitor") && exists("origin.ip") && safe("log.dvc","") == safe("origin.ip","") + && !exists("origin.user") && !oneOf("log.suser",["","-"]) && regexMatch("log.suser","^[^\\\\\\r\\n]+$") + - grok: + source: log.suid + patterns: + - fieldName: origin.user + pattern: (?s)^.+$ + where: equals("log.BitdefenderGZModule","network-monitor") && exists("origin.ip") && safe("log.dvc","") == safe("origin.ip","") + && !exists("origin.user") && !oneOf("log.suid",["","-"]) - dynamic: plugin: com.utmstack.geolocation params: diff --git a/filters/audits/bitdefender.md b/filters/audits/bitdefender.md index 7fcc391ea..f28692024 100644 --- a/filters/audits/bitdefender.md +++ b/filters/audits/bitdefender.md @@ -15,7 +15,10 @@ review input. No customer configuration, production deployment or merge is inclu text cannot invent addresses, actions or classifications. Re-read the protected raw header after generic KV parsing so extension keys cannot overwrite its event/severity. - Validate original IP fields before promotion and reject equivalent zero-address forms. - `dvc` is the managed endpoint. Explicit Network Attack Defense attacker/victim fields + `dvc` is the managed endpoint. Device host/domain/user fields follow a matching + physical side when a Network Attack Defense record names a different attacker or + victim; they are not attached to an unrelated victim IP. A local port is mapped only + when the managed endpoint is the victim. Explicit Network Attack Defense attacker/victim fields and firewall source fields retain their documented roles. Three sampled incident `src` values had displaced the managed endpoint. Their exact CEF roles, and those of `spt`, are not established by the available mapping documentation; retain them under @@ -62,7 +65,7 @@ identity, with vendor/indicator details where available. ## Validation and limits -- 77 public synthetic CEF fixtures exercise native and bounded syslog envelopes, all +- 80 public synthetic CEF fixtures exercise native and bounded syslog envelopes, all consumers, escaping, header/marker injection controls, endpoint roles, original IP guards, namespaces, hashes, ports, timestamps and benign controls. - Actual SDK CEL, configuration/Event/Alert serialization and placeholder handling are diff --git a/plugins/alerts/testdata/bitdefender_raw.json b/plugins/alerts/testdata/bitdefender_raw.json index 7d5846004..d55ff2a82 100644 --- a/plugins/alerts/testdata/bitdefender_raw.json +++ b/plugins/alerts/testdata/bitdefender_raw.json @@ -345,12 +345,13 @@ { "name": "network physical roles", "dataSource": "collector-test", - "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|network-monitor|9|BitdefenderGZModule=network-monitor BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZDetectionAttackerIp=198.51.100.9 BitdefenderGZDetectionVictimIp=192.0.2.12 BitdefenderGZDetectionLocalPort=443 BitdefenderGZMainAction=block", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|network-monitor|9|BitdefenderGZModule=network-monitor BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZDetectionAttackerIp=198.51.100.9 BitdefenderGZDetectionVictimIp=192.0.2.10 BitdefenderGZDetectionLocalPort=443 BitdefenderGZMainAction=block", "expected": { "origin.ip": "198.51.100.9", - "target.ip": "192.0.2.12", + "target.ip": "192.0.2.10", "target.port": 443, - "actionResult": "denied" + "actionResult": "denied", + "target.host": "workstation-test" }, "absent": [], "matches": [ @@ -887,5 +888,60 @@ "origin.url" ], "matches": [] + }, + { + "name": "network other victim has no reporter identity", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|network-monitor|9|BitdefenderGZModule=network-monitor BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=192.0.2.10 dvchost=workstation-test BitdefenderGZDetectionAttackerIp=198.51.100.9 BitdefenderGZDetectionVictimIp=192.0.2.12 BitdefenderGZDetectionLocalPort=443 BitdefenderGZMainAction=block", + "expected": { + "target.ip": "192.0.2.12", + "origin.ip": "198.51.100.9", + "log.endpointKey": "endpoint-test" + }, + "absent": [ + "target.host", + "target.domain", + "target.port" + ], + "matches": [ + "network_threat_detection" + ] + }, + { + "name": "managed endpoint is network attacker", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|network-monitor|9|BitdefenderGZModule=network-monitor BitdefenderGZCompanyId=company-test deviceExternalId=endpoint-test dvc=198.51.100.9 dvchost=workstation-test BitdefenderGZDetectionAttackerIp=198.51.100.9 BitdefenderGZDetectionVictimIp=192.0.2.10 BitdefenderGZDetectionLocalPort=443 BitdefenderGZMainAction=block suser=analyst BitdefenderGZComputerFQDN=workstation.example.test", + "expected": { + "origin.host": "workstation-test", + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.10", + "origin.user": "analyst", + "origin.domain": "workstation.example.test" + }, + "absent": [ + "target.host", + "target.domain", + "target.port" + ], + "matches": [ + "network_threat_detection" + ] + }, + { + "name": "network identity fallback is managed reporter", + "dataSource": "collector-test", + "raw": "CEF:0|Bitdefender|GravityZone|6.60|170000|network-monitor|9|BitdefenderGZModule=network-monitor BitdefenderGZCompanyId=company-test dvc=192.0.2.10 BitdefenderGZDetectionAttackerIp=198.51.100.9 BitdefenderGZDetectionVictimIp=192.0.2.12 BitdefenderGZDetectionLocalPort=443 BitdefenderGZMainAction=block", + "expected": { + "log.endpointKey": "192.0.2.10", + "log.endpointKeyType": "ip", + "target.ip": "192.0.2.12" + }, + "absent": [ + "target.host", + "target.port" + ], + "matches": [ + "network_threat_detection" + ] } ]