From d4c625b439a292f997f1459dcedff0b5963df881 Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Wed, 16 Sep 2026 20:34:17 -0400 Subject: [PATCH 1/3] fix: align Kaspersky filter and rule contracts --- filters/antivirus/kaspersky.yml | 94 ++++++++++++++----- filters/audits/kaspersky.md | 42 +++++++++ .../testdata/filter-contracts/kaspersky.json | 76 +++++++++++++++ .../kaspersky/code_injection_attempts.yml | 4 +- .../command_and_control_communication.yml | 2 +- .../kaspersky/critical_object_detected.yml | 4 +- .../kaspersky/data_exfiltration_attempts.yml | 2 +- rules/antivirus/kaspersky/lolbins_abuse.yml | 4 +- .../privilege_escalation_attempts.yml | 6 +- .../kaspersky/process_hollowing_detection.yml | 4 +- .../kaspersky/sandbox_evasion_attempts.yml | 2 +- .../suspicious_packed_executables.yml | 6 +- .../kaspersky/suspicious_scheduled_tasks.yml | 2 +- .../suspicious_service_installation.yml | 2 +- .../system_file_tampering_detection.yml | 2 +- .../trusted_application_compromise.yml | 2 +- .../kaspersky/wmi_abuse_detection.yml | 4 +- 17 files changed, 212 insertions(+), 46 deletions(-) create mode 100644 filters/audits/kaspersky.md create mode 100644 plugins/alerts/testdata/filter-contracts/kaspersky.json diff --git a/filters/antivirus/kaspersky.yml b/filters/antivirus/kaspersky.yml index 9ed7bcdb4..4dec916ef 100644 --- a/filters/antivirus/kaspersky.yml +++ b/filters/antivirus/kaspersky.yml @@ -1040,38 +1040,26 @@ pipeline: - rename: from: - - log.agt - to: origin.ip + - log.agt + to: log.agentAddress - rename: from: - - log.ahost - to: target.host + - log.ahost + to: log.agentHost - rename: from: - - log.amac - to: origin.mac + - log.amac + to: log.agentMac - rename: from: - log.dhost to: target.host - - rename: - from: - - log.originalAgentAddress - to: origin.ip - - rename: - from: - - log.syslogHost - to: origin.host - - rename: - from: - - log.syslogIpHost - to: origin.ip # .......................................................................# # Removing unnecessary characters of the restData @@ -1107,15 +1095,15 @@ pipeline: function: string params: key: actionResult - value: "Allow" - where: 'oneOf("action", ["Allow", "Allowed"])' + value: success + where: oneOf("action", ["Allow", "Allowed"]) - add: function: string params: key: actionResult - value: "blocked" - where: 'oneOf("action", ["Block", "Blocked", "blocked", "Redirect", "terminate", "delete", "quarantine"])' + value: denied + where: oneOf("action", ["Block", "Blocked", "blocked", "Redirect", "terminate", "delete", "quarantine"]) # .......................................................................# # Removing unused fields @@ -1126,4 +1114,64 @@ pipeline: - log.cefMsgAll - log.cefMsg - log.irrelevant - - log.notDefined \ No newline at end of file + - log.notDefined + + # 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 source fields to the standard event schema. + - rename: + from: + - log.shost + to: origin.host + - rename: + from: + - log.suser + to: origin.user + - rename: + from: + - log.smac + to: origin.mac + - rename: + from: + - log.duser + to: target.user + - rename: + from: + - log.dmac + to: target.mac + + # Normalize the source event severity. + - add: + function: string + params: + key: severity + value: info + where: (greaterOrEqual("log.cefDeviceSeverity",0) && lessOrEqual("log.cefDeviceSeverity",3)) || oneOf("log.cefDeviceSeverity",["Low","low","Unknown"]) + - add: + function: string + params: + key: severity + value: warning + where: (greaterOrEqual("log.cefDeviceSeverity",4) && lessOrEqual("log.cefDeviceSeverity",6)) || oneOf("log.cefDeviceSeverity",["Medium","medium"]) + - add: + function: string + params: + key: severity + value: error + where: (greaterOrEqual("log.cefDeviceSeverity",7) && lessOrEqual("log.cefDeviceSeverity",8)) || oneOf("log.cefDeviceSeverity",["High","high"]) + - add: + function: string + params: + key: severity + value: critical + where: (greaterOrEqual("log.cefDeviceSeverity",9) && lessOrEqual("log.cefDeviceSeverity",10)) || oneOf("log.cefDeviceSeverity",["Very-High","Very High","very-high"]) diff --git a/filters/audits/kaspersky.md b/filters/audits/kaspersky.md new file mode 100644 index 000000000..199f800a4 --- /dev/null +++ b/filters/audits/kaspersky.md @@ -0,0 +1,42 @@ +# Kaspersky normalization and rule review + +Keep reporting-agent identity separate from the source; normalize CEF identities, outcomes and severity; correct alert grouping. + +This draft targets UTMStack `v11`. It contains 1 filter changes +and 14 rule changes for this technology only. Review covered +1 filter configurations and 19 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. +- 2 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/kaspersky.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/kaspersky.json b/plugins/alerts/testdata/filter-contracts/kaspersky.json new file mode 100644 index 000000000..0c1686702 --- /dev/null +++ b/plugins/alerts/testdata/filter-contracts/kaspersky.json @@ -0,0 +1,76 @@ +{ + "technology": "Kaspersky", + "filters": [ + "filters/antivirus/kaspersky.yml" + ], + "rules": [ + "rules/antivirus/kaspersky/code_injection_attempts.yml", + "rules/antivirus/kaspersky/command_and_control_communication.yml", + "rules/antivirus/kaspersky/critical_object_detected.yml", + "rules/antivirus/kaspersky/data_exfiltration_attempts.yml", + "rules/antivirus/kaspersky/kaspersky_agent_tampering.yml", + "rules/antivirus/kaspersky/kaspersky_ransomware_behavior.yml", + "rules/antivirus/kaspersky/kaspersky_rootkit_detection.yml", + "rules/antivirus/kaspersky/lateral_movement_indicators.yml", + "rules/antivirus/kaspersky/lolbins_abuse.yml", + "rules/antivirus/kaspersky/privilege_escalation_attempts.yml", + "rules/antivirus/kaspersky/process_hollowing_detection.yml", + "rules/antivirus/kaspersky/sandbox_evasion_attempts.yml", + "rules/antivirus/kaspersky/suspicious_network_activity.yml", + "rules/antivirus/kaspersky/suspicious_packed_executables.yml", + "rules/antivirus/kaspersky/suspicious_scheduled_tasks.yml", + "rules/antivirus/kaspersky/suspicious_service_installation.yml", + "rules/antivirus/kaspersky/system_file_tampering_detection.yml", + "rules/antivirus/kaspersky/trusted_application_compromise.yml", + "rules/antivirus/kaspersky/wmi_abuse_detection.yml" + ], + "fixtures": [ + { + "name": "Kaspersky agent must not overwrite sender", + "filter": "antivirus/kaspersky.yml", + "input": { + "log": { + "src": "198.51.100.10", + "dst": "10.0.0.8", + "agt": "10.0.0.2", + "originalAgentAddress": "10.0.0.3", + "syslogIpHost": "10.0.0.4" + } + }, + "expected": { + "origin.ip": "198.51.100.10", + "target.ip": "10.0.0.8", + "log.agentAddress": "10.0.0.2" + }, + "absent": [], + "rules": {} + }, + { + "name": "Kaspersky CEF side identities", + "filter": "antivirus/kaspersky.yml", + "input": { + "log": { + "shost": "client", + "suser": "alice", + "smac": "00:11:22:33:44:55", + "dhost": "server", + "duser": "bob", + "ahost": "manager", + "agt": "10.0.0.99", + "cefDeviceSeverity": "10" + } + }, + "expected": { + "origin.host": "client", + "origin.user": "alice", + "target.host": "server", + "target.user": "bob", + "log.agentHost": "manager", + "severity": "critical" + }, + "absent": [ + "origin.ip" + ] + } + ] +} diff --git a/rules/antivirus/kaspersky/code_injection_attempts.yml b/rules/antivirus/kaspersky/code_injection_attempts.yml index 78a0efdc6..b267d25b0 100644 --- a/rules/antivirus/kaspersky/code_injection_attempts.yml +++ b/rules/antivirus/kaspersky/code_injection_attempts.yml @@ -31,5 +31,5 @@ where: | contains("action", ["terminate", "delete", "quarantine"]))) && contains("log.msg", ["lsass", "csrss", "winlogon", "services", "svchost", "explorer"]) deduplicateBy: - - origin.host - - log.cs4 + - adversary.host + - lastEvent.log.cs4 diff --git a/rules/antivirus/kaspersky/command_and_control_communication.yml b/rules/antivirus/kaspersky/command_and_control_communication.yml index 0ccc30584..501487834 100644 --- a/rules/antivirus/kaspersky/command_and_control_communication.yml +++ b/rules/antivirus/kaspersky/command_and_control_communication.yml @@ -32,5 +32,5 @@ where: | exists("target.ip") && action != "blocked" && action != "Blocked" groupBy: - - origin.host + - adversary.host - target.ip diff --git a/rules/antivirus/kaspersky/critical_object_detected.yml b/rules/antivirus/kaspersky/critical_object_detected.yml index 67399996b..36c86459b 100644 --- a/rules/antivirus/kaspersky/critical_object_detected.yml +++ b/rules/antivirus/kaspersky/critical_object_detected.yml @@ -34,5 +34,5 @@ where: | contains("log.cs4", ["Trojan", "HEUR:", "PDM:", "UDS:"]) || contains("log.msg", ["infected", "malicious", "dangerous"])) groupBy: - - origin.host - - log.signatureID + - adversary.host + - lastEvent.log.signatureID diff --git a/rules/antivirus/kaspersky/data_exfiltration_attempts.yml b/rules/antivirus/kaspersky/data_exfiltration_attempts.yml index 906328c90..e01799d05 100644 --- a/rules/antivirus/kaspersky/data_exfiltration_attempts.yml +++ b/rules/antivirus/kaspersky/data_exfiltration_attempts.yml @@ -48,5 +48,5 @@ afterEvents: within: 30m count: 5 groupBy: - - origin.ip + - adversary.ip - target.ip diff --git a/rules/antivirus/kaspersky/lolbins_abuse.yml b/rules/antivirus/kaspersky/lolbins_abuse.yml index e76283486..1a5f7cd67 100644 --- a/rules/antivirus/kaspersky/lolbins_abuse.yml +++ b/rules/antivirus/kaspersky/lolbins_abuse.yml @@ -32,5 +32,5 @@ where: | (contains("log.msg", ["download", "execute", "bypass", "encoded", "obfuscat", "hidden", "malicious"]) || exists("log.actionResult")) groupBy: - - log.cs4 - - origin.host + - lastEvent.log.cs4 + - adversary.host diff --git a/rules/antivirus/kaspersky/privilege_escalation_attempts.yml b/rules/antivirus/kaspersky/privilege_escalation_attempts.yml index d22739b6a..f1edb9ef3 100644 --- a/rules/antivirus/kaspersky/privilege_escalation_attempts.yml +++ b/rules/antivirus/kaspersky/privilege_escalation_attempts.yml @@ -36,6 +36,6 @@ where: | contains("log.cs4", "Exploit") || contains("log.msg", ["privilege", "elevation"])) groupBy: - - log.signatureID - - origin.host - - origin.user + - lastEvent.log.signatureID + - adversary.host + - adversary.user diff --git a/rules/antivirus/kaspersky/process_hollowing_detection.yml b/rules/antivirus/kaspersky/process_hollowing_detection.yml index 9ac8b4ea8..9148ed503 100644 --- a/rules/antivirus/kaspersky/process_hollowing_detection.yml +++ b/rules/antivirus/kaspersky/process_hollowing_detection.yml @@ -31,5 +31,5 @@ where: | contains("log.msg", ["hollow", "suspended", "unmap"])) && greaterOrEqual("log.cefDeviceSeverity", "3") groupBy: - - log.cs5 - - origin.host + - lastEvent.log.cs5 + - adversary.host diff --git a/rules/antivirus/kaspersky/sandbox_evasion_attempts.yml b/rules/antivirus/kaspersky/sandbox_evasion_attempts.yml index 9ef23b668..635902d28 100644 --- a/rules/antivirus/kaspersky/sandbox_evasion_attempts.yml +++ b/rules/antivirus/kaspersky/sandbox_evasion_attempts.yml @@ -34,4 +34,4 @@ where: | (equals("log.cat", "Behavior Detection") && contains("log.msg", ["delay", "sleep"]))) deduplicateBy: - - origin.host \ No newline at end of file + - adversary.host \ No newline at end of file diff --git a/rules/antivirus/kaspersky/suspicious_packed_executables.yml b/rules/antivirus/kaspersky/suspicious_packed_executables.yml index d2749117a..a57711f1a 100644 --- a/rules/antivirus/kaspersky/suspicious_packed_executables.yml +++ b/rules/antivirus/kaspersky/suspicious_packed_executables.yml @@ -17,7 +17,7 @@ description: | Detects when Kaspersky identifies suspicious packed executables, which are often used by malware to evade detection and analysis. Packed executables use compression or encryption to hide their true content and make reverse engineering more difficult. Next Steps: - 1. Identify the affected system from origin.hostname and origin.ip fields + 1. Identify the affected system from origin.host and origin.ip fields 2. Review the detected threat details from log.descMsg and log.msg fields 3. Check the action taken by the antivirus (blocked/detected) in the action field 4. Verify if the file is legitimate software that uses packing for protection @@ -37,5 +37,5 @@ where: | "PECompact", "Enigma", "Armadillo"]) || contains("log.cat", ["Trojan.Packed", "Packed"])) groupBy: - - origin.host - - origin.ip + - adversary.host + - adversary.ip diff --git a/rules/antivirus/kaspersky/suspicious_scheduled_tasks.yml b/rules/antivirus/kaspersky/suspicious_scheduled_tasks.yml index 186751821..1442a4412 100644 --- a/rules/antivirus/kaspersky/suspicious_scheduled_tasks.yml +++ b/rules/antivirus/kaspersky/suspicious_scheduled_tasks.yml @@ -43,4 +43,4 @@ where: | oneOf("log.cs1", ["infected", "suspicious"]) || exists("log.cefDeviceSeverity")) groupBy: - - origin.host + - adversary.host diff --git a/rules/antivirus/kaspersky/suspicious_service_installation.yml b/rules/antivirus/kaspersky/suspicious_service_installation.yml index adedfadb9..84822f5be 100644 --- a/rules/antivirus/kaspersky/suspicious_service_installation.yml +++ b/rules/antivirus/kaspersky/suspicious_service_installation.yml @@ -33,4 +33,4 @@ where: | (oneOf("log.cs1", ["infected", "suspicious"]) || greaterOrEqual("log.cefDeviceSeverity", "3")) groupBy: - - origin.host + - adversary.host diff --git a/rules/antivirus/kaspersky/system_file_tampering_detection.yml b/rules/antivirus/kaspersky/system_file_tampering_detection.yml index 6923bc52f..45a81e5ce 100644 --- a/rules/antivirus/kaspersky/system_file_tampering_detection.yml +++ b/rules/antivirus/kaspersky/system_file_tampering_detection.yml @@ -33,4 +33,4 @@ where: | contains("log.msg", ["system modification", "unauthorized change"]) || (equals("log.cat", "Behavior Detection") && contains("log.msg", "modify"))) groupBy: - - origin.host + - adversary.host diff --git a/rules/antivirus/kaspersky/trusted_application_compromise.yml b/rules/antivirus/kaspersky/trusted_application_compromise.yml index a33b6cf9f..fe53eb12f 100644 --- a/rules/antivirus/kaspersky/trusted_application_compromise.yml +++ b/rules/antivirus/kaspersky/trusted_application_compromise.yml @@ -36,4 +36,4 @@ where: | containsAll("log.msg", ["behavior", "trusted"])) && oneOf("log.cefDeviceSeverity", ["High", "Medium"]) groupBy: - - origin.host + - adversary.host diff --git a/rules/antivirus/kaspersky/wmi_abuse_detection.yml b/rules/antivirus/kaspersky/wmi_abuse_detection.yml index 8dbb77ac8..9a105b7e4 100644 --- a/rules/antivirus/kaspersky/wmi_abuse_detection.yml +++ b/rules/antivirus/kaspersky/wmi_abuse_detection.yml @@ -28,5 +28,5 @@ where: | contains("log.msg", "WMI")) && (greaterOrEqual("log.cefDeviceSeverity", "3") || equals("log.cat", "blocked")) groupBy: - - origin.host - - origin.user + - adversary.host + - adversary.user From b2742d7e5b811296203e534b40019c725ffa6d99 Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Thu, 17 Sep 2026 18:52:30 -0400 Subject: [PATCH 2/3] fix(kaspersky): parse native KSC logs and repair CEF rule contracts --- filters/antivirus/kaspersky.yml | 2310 ++++++++--------- filters/audits/kaspersky.md | 139 +- plugins/alerts/kaspersky_contract_test.go | 466 ++++ plugins/alerts/kaspersky_history_test.go | 251 ++ .../testdata/filter-contracts/kaspersky.json | 72 +- plugins/alerts/testdata/kaspersky_raw.json | 1065 ++++++++ .../kaspersky/code_injection_attempts.yml | 26 +- .../command_and_control_communication.yml | 34 +- .../kaspersky/critical_object_detected.yml | 29 +- .../kaspersky/data_exfiltration_attempts.yml | 54 +- .../kaspersky/kaspersky_agent_tampering.yml | 17 +- .../kaspersky_ransomware_behavior.yml | 46 +- .../kaspersky/kaspersky_rootkit_detection.yml | 15 +- .../kaspersky/lateral_movement_indicators.yml | 69 +- rules/antivirus/kaspersky/lolbins_abuse.yml | 23 +- .../privilege_escalation_attempts.yml | 25 +- .../kaspersky/process_hollowing_detection.yml | 21 +- .../kaspersky/sandbox_evasion_attempts.yml | 21 +- .../kaspersky/suspicious_network_activity.yml | 56 +- .../suspicious_packed_executables.yml | 21 +- .../kaspersky/suspicious_scheduled_tasks.yml | 19 +- .../suspicious_service_installation.yml | 19 +- .../system_file_tampering_detection.yml | 21 +- .../trusted_application_compromise.yml | 21 +- .../kaspersky/wmi_abuse_detection.yml | 21 +- 25 files changed, 3341 insertions(+), 1520 deletions(-) create mode 100644 plugins/alerts/kaspersky_contract_test.go create mode 100644 plugins/alerts/kaspersky_history_test.go create mode 100644 plugins/alerts/testdata/kaspersky_raw.json diff --git a/filters/antivirus/kaspersky.yml b/filters/antivirus/kaspersky.yml index 4dec916ef..4a8d8f3c8 100644 --- a/filters/antivirus/kaspersky.yml +++ b/filters/antivirus/kaspersky.yml @@ -1,1177 +1,1135 @@ -# KAV module filter, version 3.0.1 -# Based in docs and samples log provided -# Support Syslog CEF format -# -# Documentations -# 1- https://docs.centrify.com/Content/IntegrationContent/SIEM/arcsight-cef/arcsight-cef-format.htm -# 2- https://docs.devo.com/confluence/ndt/latest/devo-parsers/technologies-supported-in-cef-syslog-format/cef0-kaspersky -# 3- https://support.kaspersky.com/KLMS/8.2/en-US/151504.htm -# 4- https://support.kaspersky.com/ScanEngine/1.0/en-US/186767.htm -# -# Implementation -# 1. Parsing the RAW field containing the KAV - +# Kaspersky filter v3.1.0; native KSC structured syslog and CEF. pipeline: - - dataTypes: - - antivirus-kaspersky - steps: - - # .......................................................................# - # Using grok to parse syslogHeader of the message - # .......................................................................# - - grok: - patterns: - - fieldName: log.priority - pattern: '\<{{.data}}\>' - - fieldName: log.cefMsgAll - pattern: '{{.greedy}}' - - - grok: - patterns: - - fieldName: log.priority - pattern: '\<{{.data}}\>' - - fieldName: log.syslogVersion - pattern: '{{.integer}}' - - fieldName: log.deviceTime - pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}{{.space}}{{.time}}' - - fieldName: log.syslogHost - pattern: '{{.data}}\=' - - fieldName: log.syslogIpHost - pattern: '{{.ipv4}}' - - fieldName: log.notDefined - pattern: '{{.integer}}' - - fieldName: log.cefMsgAll - pattern: '{{.greedy}}' - - # .......................................................................# - # Removing unnecessary characters of the syslogHeader - # .......................................................................# - - trim: - function: prefix - substring: '<' - fields: - - log.priority - - trim: - function: suffix - substring: '>' - fields: - - log.priority - - - trim: - function: suffix - substring: '=' - fields: - - log.syslogHost - - # .......................................................................# - # Using grok to parse components of the cefMessage - # .......................................................................# - - grok: - patterns: - - fieldName: log.cef - pattern: '(CEF:)' - - fieldName: log.cefVersion - pattern: '{{.integer}}' - - fieldName: log.embDeviceVendor - pattern: '\|{{.data}}\|' - - fieldName: log.embDeviceProduct - pattern: '{{.data}}\|' - - fieldName: log.deviceVersion - pattern: '{{.data}}\|' - - fieldName: log.signatureID - pattern: '{{.data}}\|' - - fieldName: log.descMsg - pattern: '{{.data}}\|' - - fieldName: log.cefDeviceSeverity - pattern: '{{.data}}\|' - - fieldName: log.cefMsg - pattern: '{{.greedy}}' - source: log.cefMsgAll - - # .......................................................................# - # Removing unnecessary characters of the cefMessage - # .......................................................................# - - trim: - function: prefix - substring: '|' - fields: - - log.embDeviceVendor - - trim: - function: suffix - substring: '|' - fields: - - log.embDeviceVendor - - log.embDeviceProduct - - log.deviceVersion - - log.signatureID - - log.descMsg - - log.cefDeviceSeverity - - # .......................................................................# - # Using the kv filter for cefMsg - # .......................................................................# - - kv: - source: log.cefMsg - fieldSplit: " " - valueSplit: "=" - - # .......................................................................# - # Remove fields that have issues with kv filter - # .......................................................................# - - delete: - fields: - - log.destinationZoneURI - - log.msg - - log.customerURI - - log.destinationZoneExternalID - - log.cs1 - - log.cs2 - - log.cs3 - - log.cs4 - - log.cs5 - - log.cs6 - - log.cs7 - - log.cs8 - - log.cs9 - - log.cs10 - - log.originalAgentZoneURI - - log.flexString1 - - log.cat - - log.cs1Label - - log.cs2Label - - log.cs3Label - - log.cs4Label - - log.cs5Label - - log.cs6Label - - log.cs7Label - - log.cs8Label - - log.cs9Label - - log.cs10Label - - log.cn1 - - log.cn2 - - log.cn3 - - log.cn4 - - log.cn5 - - log.cn6 - - log.cn7 - - log.cn8 - - log.cn9 - - log.cn10 - - log.cn1Label - - log.cn2Label - - log.cn3Label - - log.cn4Label - - log.cn5Label - - log.cn6Label - - log.cn7Label - - log.cn8Label - - log.cn9Label - - log.cn10Label - - # .......................................................................# - # Using grok to parse kv issued fields - # .......................................................................# - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(msg=)' - - fieldName: log.msg - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(customerURI=)' - - fieldName: log.customerURI - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(destinationZoneURI=)' - - fieldName: log.destinationZoneURI - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(destinationZoneExternalID=)' - - fieldName: log.destinationZoneExternalID - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs1=)' - - fieldName: log.cs1 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs2=)' - - fieldName: log.cs2 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs3=)' - - fieldName: log.cs3 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs4=)' - - fieldName: log.cs4 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs5=)' - - fieldName: log.cs5 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs6=)' - - fieldName: log.cs6 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs7=)' - - fieldName: log.cs7 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs8=)' - - fieldName: log.cs8 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs9=)' - - fieldName: log.cs9 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs10=)' - - fieldName: log.cs10 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn1=)' - - fieldName: log.cn1 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn2=)' - - fieldName: log.cn2 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn3=)' - - fieldName: log.cn3 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn4=)' - - fieldName: log.cn4 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn5=)' - - fieldName: log.cn5 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn6=)' - - fieldName: log.cn6 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn7=)' - - fieldName: log.cn7 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn8=)' - - fieldName: log.cn8 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn9=)' - - fieldName: log.cn9 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn10=)' - - fieldName: log.cn10 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(originalAgentZoneURI=)' - - fieldName: log.originalAgentZoneURI - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(flexString1=)' - - fieldName: log.flexString1 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cat=)' - - fieldName: log.cat - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs1Label=)' - - fieldName: log.cs1Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs2Label=)' - - fieldName: log.cs2Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs3Label=)' - - fieldName: log.cs3Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs4Label=)' - - fieldName: log.cs4Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs5Label=)' - - fieldName: log.cs5Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs6Label=)' - - fieldName: log.cs6Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs7Label=)' - - fieldName: log.cs7Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs8Label=)' - - fieldName: log.cs8Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs9Label=)' - - fieldName: log.cs9Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cs10Label=)' - - fieldName: log.cs10Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn1Label=)' - - fieldName: log.cn1Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn2Label=)' - - fieldName: log.cn2Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn3Label=)' - - fieldName: log.cn3Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn4Label=)' - - fieldName: log.cn4Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn5Label=)' - - fieldName: log.cn5Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn6Label=)' - - fieldName: log.cn6Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn7Label=)' - - fieldName: log.cn7Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn8Label=)' - - fieldName: log.cn8Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn9Label=)' - - fieldName: log.cn9Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(cn10Label=)' - - fieldName: log.cn10Label - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefMsg - - # .................................................# - # Applying grok to remove unnecessary data - # .................................................# - - grok: - patterns: - - fieldName: log.destinationZoneURI - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.destinationZoneURI - - - grok: - patterns: - - fieldName: log.msg - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.msg - - - grok: - patterns: - - fieldName: log.customerURI - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.customerURI - - - grok: - patterns: - - fieldName: log.destinationZoneExternalID - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.destinationZoneExternalID - - - grok: - patterns: - - fieldName: log.cs1 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs1 - - - grok: - patterns: - - fieldName: log.cs2 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs2 - - - grok: - patterns: - - fieldName: log.cs3 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs3 - - - grok: - patterns: - - fieldName: log.cs4 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs4 - - - grok: - patterns: - - fieldName: log.cs5 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs5 - - - grok: - patterns: - - fieldName: log.cs6 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs6 - - - grok: - patterns: - - fieldName: log.cs7 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs7 - - - grok: - patterns: - - fieldName: log.cs8 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs8 - - - grok: - patterns: - - fieldName: log.cs9 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs9 - - - grok: - patterns: - - fieldName: log.cs10 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs10 - - - grok: - patterns: - - fieldName: log.cn1 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn1 - - - grok: - patterns: - - fieldName: log.cn2 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn2 - - - grok: - patterns: - - fieldName: log.cn3 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn3 - - - grok: - patterns: - - fieldName: log.cn4 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn4 - - - grok: - patterns: - - fieldName: log.cn5 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn5 - - - grok: - patterns: - - fieldName: log.cn6 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn6 - - - grok: - patterns: - - fieldName: log.cn7 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn7 - - - grok: - patterns: - - fieldName: log.cn8 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn8 - - - grok: - patterns: - - fieldName: log.cn9 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn9 - - - grok: - patterns: - - fieldName: log.cn10 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn10 - - - grok: - patterns: - - fieldName: log.originalAgentZoneURI - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.originalAgentZoneURI - - - grok: - patterns: - - fieldName: log.flexString1 - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.flexString1 - - - grok: - patterns: - - fieldName: log.cat - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cat - - - grok: - patterns: - - fieldName: log.cs1Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs1Label - - - grok: - patterns: - - fieldName: log.cs2Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs2Label - - - grok: - patterns: - - fieldName: log.cs3Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs3Label - - - grok: - patterns: - - fieldName: log.cs4Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs4Label - - - grok: - patterns: - - fieldName: log.cs5Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs5Label - - - grok: - patterns: - - fieldName: log.cs6Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs6Label - - - grok: - patterns: - - fieldName: log.cs7Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs7Label - - - grok: - patterns: - - fieldName: log.cs8Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs8Label - - - grok: - patterns: - - fieldName: log.cs9Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs9Label - - - grok: - patterns: - - fieldName: log.cs10Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cs10Label - - - grok: - patterns: - - fieldName: log.cn1Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn1Label - - - grok: - patterns: - - fieldName: log.cn2Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn2Label - - - grok: - patterns: - - fieldName: log.cn3Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn3Label - - - grok: - patterns: - - fieldName: log.cn4Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn4Label - - - grok: - patterns: - - fieldName: log.cn5Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn5Label - - - grok: - patterns: - - fieldName: log.cn6Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn6Label - - - grok: - patterns: - - fieldName: log.cn7Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn7Label - - - grok: - patterns: - - fieldName: log.cn8Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn8Label - - - grok: - patterns: - - fieldName: log.cn9Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn9Label - - - grok: - patterns: - - fieldName: log.cn10Label - pattern: '{{.greedy}}{{.space}}' - - fieldName: log.irrelevant - pattern: '{{.word}}(=)' - source: log.cn10Label - - # .......................................................................# - # Rename fields - # .......................................................................# - - rename: - from: - - log.src - to: origin.ip - - - rename: - from: - - log.dst - to: target.ip - - - rename: - from: - - log.act - to: action - - - rename: - from: - - log.agt - to: log.agentAddress - - - rename: - from: - - log.ahost - to: log.agentHost - - - rename: - from: - - log.amac - to: log.agentMac - - - rename: - from: - - log.dhost - to: target.host - - - - - # .......................................................................# - # Removing unnecessary characters of the restData - # .......................................................................# - - trim: - function: suffix - substring: '||=||=' - fields: - - log.adarcSightEventPath - - log.aid - - log.customerID - - log.destinationZoneID - - log.originalAgentId - - # Adding geolocation to origin.ip - - dynamic: - plugin: com.utmstack.geolocation - params: - source: origin.ip - destination: origin.geolocation - where: exists("origin.ip") - - # Adding geolocation to target.ip - - dynamic: - plugin: com.utmstack.geolocation - params: - source: target.ip - destination: target.geolocation - where: exists("target.ip") - - # Adding action result - - add: - function: string - params: - key: actionResult - value: success - where: oneOf("action", ["Allow", "Allowed"]) - - - add: - function: string - params: - key: actionResult - value: denied - where: oneOf("action", ["Block", "Blocked", "blocked", "Redirect", "terminate", "delete", "quarantine"]) - - # .......................................................................# - # Removing unused fields - # .......................................................................# - - delete: - fields: - - log.cef - - log.cefMsgAll - - log.cefMsg - - log.irrelevant - - log.notDefined - - # 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 source fields to the standard event schema. - - rename: - from: - - log.shost - to: origin.host - - rename: - from: - - log.suser - to: origin.user - - rename: - from: - - log.smac - to: origin.mac - - rename: - from: - - log.duser - to: target.user - - rename: - from: - - log.dmac - to: target.mac - - # Normalize the source event severity. - - add: - function: string - params: - key: severity - value: info - where: (greaterOrEqual("log.cefDeviceSeverity",0) && lessOrEqual("log.cefDeviceSeverity",3)) || oneOf("log.cefDeviceSeverity",["Low","low","Unknown"]) - - add: - function: string - params: - key: severity - value: warning - where: (greaterOrEqual("log.cefDeviceSeverity",4) && lessOrEqual("log.cefDeviceSeverity",6)) || oneOf("log.cefDeviceSeverity",["Medium","medium"]) - - add: - function: string - params: - key: severity - value: error - where: (greaterOrEqual("log.cefDeviceSeverity",7) && lessOrEqual("log.cefDeviceSeverity",8)) || oneOf("log.cefDeviceSeverity",["High","high"]) - - add: - function: string - params: - key: severity - value: critical - where: (greaterOrEqual("log.cefDeviceSeverity",9) && lessOrEqual("log.cefDeviceSeverity",10)) || oneOf("log.cefDeviceSeverity",["Very-High","Very High","very-high"]) +- dataTypes: + - antivirus-kaspersky + steps: + - delete: + fields: + - log.format + - log.cefExtension + - log.ksc + - log.eventType + - log.endpointKey + - log.endpointKeyType + - log.correlationCandidate + - 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]{1,3}>[1-9][0-9]*\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+-\s+|<[0-9]{1,3}>)?CEF:' + - fieldName: log.cefVersion + pattern: '[0-9]+' + - fieldName: '' + pattern: \| + - fieldName: log.embDeviceVendor + pattern: (?:\\.|[^\\|\r\n])+ + - fieldName: '' + pattern: \| + - fieldName: log.embDeviceProduct + pattern: (?:\\.|[^\\|\r\n])* + - fieldName: '' + pattern: \| + - fieldName: log.deviceVersion + pattern: (?:\\.|[^\\|\r\n])* + - fieldName: '' + pattern: \| + - fieldName: log.signatureID + pattern: (?:\\.|[^\\|\r\n])* + - fieldName: '' + pattern: \| + - fieldName: log.descMsg + pattern: (?:\\.|[^\\|\r\n])* + - fieldName: '' + pattern: \| + - fieldName: log.cefDeviceSeverity + pattern: (?:\\.|[^\\|\r\n])* + - fieldName: '' + pattern: \| + - fieldName: log.cefExtension + pattern: (?s:.*)$ + - kv: + source: log.cefExtension + fieldSplit: ' ' + valueSplit: '=' + where: exists("log.cefExtension") + - delete: + fields: + - log.act + - log.agt + - log.ahost + - log.amac + - log.cat + - log.cn1 + - log.cn10 + - log.cn10Label + - log.cn1Label + - log.cn2 + - log.cn2Label + - log.cn3 + - log.cn3Label + - log.cn4 + - log.cn4Label + - log.cn5 + - log.cn5Label + - log.cn6 + - log.cn6Label + - log.cn7 + - log.cn7Label + - log.cn8 + - log.cn8Label + - log.cn9 + - log.cn9Label + - log.cs1 + - log.cs10 + - log.cs10Label + - log.cs1Label + - log.cs2 + - log.cs2Label + - log.cs3 + - log.cs3Label + - log.cs4 + - log.cs4Label + - log.cs5 + - log.cs5Label + - log.cs6 + - log.cs6Label + - log.cs7 + - log.cs7Label + - log.cs8 + - log.cs8Label + - log.cs9 + - log.cs9Label + - log.customerURI + - log.destinationZoneExternalID + - log.destinationZoneURI + - log.dhost + - log.dmac + - log.dpt + - log.dst + - log.duser + - log.flexString1 + - log.fname + - log.msg + - log.originalAgentZoneURI + - log.proto + - log.shost + - log.smac + - log.spt + - log.src + - log.suser + - log.cefVersion + - log.embDeviceVendor + - log.embDeviceProduct + - log.deviceVersion + - log.signatureID + - log.descMsg + - log.cefDeviceSeverity + - log.format + - log.cefExtension + - log.ksc + - log.eventType + - log.endpointKey + - log.endpointKeyType + - log.correlationCandidate + - 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]{1,3}>[1-9][0-9]*\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+-\s+|<[0-9]{1,3}>)?CEF:' + - fieldName: log.cefVersion + pattern: '[0-9]+' + - fieldName: '' + pattern: \| + - fieldName: log.embDeviceVendor + pattern: (?:\\.|[^\\|\r\n])+ + - fieldName: '' + pattern: \| + - fieldName: log.embDeviceProduct + pattern: (?:\\.|[^\\|\r\n])* + - fieldName: '' + pattern: \| + - fieldName: log.deviceVersion + pattern: (?:\\.|[^\\|\r\n])* + - fieldName: '' + pattern: \| + - fieldName: log.signatureID + pattern: (?:\\.|[^\\|\r\n])* + - fieldName: '' + pattern: \| + - fieldName: log.descMsg + pattern: (?:\\.|[^\\|\r\n])* + - fieldName: '' + pattern: \| + - fieldName: log.cefDeviceSeverity + pattern: (?:\\.|[^\\|\r\n])* + - fieldName: '' + pattern: \| + - fieldName: log.cefExtension + pattern: (?s:.*)$ + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*act= + - fieldName: log.act + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*agt= + - fieldName: log.agt + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*ahost= + - fieldName: log.ahost + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*amac= + - fieldName: log.amac + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cat= + - fieldName: log.cat + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn1= + - fieldName: log.cn1 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn10= + - fieldName: log.cn10 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn10Label= + - fieldName: log.cn10Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn1Label= + - fieldName: log.cn1Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn2= + - fieldName: log.cn2 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn2Label= + - fieldName: log.cn2Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn3= + - fieldName: log.cn3 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn3Label= + - fieldName: log.cn3Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn4= + - fieldName: log.cn4 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn4Label= + - fieldName: log.cn4Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn5= + - fieldName: log.cn5 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn5Label= + - fieldName: log.cn5Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn6= + - fieldName: log.cn6 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn6Label= + - fieldName: log.cn6Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn7= + - fieldName: log.cn7 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn7Label= + - fieldName: log.cn7Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn8= + - fieldName: log.cn8 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn8Label= + - fieldName: log.cn8Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn9= + - fieldName: log.cn9 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn9Label= + - fieldName: log.cn9Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs1= + - fieldName: log.cs1 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs10= + - fieldName: log.cs10 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs10Label= + - fieldName: log.cs10Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs1Label= + - fieldName: log.cs1Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs2= + - fieldName: log.cs2 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs2Label= + - fieldName: log.cs2Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs3= + - fieldName: log.cs3 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs3Label= + - fieldName: log.cs3Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs4= + - fieldName: log.cs4 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs4Label= + - fieldName: log.cs4Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs5= + - fieldName: log.cs5 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs5Label= + - fieldName: log.cs5Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs6= + - fieldName: log.cs6 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs6Label= + - fieldName: log.cs6Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs7= + - fieldName: log.cs7 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs7Label= + - fieldName: log.cs7Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs8= + - fieldName: log.cs8 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs8Label= + - fieldName: log.cs8Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs9= + - fieldName: log.cs9 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs9Label= + - fieldName: log.cs9Label + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*customerURI= + - fieldName: log.customerURI + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*destinationZoneExternalID= + - fieldName: log.destinationZoneExternalID + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*destinationZoneURI= + - fieldName: log.destinationZoneURI + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*dhost= + - fieldName: log.dhost + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*dmac= + - fieldName: log.dmac + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*dpt= + - fieldName: log.dpt + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*dst= + - fieldName: log.dst + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*duser= + - fieldName: log.duser + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*flexString1= + - fieldName: log.flexString1 + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*fname= + - fieldName: log.fname + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*msg= + - fieldName: log.msg + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*originalAgentZoneURI= + - fieldName: log.originalAgentZoneURI + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*proto= + - fieldName: log.proto + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*shost= + - fieldName: log.shost + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*smac= + - fieldName: log.smac + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*spt= + - fieldName: log.spt + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*src= + - fieldName: log.src + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - grok: + source: log.cefExtension + patterns: + - fieldName: '' + pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*suser= + - fieldName: log.suser + pattern: (?:\\.|[^\\=\r\n])*? + - fieldName: '' + pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + - add: + function: string + params: + key: log.format + value: cef + where: exists("log.cefExtension") + - grok: + source: log.src + patterns: + - fieldName: origin.ip + pattern: (?s)^.+$ + where: (inCIDR("log.src","0.0.0.0/0") || inCIDR("log.src","::/0")) && !inCIDR("log.src","0.0.0.0/32") && !inCIDR("log.src","::/128") + - grok: + source: log.dst + patterns: + - fieldName: target.ip + pattern: (?s)^.+$ + where: (inCIDR("log.dst","0.0.0.0/0") || inCIDR("log.dst","::/0")) && !inCIDR("log.dst","0.0.0.0/32") && !inCIDR("log.dst","::/128") + - grok: + source: log.shost + patterns: + - fieldName: origin.host + pattern: (?s)^.+$ + where: exists("log.shost") && !oneOf("log.shost",["","-"]) && !contains("log.shost","\\") + - grok: + source: log.dhost + patterns: + - fieldName: target.host + pattern: (?s)^.+$ + where: exists("log.dhost") && !oneOf("log.dhost",["","-"]) && !contains("log.dhost","\\") + - grok: + source: log.suser + patterns: + - fieldName: origin.user + pattern: (?s)^.+$ + where: exists("log.suser") && !oneOf("log.suser",["","-"]) && !contains("log.suser","\\") + - grok: + source: log.duser + patterns: + - fieldName: target.user + pattern: (?s)^.+$ + where: exists("log.duser") && !oneOf("log.duser",["","-"]) && !contains("log.duser","\\") + - grok: + source: log.smac + patterns: + - fieldName: origin.mac + pattern: (?s)^.+$ + where: exists("log.smac") && !oneOf("log.smac",["","-"]) && !contains("log.smac","\\") + - grok: + source: log.dmac + patterns: + - fieldName: target.mac + pattern: (?s)^.+$ + where: exists("log.dmac") && !oneOf("log.dmac",["","-"]) && !contains("log.dmac","\\") + - grok: + source: log.agt + patterns: + - fieldName: log.agentAddress + pattern: (?s)^.+$ + where: exists("log.agt") && !oneOf("log.agt",["","-"]) + - grok: + source: log.ahost + patterns: + - fieldName: log.agentHost + pattern: (?s)^.+$ + where: exists("log.ahost") && !oneOf("log.ahost",["","-"]) + - grok: + source: log.amac + patterns: + - fieldName: log.agentMac + pattern: (?s)^.+$ + where: exists("log.amac") && !oneOf("log.amac",["","-"]) + - grok: + source: log.spt + patterns: + - fieldName: origin.port + pattern: (?s)^.+$ + where: regexMatch("log.spt","^[0-9]+$") && greaterThan("log.spt",0) && lessOrEqual("log.spt",65535) + - cast: + fields: + - origin.port + to: int + where: exists("origin.port") + - grok: + source: log.dpt + patterns: + - fieldName: target.port + pattern: (?s)^.+$ + where: regexMatch("log.dpt","^[0-9]+$") && greaterThan("log.dpt",0) && lessOrEqual("log.dpt",65535) + - cast: + fields: + - target.port + to: int + where: exists("target.port") + - grok: + source: log.proto + patterns: + - fieldName: protocol + pattern: (?s)^.+$ + where: exists("log.proto") && !oneOf("log.proto",["","-"]) + - grok: + source: log.act + patterns: + - fieldName: action + pattern: (?s)^.+$ + where: exists("log.act") && !oneOf("log.act",["","-"]) + - add: + function: string + params: + key: actionResult + value: success + where: regexMatch("action","(?i)^(allow|allowed)$") + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("action","(?i)^(block|blocked)$") + - add: + function: string + params: + key: severity + value: info + where: (greaterOrEqual("log.cefDeviceSeverity",0) && lessOrEqual("log.cefDeviceSeverity",3)) || oneOf("log.cefDeviceSeverity",["Low","low","Unknown"]) + - add: + function: string + params: + key: severity + value: warning + where: (greaterOrEqual("log.cefDeviceSeverity",4) && lessOrEqual("log.cefDeviceSeverity",6)) || oneOf("log.cefDeviceSeverity",["Medium","medium"]) + - add: + function: string + params: + key: severity + value: error + where: (greaterOrEqual("log.cefDeviceSeverity",7) && lessOrEqual("log.cefDeviceSeverity",8)) || oneOf("log.cefDeviceSeverity",["High","high"]) + - add: + function: string + params: + key: severity + value: critical + where: (greaterOrEqual("log.cefDeviceSeverity",9) && lessOrEqual("log.cefDeviceSeverity",10)) || oneOf("log.cefDeviceSeverity",["Very-High","Very + High","very-high"]) + - delete: + fields: + - log.cefExtension + - grok: + source: raw + patterns: + - fieldName: '' + pattern: ^< + - fieldName: log.priority + pattern: '[0-9]{1,3}' + - fieldName: '' + pattern: '>' + - fieldName: log.ksc.syslogVersion + pattern: '[1-9][0-9]*' + - fieldName: '' + pattern: ' ' + - fieldName: log.ksc.time + pattern: \S+ + - fieldName: '' + pattern: ' ' + - fieldName: log.ksc.reportingHost + pattern: \S+ + - fieldName: '' + pattern: ' ' + - fieldName: log.ksc.appName + pattern: \S+ + - fieldName: '' + pattern: ' ' + - fieldName: log.ksc.procId + pattern: \S+ + - fieldName: '' + pattern: ' ' + - fieldName: log.ksc.messageId + pattern: \S+ + - fieldName: '' + pattern: ' \[event@23668' + - fieldName: log.ksc.params + pattern: '(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")*' + - fieldName: '' + pattern: '\](?: )?' + - fieldName: log.msg + pattern: (?s:.*)$ + - add: + function: string + params: + key: log.format + value: ksc-syslog + where: exists("log.ksc.params") + - grok: + source: log.ksc.params + patterns: + - fieldName: '' + pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* p1="' + - fieldName: log.ksc.p1 + pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + - fieldName: '' + pattern: '"(?: |$)' + - grok: + source: log.ksc.params + patterns: + - fieldName: '' + pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* et="' + - fieldName: log.ksc.et + pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + - fieldName: '' + pattern: '"(?: |$)' + - grok: + source: log.ksc.params + patterns: + - fieldName: '' + pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* etdn="' + - fieldName: log.ksc.etdn + pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + - fieldName: '' + pattern: '"(?: |$)' + - grok: + source: log.ksc.params + patterns: + - fieldName: '' + pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* hdn="' + - fieldName: log.ksc.hdn + pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + - fieldName: '' + pattern: '"(?: |$)' + - grok: + source: log.ksc.params + patterns: + - fieldName: '' + pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* hip="' + - fieldName: log.ksc.hip + pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + - fieldName: '' + pattern: '"(?: |$)' + - grok: + source: log.ksc.params + patterns: + - fieldName: '' + pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* gn="' + - fieldName: log.ksc.gn + pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + - fieldName: '' + pattern: '"(?: |$)' + - grok: + source: log.ksc.params + patterns: + - fieldName: '' + pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* kscfqdn="' + - fieldName: log.ksc.kscfqdn + pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + - fieldName: '' + pattern: '"(?: |$)' + - grok: + source: log.ksc.et + patterns: + - fieldName: log.eventType + pattern: (?s)^.+$ + where: exists("log.ksc.et") && !oneOf("log.ksc.et",["","-"]) + - grok: + source: log.ksc.messageId + patterns: + - fieldName: log.eventType + pattern: (?s)^.+$ + where: '!exists("log.eventType")' + - grok: + source: log.ksc.hip + patterns: + - fieldName: target.ip + pattern: (?s)^.+$ + where: (inCIDR("log.ksc.hip","0.0.0.0/0") || inCIDR("log.ksc.hip","::/0")) && !inCIDR("log.ksc.hip","0.0.0.0/32") && !inCIDR("log.ksc.hip","::/128") + - grok: + source: log.ksc.hdn + patterns: + - fieldName: target.host + pattern: (?s)^.+$ + where: exists("log.ksc.hdn") && !oneOf("log.ksc.hdn",["","-"]) && !contains("log.ksc.hdn","\\") + - grok: + source: log.ksc.time + patterns: + - fieldName: deviceTime + pattern: (?s)^.+$ + where: regexMatch("log.ksc.time","^[0-9]{4}-(0[1-9]|1[0-2])-([0-2][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?(Z|[+-][0-2][0-9]:[0-5][0-9])$") + - add: + function: string + params: + key: severity + value: critical + where: equals("log.format","ksc-syslog") && oneOf("log.priority",["0","1","2","8","9","10","16","17","18","24","25","26","32","33","34","40","41","42","48","49","50","56","57","58","64","65","66","72","73","74","80","81","82","88","89","90","96","97","98","104","105","106","112","113","114","120","121","122","128","129","130","136","137","138","144","145","146","152","153","154","160","161","162","168","169","170","176","177","178","184","185","186"]) + - add: + function: string + params: + key: severity + value: error + where: equals("log.format","ksc-syslog") && oneOf("log.priority",["3","11","19","27","35","43","51","59","67","75","83","91","99","107","115","123","131","139","147","155","163","171","179","187"]) + - add: + function: string + params: + key: severity + value: warning + where: equals("log.format","ksc-syslog") && oneOf("log.priority",["4","12","20","28","36","44","52","60","68","76","84","92","100","108","116","124","132","140","148","156","164","172","180","188"]) + - add: + function: string + params: + key: severity + value: info + where: equals("log.format","ksc-syslog") && oneOf("log.priority",["5","6","13","14","21","22","29","30","37","38","45","46","53","54","61","62","69","70","77","78","85","86","93","94","101","102","109","110","117","118","125","126","133","134","141","142","149","150","157","158","165","166","173","174","181","182","189","190"]) + - add: + function: string + params: + key: severity + value: debug + where: equals("log.format","ksc-syslog") && oneOf("log.priority",["7","15","23","31","39","47","55","63","71","79","87","95","103","111","119","127","135","143","151","159","167","175","183","191"]) + - 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") + - grok: + source: origin.ip + patterns: + - fieldName: log.endpointKey + pattern: (?s)^.+$ + where: equals("log.format","cef") && exists("origin.ip") && !oneOf("origin.ip",["","-"]) && !exists("log.endpointKey") + - add: + function: string + params: + key: log.endpointKeyType + value: ip + where: exists("log.endpointKey") && !exists("log.endpointKeyType") + - grok: + source: origin.host + patterns: + - fieldName: log.endpointKey + pattern: (?s)^.+$ + where: equals("log.format","cef") && exists("origin.host") && !oneOf("origin.host",["","-"]) && !exists("log.endpointKey") + - add: + function: string + params: + key: log.endpointKeyType + value: host + where: exists("log.endpointKey") && !exists("log.endpointKeyType") + - add: + function: string + params: + key: log.correlationCandidate.data_exfiltration_attempts + value: match + where: |- + equals("log.format","cef") && ((equals("log.cat", "NetworkThreat") || + regexMatch("log.cs2", "(?i).*(trojan|backdoor).*") || + regexMatch("log.msg", "(?i).*(data.*transfer|exfiltrat|upload.*suspicious|unauthorized.*transfer).*") || + regexMatch("log.msg", "(?i).*(data.*exfiltration|suspicious.*upload|unauthorized.*transfer).*")) && + exists("target.ip") && + greaterOrEqual("log.cefDeviceSeverity", "3") && exists("origin.ip")) && exists("log.endpointKey") && exists("log.endpointKeyType") && exists("dataSource") + - add: + function: string + params: + key: log.correlationCandidate.kaspersky_ransomware_behavior + value: match + where: |- + equals("log.format","cef") && (exists("log.signatureID") && + (regexMatch("log.msg", "(?i)(ransomware|ransom|trojan-ransom|cryptolocker|locky|cerber|wannacry|ryuk|conti|lockbit|blackcat)") || + contains("log.cs2", "Trojan-Ransom") || contains("log.cs4", "Trojan-Ransom") || + (contains("log.msg", "encrypt") && contains("log.msg", "mass")) || + (contains("log.msg", "System Watcher") && contains("log.msg", "rollback")))) && exists("log.endpointKey") && exists("log.endpointKeyType") && exists("dataSource") + - add: + function: string + params: + key: log.correlationCandidate.lateral_movement_indicators + value: match + where: |- + equals("log.format","cef") && ((equals("log.cn1", "3") || equals("log.cs1", "DETECT") || equals("action", "blocked")) && + (contains("log.msg", ["psexec", "wmi", "rdp", "smb", "admin$", "ipc$", "c$", + "remote", "lateral", "pivot"]) || + contains("log.cs4", ["exploit", "mimikatz", "bloodhound", "sharphound", "propagat"])) && + exists("target.ip") && + exists("origin.ip") && + safe("origin.ip", "") != safe("target.ip", "")) && exists("log.endpointKey") && exists("log.endpointKeyType") && exists("dataSource") + - add: + function: string + params: + key: log.correlationCandidate.suspicious_network_activity + value: match + where: |- + equals("log.format","cef") && (exists("log.signatureID") && + (contains("log.msg", ["suspicious connection", "network attack", "port scan", "unusual traffic"]) || + contains("log.msg", "network") || + contains("log.cs1", "NETWORK") || + contains("log.cs2", "Net-Worm") || + contains("log.cs4", "Net-Worm") || + (exists("target.ip") && exists("log.dpt"))) && + equals("actionResult", "denied") && exists("origin.ip") && exists("target.ip")) && exists("log.endpointKey") && exists("log.endpointKeyType") && exists("dataSource") diff --git a/filters/audits/kaspersky.md b/filters/audits/kaspersky.md index 199f800a4..e28a43149 100644 --- a/filters/audits/kaspersky.md +++ b/filters/audits/kaspersky.md @@ -1,42 +1,121 @@ -# Kaspersky normalization and rule review +# Kaspersky native parsing and CEF consumer review -Keep reporting-agent identity separate from the source; normalize CEF identities, outcomes and severity; correct alert grouping. +Security Center native RFC5424 records were reaching the Kaspersky index with only +`log.priority` parsed. The CEF-only filter did not recognize their `event@23668` +structured data. This revision adds that observed format and repairs the legacy CEF +producer/consumer contract. It replaces the review submission in historical PR #2594. +The authoritative schema is ThreatWinds go-sdk **v1.1.31**, as pinned by v11. -This draft targets UTMStack `v11`. It contains 1 filter changes -and 14 rule changes for this technology only. Review covered -1 filter configurations and 19 matching shipped rule files. -Unchanged rules are listed in the regression manifest; they are not duplicated in the diff. +## Evidence and source identity -## Contract and validation +A completed fleet index inventory identified one carrier, `isspol.utmstack.com`. +Its fresh retained count was 2,981; a separate 30-day raw-token aggregation found +2,738 records containing `KLSRV_HOST_STATUS_CRITICAL` and 126 without that token. +These are different query scopes and captures, not a class-complete population estimate. +Ten deduplicated representatives were selected using latest records, dataSource strata, +and a supplementary noncritical-token stratum. Six describe critical device status; +four describe an unmanaged device. All ten contain native `event@23668` structured +data and neither a CEF nor a LEEF header. -- 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. -- 2 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. +Examples used privately: -The shared alert-contract PR supplies the reusable Go runner for the manifest in -`plugins/alerts/testdata/filter-contracts/kaspersky.json`. Apply that support before running `go test ./...` in `plugins/alerts`. +- `152e263c-ab27-46b1-9e46-b661e7fec8d9`, `v11-log-antivirus-kaspersky-2026-09-17`. +- `f062b03a-09d1-4e66-9aa9-925846217bae`, `v11-log-antivirus-kaspersky-2026-09-02`. -The changed rules also require the shared alert-grouping fix to resolve `lastEvent.*` values correctly at runtime. +Both are on `isspol.utmstack.com`. Customer payloads and device identities are excluded +from this PR. The deployed filter SHA-256 was +`4200885fb3762a61bea1b8fd78d9625037e55dbdf0eadbfa5857b535acda1bb9`. +Field-cap inspection covered 31 retained indices; the requested parsed fields exposed +only priority. The shipped integration guide requests CEF. The evidence establishes an +input-format mismatch at this instance, not a universal vendor format change. -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. +## Mappings and outcomes +Native parsing checks the complete envelope and quoted structured-data syntax before +promoting values. `hip` and `hdn` describe the managed device and populate `target.ip` +and `target.host`; reporting-host and management-server fields stay under `log.ksc`. +No actor is inferred from the relay. Original time populates `deviceTime` when it is +RFC3339, and syslog priority supplies the documented SDK severity values. Event type, +display name, reason and administrative group remain vendor data. An administrative +device group is not assumed to be a security group. Native health events establish +neither an action result nor a successful connection. +CEF headers must start at the beginning of a bare record or supported syslog envelope; +message text cannot become a second header. Vendor/product header strings remain open +for compatibility. After generic KV, 63 consumed vendor keys are reconstructed at CEF +boundaries and protected header/derived fields are restored or cleared. Escaped equals +signs cannot manufacture source IPs. Original CEF values remain available under `log`. +Source/destination IPs are validated before promotion and geolocation; unspecified IPv4, +expanded IPv6 and IPv4-mapped zero addresses stay vendor data. CEF hosts, users, MACs, +ports and protocol map to their standard side when usable. Protocol spelling is retained. +General CEF/structured-data unescaping is not implemented by an invented filter step; +escaped identity text remains vendor data. -## References +Explicit allow decisions map to `success`, explicit block decisions to `denied`, +case-insensitively. Redirect, deletion, termination, quarantine and unknown CEF action +names alone no longer imply a connection outcome. Their original action remains visible. +These changes do not assert that an allowed request established a network session. -- [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) +## Consumers and history + +All 19 shipped rules now explicitly consume the CEF format they interpret. Native health +messages containing malware, task, WMI or connection-related words cannot become attack +candidates. No new health or native-malware rule was invented from this sample. + +CEF consumer repairs include standard source/destination fields, quoted-path `safe` +lookups, normalized block outcomes, `log.msg` rather than an unproduced `log.message`, +and the actual standard `actionResult`. The C2 rule requires a C2 indicator; generic +`NetworkThreat` alone is insufficient. Explicit blocks are excluded for all tested +spellings, while unknown outcomes are described as unknown, not successful C2. + +Four histories preserve their existing thresholds/windows: lateral movement 3/2h, +suspicious network activity 5/30m, ransomware 3/10m, exfiltration 5/30m. Each counts +only candidates for that rule, from the same collector and source-identity namespace. +Source identity prefers a valid IP, then a real source hostname; it never fabricates an +IP. Network history also matches the destination. Exfiltration retains its existing +`NetworkThreat` history restriction. Ordinary endpoint activity cannot fill a threshold. +Input-supplied candidate markers are cleared before deriving them. + +Filter and rules must ship together. New history markers need up to **2 hours** of +warm-up; old events do not contain them. Saved searches depending on remediation actions +being `denied` must use the retained vendor action or explicitly review that migration. +Native IP/host/time/severity coverage will increase. All 19 YAML consumers and the shipped +integration guide were inspected; customer-created dashboards/searches were not exported +or changed. Original CEF fields are retained to reduce compatibility losses. + +## Verification and limits -`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. +- **82 synthetic raw cases** cover native quoting, malformed input, embedded CEF, + reporting/managed-device roles, missing identities, semantic zero IPs, ports, severity, + outcomes, CEF compatibility and positive/negative predicates for every rule. +- Actual SDK v1.1.31 performs CEL evaluation, Event conversion and all four historical + query/threshold suites against a local isolated mock. Tests cover threshold boundaries, + time windows, collector/source/destination scope, unrelated populations and missing keys. +- Shared contract overlay: **112 passing test/subtest records**, no failures or skips, + including the separate private ten-record replay. +- Private raw replay recovers target IP, target host and device time in **10/10** records; + all 19 attack predicates remain false. No actor or action result is invented. +- The extraction tests model explicit YAML operations. They are not the closed + EventProcessor, external geolocation, a live history query or observed alert creation. + +The sample contains no CEF attack telemetry. CEF custom-slot meanings, attack-side +identity semantics and the precision of legacy text heuristics therefore remain +unverified against real vendor attacks. Synthetic compatibility is not proof of those +meanings or of reduced customer alert volume. Official support links redirected to +country/general support pages, so their HTTP 200 responses were not accepted as format +documentation. Other native event classes, other export envelopes, closed-runtime +behavior, parsing cost and alert volume require staging and vendor-format evidence. +No customer configuration was modified; no deployment or merge is authorized here. + +Indexed `lastEvent.*` grouping depends separately on draft +[#2627](https://github.com/utmstack/UTMStack/pull/2627). Its fleet-wide deduplication +rollout and volume measurement must remain separate from this source review. + +## Authority and attempted vendor reference + +- [Pinned SDK schema](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/plugins.proto) +- [SDK field semantics](https://github.com/threatwinds/go-sdk/wiki/Standard-Event-Schema) +- [Filter steps](https://github.com/threatwinds/go-sdk/wiki/Filter-Steps-Reference) +- [Correlation rules](https://github.com/threatwinds/go-sdk/wiki/Implementing-Rules) +- [Kaspersky configured documentation entry](https://support.kaspersky.com/KSC/14/en-US/) + (redirected; log-format content unavailable during this review). diff --git a/plugins/alerts/kaspersky_contract_test.go b/plugins/alerts/kaspersky_contract_test.go new file mode 100644 index 000000000..c614c648f --- /dev/null +++ b/plugins/alerts/kaspersky_contract_test.go @@ -0,0 +1,466 @@ +package main + +// Offline Kaspersky extraction model, not the closed EventProcessor. +// Explicit YAML grok/rename/cast/trim/add/delete and documented 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 kaspFixture 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 kaspPut(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 kaspGet(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 kaspConfig(t *testing.T) *plugins.Config { + t.Helper() + b, e := utils.ReadPbYaml("../../filters/antivirus/kaspersky.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 kaspRegex(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 kaspParse(t *testing.T, cfg *plugins.Config, raw string, dataSource string, cache *plugins.CELCache) string { + t.Helper() + draft := map[string]any{"raw": raw, "dataType": "antivirus-kaspersky", "dataSource": dataSource, "log": map[string]any{}} + for _, stage := range cfg.Pipeline { + matched := false + for _, dataType := range stage.DataTypes { + if dataType == "antivirus-kaspersky" { + 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 := kaspGet(draft, src) + if !ok { + continue + } + str, ok := v.(string) + if !ok { + t.Fatalf("non-string grok source %s", src) + } + r := kaspRegex(t, g, cfg) + m := r.FindStringSubmatch(str) + if m == nil { + continue + } + for i, p := range g.Patterns { + if p.FieldName != "" { + kaspPut(draft, p.FieldName, m[r.SubexpIndex(fmt.Sprintf("f%d", i))], false) + } + } + case "rename": + for _, p := range s.Rename.From { + if v, ok := kaspGet(draft, p); ok { + kaspPut(draft, s.Rename.To, v, false) + kaspPut(draft, p, nil, true) + break + } + } + case "trim": + for _, p := range s.Trim.Fields { + if v, ok := kaspGet(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) + } + kaspPut(draft, p, str, false) + } + } + case "add": + if s.Add.Function != "string" { + t.Fatalf("unsupported add function %s", s.Add.Function) + } + kaspPut(draft, s.Add.Params["key"].GetStringValue(), s.Add.Params["value"].AsInterface(), false) + case "delete": + for _, p := range s.Delete.Fields { + kaspPut(draft, p, nil, true) + } + case "kv": + v, ok := kaspGet(draft, s.Kv.Source) + if !ok { + continue + } + // The model splits KV on the configured separators. Live Kaspersky CEF was not sampled. + // 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 != "" { + kaspPut(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 := kaspGet(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 := kaspGet(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 kaspSanitizeJSON(parsed) { + kaspPut(draft, "log."+key, value, false) + } + case "cast": + for _, field := range s.Cast.Fields { + if value, ok := kaspGet(draft, field); ok { + switch s.Cast.To { + case "string": + kaspPut(draft, field, utils.CastString(value), false) + case "int": + kaspPut(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 kaspRules(t *testing.T) map[string]*plugins.Rule { + t.Helper() + paths, e := filepath.Glob("../../rules/antivirus/kaspersky/*.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 kaspSanitizeJSON(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 = kaspSanitizeJSON(nested) + } + out[key] = value + } + return out +} + +func kaspFixtures(t *testing.T) []kaspFixture { + t.Helper() + b, e := os.ReadFile("testdata/kaspersky_raw.json") + if e != nil { + t.Fatal(e) + } + var cases []kaspFixture + if e = json.Unmarshal(b, &cases); e != nil { + t.Fatal(e) + } + return cases +} + +func TestKasperskyRawContracts(t *testing.T) { + cfg, rules, cache := kaspConfig(t), kaspRules(t), plugins.NewCELCache("kaspersky-raw") + positive := map[string]int{} + negative := map[string]int{} + if len(rules) != 19 { + t.Fatalf("rules: %d", len(rules)) + } + for _, f := range kaspFixtures(t) { + t.Run(f.Name, func(t *testing.T) { + out := kaspParse(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 TestKasperskyPrivateReplay(t *testing.T) { + p := os.Getenv("KASPERSKY_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 := kaspConfig(t), kaspRules(t), plugins.NewCELCache("kaspersky-private") + results := []map[string]any{} + for _, d := range docs { + out := kaspParse(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("KASPERSKY_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/kaspersky_history_test.go b/plugins/alerts/kaspersky_history_test.go new file mode 100644 index 000000000..155c6d12a --- /dev/null +++ b/plugins/alerts/kaspersky_history_test.go @@ -0,0 +1,251 @@ +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 TestKasperskySDKHistory(t *testing.T) { + if os.Getenv("UTM_KASPERSKY_HISTORY_CHILD") != "1" { + c := exec.Command(os.Args[0], "-test.run=^TestKasperskySDKHistory$") + c.Env = append(os.Environ(), "UTM_KASPERSKY_HISTORY_CHILD=1") + if b, e := c.CombinedOutput(); e != nil { + t.Fatalf("isolated history: %v\n%s", e, b) + } + return + } + cfg, rules, cache := kaspConfig(t), kaspRules(t), plugins.NewCELCache("kasp-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.endpointKeyType", "log.endpointKey", "origin.ip", "target.ip", "log.cat"} + 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-kaspersky-test": map[string]any{"mappings": mapping}}) + return + } + if r.URL.Path != "/v11-log-antivirus-kaspersky-*/_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-kaspersky-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) + } + kaspPut(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 + }{ + {"lateral_movement_indicators", "lateral_movement_indicators", "2h", 3, false, nil}, + {"suspicious_network_activity", "suspicious_network_activity", "30m", 5, false, map[string]string{"target.ip": "192.0.2.8"}}, + {"kaspersky_ransomware_behavior", "kaspersky_ransomware_behavior", "10m", 3, false, nil}, + {"data_exfiltration_attempts", "data_exfiltration_attempts", "30m", 5, false, map[string]string{"log.cat": "NetworkThreat"}}, + } + fixtures := map[string]kaspFixture{} + for _, f := range kaspFixtures(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 := kaspParse(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.endpointKeyType": "ip", marker: "match"} + notTerms = map[string]string{} + if tc.cross { + notTerms["log.endpointKey"] = "198.51.100.9" + } else { + terms["log.endpointKey"] = "198.51.100.9" + } + 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.endpointKeyType"} { + check("different_"+field, mutate(prior, field, "other"), tc.count, false) + } + if tc.cross { + check("same_endpoint", mutate(prior, "log.endpointKey", "198.51.100.9"), 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) + } + 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/filter-contracts/kaspersky.json b/plugins/alerts/testdata/filter-contracts/kaspersky.json index 0c1686702..d2150baad 100644 --- a/plugins/alerts/testdata/filter-contracts/kaspersky.json +++ b/plugins/alerts/testdata/filter-contracts/kaspersky.json @@ -26,51 +26,45 @@ ], "fixtures": [ { - "name": "Kaspersky agent must not overwrite sender", + "name": "Kaspersky derived format and history cannot be supplied by input", "filter": "antivirus/kaspersky.yml", "input": { + "dataType": "antivirus-kaspersky", "log": { - "src": "198.51.100.10", - "dst": "10.0.0.8", - "agt": "10.0.0.2", - "originalAgentAddress": "10.0.0.3", - "syslogIpHost": "10.0.0.4" + "format": "cef", + "endpointKey": "forged", + "correlationCandidate": { + "kaspersky_ransomware_behavior": "match" + } } }, - "expected": { - "origin.ip": "198.51.100.10", - "target.ip": "10.0.0.8", - "log.agentAddress": "10.0.0.2" - }, - "absent": [], - "rules": {} - }, - { - "name": "Kaspersky CEF side identities", - "filter": "antivirus/kaspersky.yml", - "input": { - "log": { - "shost": "client", - "suser": "alice", - "smac": "00:11:22:33:44:55", - "dhost": "server", - "duser": "bob", - "ahost": "manager", - "agt": "10.0.0.99", - "cefDeviceSeverity": "10" - } - }, - "expected": { - "origin.host": "client", - "origin.user": "alice", - "target.host": "server", - "target.user": "bob", - "log.agentHost": "manager", - "severity": "critical" - }, + "expected": {}, "absent": [ - "origin.ip" - ] + "log.format", + "log.endpointKey", + "log.correlationCandidate" + ], + "rules": { + "rules/antivirus/kaspersky/code_injection_attempts.yml": false, + "rules/antivirus/kaspersky/command_and_control_communication.yml": false, + "rules/antivirus/kaspersky/critical_object_detected.yml": false, + "rules/antivirus/kaspersky/data_exfiltration_attempts.yml": false, + "rules/antivirus/kaspersky/kaspersky_agent_tampering.yml": false, + "rules/antivirus/kaspersky/kaspersky_ransomware_behavior.yml": false, + "rules/antivirus/kaspersky/kaspersky_rootkit_detection.yml": false, + "rules/antivirus/kaspersky/lateral_movement_indicators.yml": false, + "rules/antivirus/kaspersky/lolbins_abuse.yml": false, + "rules/antivirus/kaspersky/privilege_escalation_attempts.yml": false, + "rules/antivirus/kaspersky/process_hollowing_detection.yml": false, + "rules/antivirus/kaspersky/sandbox_evasion_attempts.yml": false, + "rules/antivirus/kaspersky/suspicious_network_activity.yml": false, + "rules/antivirus/kaspersky/suspicious_packed_executables.yml": false, + "rules/antivirus/kaspersky/suspicious_scheduled_tasks.yml": false, + "rules/antivirus/kaspersky/suspicious_service_installation.yml": false, + "rules/antivirus/kaspersky/system_file_tampering_detection.yml": false, + "rules/antivirus/kaspersky/trusted_application_compromise.yml": false, + "rules/antivirus/kaspersky/wmi_abuse_detection.yml": false + } } ] } diff --git a/plugins/alerts/testdata/kaspersky_raw.json b/plugins/alerts/testdata/kaspersky_raw.json new file mode 100644 index 000000000..ed147df0b --- /dev/null +++ b/plugins/alerts/testdata/kaspersky_raw.json @@ -0,0 +1,1065 @@ +[ + { + "name": "KLSRV_HOST_STATUS_CRITICAL", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "log.format": "ksc-syslog", + "log.eventType": "KLSRV_HOST_STATUS_CRITICAL", + "target.ip": "192.0.2.8", + "target.host": "endpoint-test", + "deviceTime": "2026-09-17T12:34:56Z", + "log.ksc.reportingHost": "relay.example.test", + "log.ksc.kscfqdn": "manager.example.test", + "log.ksc.gn": "Managed endpoints", + "log.msg": "Device has not connected recently.", + "severity": "critical" + }, + "absent": [ + "origin.ip", + "origin.host", + "target.group", + "action", + "actionResult", + "log.signatureID" + ], + "matches": [] + }, + { + "name": "KLSRV_HOST_OUT_CONTROL", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_OUT_CONTROL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_OUT_CONTROL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "log.format": "ksc-syslog", + "log.eventType": "KLSRV_HOST_OUT_CONTROL", + "target.ip": "192.0.2.8", + "target.host": "endpoint-test", + "deviceTime": "2026-09-17T12:34:56Z", + "log.ksc.reportingHost": "relay.example.test", + "log.ksc.kscfqdn": "manager.example.test", + "log.ksc.gn": "Managed endpoints", + "log.msg": "Device has not connected recently.", + "severity": "critical" + }, + "absent": [ + "origin.ip", + "origin.host", + "target.group", + "action", + "actionResult", + "log.signatureID" + ], + "matches": [] + }, + { + "name": "health message cannot become CEF 2", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] ransomware rootkit network attack scheduled task powershell malicious lsass injection WMI", + "dataSource": "collector-test", + "expected": { + "log.format": "ksc-syslog", + "target.ip": "192.0.2.8" + }, + "absent": [ + "origin.ip", + "actionResult", + "log.signatureID" + ], + "matches": [] + }, + { + "name": "health message cannot become CEF 3", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 act=Allowed msg=botnet", + "dataSource": "collector-test", + "expected": { + "log.format": "ksc-syslog", + "target.ip": "192.0.2.8" + }, + "absent": [ + "origin.ip", + "actionResult", + "log.signatureID" + ], + "matches": [] + }, + { + "name": "quoted text cannot inject IP", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"failure hip=\\\"198.51.100.9\\\"\" et=\"KLSRV_HOST_STATUS_CRITICAL\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Group \\\\ Team\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.ip": "192.0.2.8", + "log.ksc.gn": "Group \\\\ Team" + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "escaped delimiter", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"message \\] quoted \\\"value\\\"\" hip=\"192.0.2.8\" hdn=\"endpoint-test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.ip": "192.0.2.8" + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "native invalid or unspecified 0.0.0.0", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 hip=\"0.0.0.0\" hdn=\"endpoint-test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.host": "endpoint-test", + "log.ksc.hip": "0.0.0.0" + }, + "absent": [ + "target.ip", + "origin.ip" + ], + "matches": [] + }, + { + "name": "native invalid or unspecified ::", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 hip=\"::\" hdn=\"endpoint-test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.host": "endpoint-test", + "log.ksc.hip": "::" + }, + "absent": [ + "target.ip", + "origin.ip" + ], + "matches": [] + }, + { + "name": "native invalid or unspecified 0:0:0:0:0:0:0:0", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 hip=\"0:0:0:0:0:0:0:0\" hdn=\"endpoint-test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.host": "endpoint-test", + "log.ksc.hip": "0:0:0:0:0:0:0:0" + }, + "absent": [ + "target.ip", + "origin.ip" + ], + "matches": [] + }, + { + "name": "native invalid or unspecified ::ffff:0.0.0.0", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 hip=\"::ffff:0.0.0.0\" hdn=\"endpoint-test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.host": "endpoint-test", + "log.ksc.hip": "::ffff:0.0.0.0" + }, + "absent": [ + "target.ip", + "origin.ip" + ], + "matches": [] + }, + { + "name": "native invalid or unspecified endpoint-test", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 hip=\"endpoint-test\" hdn=\"endpoint-test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.host": "endpoint-test", + "log.ksc.hip": "endpoint-test" + }, + "absent": [ + "target.ip", + "origin.ip" + ], + "matches": [] + }, + { + "name": "native invalid or unspecified 999.1.1.1", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 hip=\"999.1.1.1\" hdn=\"endpoint-test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.host": "endpoint-test", + "log.ksc.hip": "999.1.1.1" + }, + "absent": [ + "target.ip", + "origin.ip" + ], + "matches": [] + }, + { + "name": "native invalid or unspecified -", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 hip=\"-\" hdn=\"endpoint-test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.host": "endpoint-test", + "log.ksc.hip": "-" + }, + "absent": [ + "target.ip", + "origin.ip" + ], + "matches": [] + }, + { + "name": "native IPv6", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 hip=\"2001:db8::8\" hdn=\"endpoint-test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.ip": "2001:db8::8" + }, + "absent": [], + "matches": [] + }, + { + "name": "native parameter order", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 gn=\"Group one\" hip=\"192.0.2.8\" p1=\"message text\" hdn=\"endpoint-test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.ip": "192.0.2.8", + "target.host": "endpoint-test", + "log.ksc.p1": "message text" + }, + "absent": [], + "matches": [] + }, + { + "name": "native absent address", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 hdn=\"endpoint-test\" et=\"KLSRV_HOST_OUT_CONTROL\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.host": "endpoint-test" + }, + "absent": [ + "target.ip", + "origin.ip", + "actionResult" + ], + "matches": [] + }, + { + "name": "native escaped host retained", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 hdn=\"endpoint\\\"test\" hip=\"192.0.2.8\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "target.ip": "192.0.2.8", + "log.ksc.hdn": "endpoint\\\"test" + }, + "absent": [ + "target.host" + ], + "matches": [] + }, + { + "name": "native NIL timestamp", + "raw": "<10>1 - relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "log.ksc.time": "-" + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "native syslog severity 11", + "raw": "<11>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "severity": "error" + }, + "absent": [], + "matches": [] + }, + { + "name": "native syslog severity 12", + "raw": "<12>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "severity": "warning" + }, + "absent": [], + "matches": [] + }, + { + "name": "native syslog severity 13", + "raw": "<13>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "severity": "info" + }, + "absent": [], + "matches": [] + }, + { + "name": "native syslog severity 14", + "raw": "<14>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "severity": "info" + }, + "absent": [], + "matches": [] + }, + { + "name": "native syslog severity 15", + "raw": "<15>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": { + "severity": "debug" + }, + "absent": [], + "matches": [] + }, + { + "name": "reject malformed native 23", + "raw": "prefix <10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": {}, + "absent": [ + "log.format", + "target.ip", + "origin.ip", + "actionResult" + ], + "matches": [] + }, + { + "name": "reject malformed native 24", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@99999 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": {}, + "absent": [ + "log.format", + "target.ip", + "origin.ip", + "actionResult" + ], + "matches": [] + }, + { + "name": "reject malformed native 25", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8 gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": {}, + "absent": [ + "log.format", + "target.ip", + "origin.ip", + "actionResult" + ], + "matches": [] + }, + { + "name": "reject malformed native 26", + "raw": "<10>1 2026-09-17T12:34:56Z relay.example.test 1093|1.0.0.0 - KLSRV_HOST_STATUS_CRITICAL [event@23668 p1=\"Device ] not connected\" et=\"KLSRV_HOST_STATUS_CRITICAL\" etdn=\"Device status is Critical.\" hdn=\"endpoint-test\" hip=\"192.0.2.8\" gn=\"Managed endpoints\" kscfqdn=\"manager.example.test\"] Device has not connected recently.", + "dataSource": "collector-test", + "expected": {}, + "absent": [ + "log.format", + "target.ip", + "origin.ip", + "actionResult" + ], + "matches": [] + }, + { + "name": "code_injection_attempts", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test msg=injection into lsass", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "code_injection_attempts" + ] + }, + { + "name": "command_and_control_communication", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test msg=beacon detected", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "command_and_control_communication" + ] + }, + { + "name": "critical_object_detected", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|8|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test cs1=MALWARE", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "critical_object_detected" + ] + }, + { + "name": "data_exfiltration_attempts", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test cat=NetworkThreat", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "data_exfiltration_attempts" + ] + }, + { + "name": "kaspersky_agent_tampering", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test msg=protection disabled", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "kaspersky_agent_tampering" + ] + }, + { + "name": "kaspersky_ransomware_behavior", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test msg=ransomware identified", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "kaspersky_ransomware_behavior" + ] + }, + { + "name": "kaspersky_rootkit_detection", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test msg=rootkit identified", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "kaspersky_rootkit_detection" + ] + }, + { + "name": "lateral_movement_indicators", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test cs1=DETECT msg=psexec", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "lateral_movement_indicators" + ] + }, + { + "name": "lolbins_abuse", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test cs1=DETECT msg=certutil download", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "lolbins_abuse" + ] + }, + { + "name": "privilege_escalation_attempts", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test msg=token manipulation", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "privilege_escalation_attempts" + ] + }, + { + "name": "process_hollowing_detection", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test cs1=DETECT msg=hollow", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "process_hollowing_detection" + ] + }, + { + "name": "sandbox_evasion_attempts", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test msg=sandbox evasion", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "sandbox_evasion_attempts" + ] + }, + { + "name": "suspicious_network_activity", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Blocked msg=network attack", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "suspicious_network_activity" + ] + }, + { + "name": "suspicious_packed_executables", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=blocked msg=Packed executable", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "suspicious_packed_executables" + ] + }, + { + "name": "suspicious_scheduled_tasks", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test cs1=suspicious msg=scheduled task", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "suspicious_scheduled_tasks" + ] + }, + { + "name": "suspicious_service_installation", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test cs1=suspicious msg=Service install", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "suspicious_service_installation" + ] + }, + { + "name": "system_file_tampering_detection", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test msg=critical file changed", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "system_file_tampering_detection" + ] + }, + { + "name": "trusted_application_compromise", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|Medium|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test cs1=TRUSTED_COMP", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "trusted_application_compromise" + ] + }, + { + "name": "wmi_abuse_detection", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test msg=WMI", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.format": "cef" + }, + "absent": [], + "matches": [ + "wmi_abuse_detection" + ] + }, + { + "name": "CEF outcome Allow", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Allow", + "dataSource": "collector-test", + "expected": { + "action": "Allow", + "actionResult": "success" + }, + "absent": [], + "matches": [] + }, + { + "name": "CEF outcome Allowed", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Allowed", + "dataSource": "collector-test", + "expected": { + "action": "Allowed", + "actionResult": "success" + }, + "absent": [], + "matches": [] + }, + { + "name": "CEF outcome BLOCKED", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=BLOCKED", + "dataSource": "collector-test", + "expected": { + "action": "BLOCKED", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "CEF outcome Block", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Block", + "dataSource": "collector-test", + "expected": { + "action": "Block", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "CEF outcome blocked", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=blocked", + "dataSource": "collector-test", + "expected": { + "action": "blocked", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "CEF outcome delete", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=delete", + "dataSource": "collector-test", + "expected": { + "action": "delete" + }, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "CEF outcome terminate", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=terminate", + "dataSource": "collector-test", + "expected": { + "action": "terminate" + }, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "CEF outcome quarantine", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=quarantine", + "dataSource": "collector-test", + "expected": { + "action": "quarantine" + }, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "CEF outcome Redirect", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Redirect", + "dataSource": "collector-test", + "expected": { + "action": "Redirect" + }, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "CEF outcome detected", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=detected", + "dataSource": "collector-test", + "expected": { + "action": "detected" + }, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "CEF outcome unknown", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=unknown", + "dataSource": "collector-test", + "expected": { + "action": "unknown" + }, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "C2 explicit block Block", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Block msg=beacon", + "dataSource": "collector-test", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "C2 explicit block Blocked", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Blocked msg=beacon", + "dataSource": "collector-test", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "C2 explicit block BLOCKED", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=BLOCKED msg=beacon", + "dataSource": "collector-test", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "C2 explicit block blocked", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=blocked msg=beacon", + "dataSource": "collector-test", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "C2 unknown outcome is not success", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test msg=beacon", + "dataSource": "collector-test", + "expected": {}, + "absent": [ + "actionResult" + ], + "matches": [ + "command_and_control_communication" + ] + }, + { + "name": "generic network category is not C2", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|1|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test cat=NetworkThreat", + "dataSource": "collector-test", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "CEF invalid address 0.0.0.0", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=0.0.0.0 dst=0.0.0.0", + "dataSource": "collector-test", + "expected": { + "log.src": "0.0.0.0", + "log.dst": "0.0.0.0" + }, + "absent": [ + "origin.ip", + "target.ip" + ], + "matches": [] + }, + { + "name": "CEF invalid address 0:0:0:0:0:0:0:0", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=0:0:0:0:0:0:0:0 dst=0:0:0:0:0:0:0:0", + "dataSource": "collector-test", + "expected": { + "log.src": "0:0:0:0:0:0:0:0", + "log.dst": "0:0:0:0:0:0:0:0" + }, + "absent": [ + "origin.ip", + "target.ip" + ], + "matches": [] + }, + { + "name": "CEF invalid address ::ffff:0.0.0.0", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=::ffff:0.0.0.0 dst=::ffff:0.0.0.0", + "dataSource": "collector-test", + "expected": { + "log.src": "::ffff:0.0.0.0", + "log.dst": "::ffff:0.0.0.0" + }, + "absent": [ + "origin.ip", + "target.ip" + ], + "matches": [] + }, + { + "name": "CEF invalid address host.example.test", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=host.example.test dst=host.example.test", + "dataSource": "collector-test", + "expected": { + "log.src": "host.example.test", + "log.dst": "host.example.test" + }, + "absent": [ + "origin.ip", + "target.ip" + ], + "matches": [] + }, + { + "name": "CEF invalid address 999.1.1.1", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=999.1.1.1 dst=999.1.1.1", + "dataSource": "collector-test", + "expected": { + "log.src": "999.1.1.1", + "log.dst": "999.1.1.1" + }, + "absent": [ + "origin.ip", + "target.ip" + ], + "matches": [] + }, + { + "name": "CEF reporter identity separate", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test agt=192.0.2.99 ahost=manager-test amac=00:11:22:33:44:55", + "dataSource": "collector-test", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.agentAddress": "192.0.2.99", + "log.agentHost": "manager-test" + }, + "absent": [], + "matches": [] + }, + { + "name": "CEF escaped equals not a key", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|msg=text src\\=198.51.100.99 dst=192.0.2.8", + "dataSource": "collector-test", + "expected": { + "log.msg": "text src\\=198.51.100.99", + "target.ip": "192.0.2.8" + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "CEF header fields protected", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|1|signatureID=999 cefDeviceSeverity=10 msg=ordinary event", + "dataSource": "collector-test", + "expected": { + "log.signatureID": "3", + "log.cefDeviceSeverity": "1", + "severity": "info" + }, + "absent": [], + "matches": [] + }, + { + "name": "CEF spoofed derived marker removed", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|endpointKey=spoof correlationCandidate=spoof format=ksc-syslog msg=ordinary event", + "dataSource": "collector-test", + "expected": { + "log.format": "cef" + }, + "absent": [ + "log.endpointKey", + "log.correlationCandidate" + ], + "matches": [] + }, + { + "name": "CEF ports", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test spt=49152 dpt=443 proto=TCP", + "dataSource": "collector-test", + "expected": { + "origin.port": 49152, + "target.port": 443, + "protocol": "TCP" + }, + "absent": [], + "matches": [] + }, + { + "name": "CEF invalid ports", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|spt=65536 dpt=not-a-port", + "dataSource": "collector-test", + "expected": {}, + "absent": [ + "origin.port", + "target.port" + ], + "matches": [] + }, + { + "name": "CEF wrapper 74", + "raw": "<10>CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Blocked", + "dataSource": "collector-test", + "expected": { + "log.format": "cef", + "origin.ip": "198.51.100.9", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "CEF wrapper 75", + "raw": "<10>1 2026-09-17T12:00:00Z relay-test KSC - - - CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Blocked", + "dataSource": "collector-test", + "expected": { + "log.format": "cef", + "origin.ip": "198.51.100.9", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "CEF wrapper 76", + "raw": "<10>Sep 17 12:00:00 relay-test KSC: CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Blocked", + "dataSource": "collector-test", + "expected": { + "log.format": "cef", + "origin.ip": "198.51.100.9", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "embedded CEF rejected 77", + "raw": "unrelated message CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Allowed", + "dataSource": "collector-test", + "expected": {}, + "absent": [ + "log.format", + "actionResult", + "origin.ip" + ], + "matches": [] + }, + { + "name": "embedded CEF rejected 78", + "raw": "<10>1 2026-09-17T12:00:00Z relay-test app - - [meta x=\"CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 shost=source-test dhost=target-test act=Allowed", + "dataSource": "collector-test", + "expected": {}, + "absent": [ + "log.format", + "actionResult", + "origin.ip" + ], + "matches": [] + }, + { + "name": "ransomware source host fallback", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|shost=source-test msg=ransomware", + "dataSource": "collector-test", + "expected": { + "log.endpointKeyType": "host", + "log.endpointKey": "source-test" + }, + "absent": [ + "origin.ip" + ], + "matches": [ + "kaspersky_ransomware_behavior" + ] + }, + { + "name": "ransomware no invented source identity", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|msg=ransomware", + "dataSource": "collector-test", + "expected": {}, + "absent": [ + "origin.ip", + "log.endpointKey" + ], + "matches": [] + }, + { + "name": "legacy CEF vendor spelling retained", + "raw": "CEF:0|KasperskyLab|SecurityCenter|14|3|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 act=Blocked", + "dataSource": "collector-test", + "expected": { + "log.embDeviceVendor": "KasperskyLab", + "origin.ip": "198.51.100.9", + "actionResult": "denied" + }, + "absent": [], + "matches": [] + } +] diff --git a/rules/antivirus/kaspersky/code_injection_attempts.yml b/rules/antivirus/kaspersky/code_injection_attempts.yml index b267d25b0..adae64764 100644 --- a/rules/antivirus/kaspersky/code_injection_attempts.yml +++ b/rules/antivirus/kaspersky/code_injection_attempts.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Code Injection Attempt Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion, Privilege Escalation -technique: "T1055 - Process Injection" +technique: T1055 - Process Injection adversary: origin references: - - https://attack.mitre.org/techniques/T1055/ - - https://support.kaspersky.com/KESWin/11/en-us/151065.htm +- https://attack.mitre.org/techniques/T1055/ +- https://support.kaspersky.com/KESWin/11/en-us/151065.htm description: | Detects attempts to inject malicious code into legitimate processes. This technique is commonly used by malware to evade detection and gain elevated privileges by running within trusted processes. - + Next Steps: 1. Immediately isolate the affected system to prevent lateral movement 2. Identify the source process that attempted the injection @@ -25,11 +24,10 @@ description: | 6. Check for persistence mechanisms (scheduled tasks, registry keys, services) 7. Review network connections from the affected host for C2 communications 8. Consider reimaging the system if critical processes were compromised -where: | - (regexMatch("log.msg", "(?i).*(inject|injection|CreateRemoteThread|SetWindowsHookEx|WriteProcessMemory).*") || - (contains("log.cs4", ["inject", "hooking", "trojan", "backdoor"]) && - contains("action", ["terminate", "delete", "quarantine"]))) && - contains("log.msg", ["lsass", "csrss", "winlogon", "services", "svchost", "explorer"]) +where: "equals(\"log.format\",\"cef\") && ((regexMatch(\"log.msg\", \"(?i).*(inject|injection|CreateRemoteThread|SetWindowsHookEx|WriteProcessMemory).*\"\ + ) ||\n (contains(\"log.cs4\", [\"inject\", \"hooking\", \"trojan\", \"backdoor\"]) && \n contains(\"action\", [\"terminate\", \"\ + delete\", \"quarantine\"]))) &&\ncontains(\"log.msg\", [\"lsass\", \"csrss\", \"winlogon\", \"services\", \"svchost\", \"explorer\"\ + ]))\n" deduplicateBy: - - adversary.host - - lastEvent.log.cs4 +- adversary.host +- lastEvent.log.cs4 diff --git a/rules/antivirus/kaspersky/command_and_control_communication.yml b/rules/antivirus/kaspersky/command_and_control_communication.yml index 501487834..1c878a1bd 100644 --- a/rules/antivirus/kaspersky/command_and_control_communication.yml +++ b/rules/antivirus/kaspersky/command_and_control_communication.yml @@ -1,36 +1,24 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Command and Control Communication Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Command and Control -technique: "T1071 - Application Layer Protocol" +technique: T1071 - Application Layer Protocol adversary: origin references: - - https://attack.mitre.org/techniques/T1071/ - - https://support.kaspersky.com/KLMS/8.2/en-US/151504.htm +- https://attack.mitre.org/techniques/T1071/ +- https://support.kaspersky.com/KLMS/8.2/en-US/151504.htm description: | - Detects potential command and control (C2) communication attempts identified by Kaspersky, including suspicious outbound connections, malware callbacks, and botnet communication patterns. This rule triggers when Kaspersky identifies network threats related to botnet activity, C2 communications, or malware beaconing that was not successfully blocked. - - Next Steps: - 1. Immediately isolate the affected system from the network to prevent further C2 communication - 2. Review the target IP address against threat intelligence feeds to confirm malicious activity - 3. Check if other systems have communicated with the same C2 server - 4. Analyze the process or malware that initiated the connection - 5. Review Kaspersky logs for additional context about the threat - 6. Perform a full system scan and forensic analysis on the affected machine - 7. Update antivirus signatures and ensure real-time protection is enabled - 8. Consider reimaging the system if compromise is confirmed + Reports Kaspersky CEF command-and-control indicators without an explicit block outcome. Missing or unknown actions do not prove that a connection succeeded. Confirm the vendor event, source/destination roles, process and traffic before containment. A generic NetworkThreat category alone is insufficient. where: | - (contains("log.cs2", ["Bot", "bot", "C2", "Command", "command"]) || - contains("log.msg", ["callback", "beacon", "botnet", "command and control"]) || - equals("log.cat", "NetworkThreat")) && + equals("log.format","cef") && ((contains("log.cs2", ["Bot", "bot", "C2", "Command", "command"]) || + contains("log.msg", ["callback", "beacon", "botnet", "command and control"])) && exists("target.ip") && - action != "blocked" && action != "Blocked" + !equals("actionResult", "denied")) groupBy: - - adversary.host - - target.ip +- adversary.host +- target.ip diff --git a/rules/antivirus/kaspersky/critical_object_detected.yml b/rules/antivirus/kaspersky/critical_object_detected.yml index 36c86459b..eb0ec2d11 100644 --- a/rules/antivirus/kaspersky/critical_object_detected.yml +++ b/rules/antivirus/kaspersky/critical_object_detected.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Critical Object Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Execution -technique: "T1204 - User Execution: Malicious File" +technique: 'T1204 - User Execution: Malicious File' adversary: origin references: - - https://support.kaspersky.com/ScanEngine/1.0/en-US/186767.htm - - https://attack.mitre.org/techniques/T1204/ +- https://support.kaspersky.com/ScanEngine/1.0/en-US/186767.htm +- https://attack.mitre.org/techniques/T1204/ description: | Detects when Kaspersky identifies critical threats including malware, trojans, or other dangerous objects that require immediate attention. High severity detections often indicate active threats. - + Next Steps: 1. Immediately isolate the affected system from the network to prevent lateral movement 2. Identify the malware name/signature from log.cs1, log.cs2, or log.cs4 fields @@ -25,14 +24,10 @@ description: | 6. Collect and preserve forensic artifacts if needed for incident response 7. Update antivirus signatures and run a full system scan 8. Consider reimaging the system if the infection is severe or persistent -where: | - (greaterOrEqual("log.cefDeviceSeverity", "7") || - equalsIgnoreCase("log.cefDeviceSeverity", "High") || - equalsIgnoreCase("log.cefDeviceSeverity", "Very-High")) && - (contains("log.cs1", ["INFECTED", "MALWARE", "TROJAN"]) || - contains("log.cs2", ["Trojan", "HEUR:", "PDM:", "UDS:"]) || - contains("log.cs4", ["Trojan", "HEUR:", "PDM:", "UDS:"]) || - contains("log.msg", ["infected", "malicious", "dangerous"])) +where: "equals(\"log.format\",\"cef\") && ((greaterOrEqual(\"log.cefDeviceSeverity\", \"7\") || \n equalsIgnoreCase(\"log.cefDeviceSeverity\"\ + , \"High\") ||\n equalsIgnoreCase(\"log.cefDeviceSeverity\", \"Very-High\")) &&\n(contains(\"log.cs1\", [\"INFECTED\", \"MALWARE\"\ + , \"TROJAN\"]) ||\n contains(\"log.cs2\", [\"Trojan\", \"HEUR:\", \"PDM:\", \"UDS:\"]) ||\n contains(\"log.cs4\", [\"Trojan\", \"\ + HEUR:\", \"PDM:\", \"UDS:\"]) ||\n contains(\"log.msg\", [\"infected\", \"malicious\", \"dangerous\"])))\n" groupBy: - - adversary.host - - lastEvent.log.signatureID +- adversary.host +- lastEvent.log.signatureID diff --git a/rules/antivirus/kaspersky/data_exfiltration_attempts.yml b/rules/antivirus/kaspersky/data_exfiltration_attempts.yml index e01799d05..e42ae55b0 100644 --- a/rules/antivirus/kaspersky/data_exfiltration_attempts.yml +++ b/rules/antivirus/kaspersky/data_exfiltration_attempts.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Data Exfiltration Attempts Detection impact: confidentiality: 3 integrity: 2 availability: 1 category: Exfiltration -technique: "T1048 - Exfiltration Over Alternative Protocol" +technique: T1048 - Exfiltration Over Alternative Protocol adversary: origin references: - - https://attack.mitre.org/techniques/T1048/ - - https://support.kaspersky.com/KLMS/8.2/en-US/151684.htm +- https://attack.mitre.org/techniques/T1048/ +- https://support.kaspersky.com/KLMS/8.2/en-US/151684.htm description: | Detects potential data exfiltration attempts identified by Kaspersky through suspicious network traffic patterns, large data transfers, or connections to suspicious external destinations. This rule monitors for network threats, trojan/backdoor detections, and suspicious data transfer patterns that may indicate data exfiltration. - + Next Steps: 1. Immediately identify the source host (origin.ip) and any associated user accounts on the affected system 2. Check if the destination IP (target.ip) is known malicious using threat intelligence sources @@ -29,24 +28,35 @@ description: | - Initiate full incident response procedures - Preserve evidence for forensic analysis 8. Document all findings and actions taken for compliance and future reference + + History counts only matching CEF candidates from the same collector and source identity. where: | - (equals("log.cat", "NetworkThreat") || + equals("log.format","cef") && ((equals("log.cat", "NetworkThreat") || regexMatch("log.cs2", "(?i).*(trojan|backdoor).*") || regexMatch("log.msg", "(?i).*(data.*transfer|exfiltrat|upload.*suspicious|unauthorized.*transfer).*") || regexMatch("log.msg", "(?i).*(data.*exfiltration|suspicious.*upload|unauthorized.*transfer).*")) && exists("target.ip") && - greaterOrEqual("log.cefDeviceSeverity", "3") -afterEvents: - - indexPattern: v11-log-antivirus-kaspersky-* - with: - - field: origin.ip - operator: filter_term - value: '{{.origin.ip}}' - - field: log.cat - operator: filter_term - value: NetworkThreat - within: 30m - count: 5 + greaterOrEqual("log.cefDeviceSeverity", "3") && exists("origin.ip")) && exists("log.endpointKey") && exists("log.endpointKeyType") && exists("dataSource") groupBy: - - adversary.ip - - target.ip +- adversary.ip +- target.ip +correlation: +- indexPattern: v11-log-antivirus-kaspersky-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: filter_term + value: '{{.log.endpointKey}}' + - field: log.correlationCandidate.data_exfiltration_attempts + operator: filter_term + value: match + - field: log.cat + operator: filter_term + value: NetworkThreat + within: 30m + count: 5 diff --git a/rules/antivirus/kaspersky/kaspersky_agent_tampering.yml b/rules/antivirus/kaspersky/kaspersky_agent_tampering.yml index f0d2ded8c..6199d24d1 100644 --- a/rules/antivirus/kaspersky/kaspersky_agent_tampering.yml +++ b/rules/antivirus/kaspersky/kaspersky_agent_tampering.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Agent Disabled or Tampered impact: confidentiality: 3 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://support.kaspersky.com/ - - https://attack.mitre.org/techniques/T1562/001/ +- https://support.kaspersky.com/ +- https://attack.mitre.org/techniques/T1562/001/ description: | Detects when the Kaspersky security agent is disabled, stopped, or tampered with. This is a critical indicator of defense evasion as attackers disable endpoint protection to execute malware undetected. @@ -24,8 +23,8 @@ description: | 5. Perform a full system scan 6. Check for similar events on other endpoints where: | - exists("log.signatureID") && + equals("log.format","cef") && (exists("log.signatureID") && (regexMatch("log.msg", "(?i)(kaspersky|klnagent|kavfs|kesl).*( disabled| stopped| removed| tampered| uninstalled)") || regexMatch("log.msg", "(?i)(protection|self-defense).*(disabled|off|stopped)") || - (contains("log.cs1", "PROTECTION") && contains("log.message", "disabled")) || - (contains("log.msg", "agent") && contains("log.message", "not running"))) \ No newline at end of file + (contains("log.cs1", "PROTECTION") && contains("log.msg", "disabled")) || + (contains("log.msg", "agent") && contains("log.msg", "not running")))) diff --git a/rules/antivirus/kaspersky/kaspersky_ransomware_behavior.yml b/rules/antivirus/kaspersky/kaspersky_ransomware_behavior.yml index c23b4a878..1ef910a16 100644 --- a/rules/antivirus/kaspersky/kaspersky_ransomware_behavior.yml +++ b/rules/antivirus/kaspersky/kaspersky_ransomware_behavior.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Ransomware Behavior Detection 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://support.kaspersky.com/ - - https://attack.mitre.org/techniques/T1486/ +- https://support.kaspersky.com/ +- https://attack.mitre.org/techniques/T1486/ description: | Detects ransomware behavior patterns identified by Kaspersky including mass file encryption, ransom note creation, and ransomware-specific malware classifications. @@ -24,17 +23,32 @@ description: | 5. Engage incident response team 6. Scan other systems for the same indicators 7. Determine the initial infection vector + + History counts only matching CEF candidates from the same collector and source identity. where: | - exists("log.signatureID") && + equals("log.format","cef") && (exists("log.signatureID") && (regexMatch("log.msg", "(?i)(ransomware|ransom|trojan-ransom|cryptolocker|locky|cerber|wannacry|ryuk|conti|lockbit|blackcat)") || contains("log.cs2", "Trojan-Ransom") || contains("log.cs4", "Trojan-Ransom") || (contains("log.msg", "encrypt") && contains("log.msg", "mass")) || - (contains("log.msg", "System Watcher") && contains("log.msg", "rollback"))) -afterEvents: - - indexPattern: v11-log-antivirus-kaspersky-* - with: - - field: log.src - operator: filter_term - value: '{{.log.src}}' - within: 10m - count: 3 + (contains("log.msg", "System Watcher") && contains("log.msg", "rollback")))) && exists("log.endpointKey") && exists("log.endpointKeyType") && exists("dataSource") +correlation: +- indexPattern: v11-log-antivirus-kaspersky-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: filter_term + value: '{{.log.endpointKey}}' + - field: log.correlationCandidate.kaspersky_ransomware_behavior + operator: filter_term + value: match + within: 10m + count: 3 +groupBy: +- lastEvent.log.endpointKeyType +- lastEvent.log.endpointKey +- lastEvent.log.cs4 diff --git a/rules/antivirus/kaspersky/kaspersky_rootkit_detection.yml b/rules/antivirus/kaspersky/kaspersky_rootkit_detection.yml index 751cb3c70..4a7be1b9c 100644 --- a/rules/antivirus/kaspersky/kaspersky_rootkit_detection.yml +++ b/rules/antivirus/kaspersky/kaspersky_rootkit_detection.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Rootkit Detection impact: confidentiality: 3 integrity: 3 availability: 3 category: Defense Evasion -technique: "T1014 - Rootkit" +technique: T1014 - Rootkit adversary: origin references: - - https://support.kaspersky.com/ - - https://attack.mitre.org/techniques/T1014/ +- https://support.kaspersky.com/ +- https://attack.mitre.org/techniques/T1014/ description: | Detects rootkit activity identified by Kaspersky security, including hidden processes, kernel-level modifications, and rootkit-specific malware classifications that indicate a deeply compromised system. @@ -24,8 +23,8 @@ description: | 5. Check for lateral movement from the compromised host 6. Determine the initial infection vector where: | - exists("log.signatureID") && + equals("log.format","cef") && (exists("log.signatureID") && (regexMatch("log.msg", "(?i)(rootkit|bootkit|Rootkit|hidden.*process|hidden.*module)") || contains("log.cs2", "Rootkit") || contains("log.cs4", "Rootkit") || contains("log.cs2", "Bootkit") || contains("log.cs4", "Bootkit") || - (contains("log.msg", "System Analysis") && contains("log.msg", "hidden"))) + (contains("log.msg", "System Analysis") && contains("log.msg", "hidden")))) diff --git a/rules/antivirus/kaspersky/lateral_movement_indicators.yml b/rules/antivirus/kaspersky/lateral_movement_indicators.yml index 6a143ad55..688a8b3c9 100644 --- a/rules/antivirus/kaspersky/lateral_movement_indicators.yml +++ b/rules/antivirus/kaspersky/lateral_movement_indicators.yml @@ -1,45 +1,50 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Lateral Movement Indicators Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Lateral Movement -technique: "T1021 - Remote Services" +technique: T1021 - Remote Services adversary: origin references: - - https://attack.mitre.org/tactics/TA0008/ - - https://support.kaspersky.com/KESWin/11/en-us/151065.htm -description: | - Detects indicators of lateral movement attempts within the network through Kaspersky antivirus logs. - Attackers use various techniques including PSExec, WMI, RDP, SMB shares, and exploitation tools to move - from one compromised system to others, expanding their access and control across the network. This rule - identifies blocked or detected activities that may indicate lateral movement attempts. - - Next Steps: - 1. Investigate the source IP and hostname for signs of compromise - 2. Review authentication logs for the same time period to identify potential credential theft - 3. Check if the detected tools (PSExec, WMI, RDP) are authorized for use in your environment - 4. Examine network traffic between the source and destination systems - 5. Look for other suspicious activities from the same source host - 6. Consider isolating affected systems if lateral movement is confirmed - 7. Review similar patterns from the same source within the detection window +- https://attack.mitre.org/tactics/TA0008/ +- https://support.kaspersky.com/KESWin/11/en-us/151065.htm +description: "Detects indicators of lateral movement attempts within the network through Kaspersky antivirus logs. \nAttackers use various\ + \ techniques including PSExec, WMI, RDP, SMB shares, and exploitation tools to move \nfrom one compromised system to others, expanding\ + \ their access and control across the network. This rule \nidentifies blocked or detected activities that may indicate lateral movement\ + \ attempts.\n\nNext Steps:\n1. Investigate the source IP and hostname for signs of compromise\n2. Review authentication logs for the\ + \ same time period to identify potential credential theft\n3. Check if the detected tools (PSExec, WMI, RDP) are authorized for use\ + \ in your environment\n4. Examine network traffic between the source and destination systems\n5. Look for other suspicious activities\ + \ from the same source host\n6. Consider isolating affected systems if lateral movement is confirmed\n7. Review similar patterns from\ + \ the same source within the detection window\n\nHistory counts only matching CEF candidates from the same collector and source identity.\n" where: | - (equals("log.cn1", "3") || equals("log.cs1", "DETECT") || equals("log.act", "blocked")) && + equals("log.format","cef") && ((equals("log.cn1", "3") || equals("log.cs1", "DETECT") || equals("action", "blocked")) && (contains("log.msg", ["psexec", "wmi", "rdp", "smb", "admin$", "ipc$", "c$", "remote", "lateral", "pivot"]) || contains("log.cs4", ["exploit", "mimikatz", "bloodhound", "sharphound", "propagat"])) && - exists("log.dst") && - exists("log.src") && - safe(log.src, "") != safe(log.dst, "") -afterEvents: - - indexPattern: v11-log-antivirus-kaspersky-* - with: - - field: log.src - operator: filter_term - value: '{{.log.src}}' - within: 2h - count: 3 + exists("target.ip") && + exists("origin.ip") && + safe("origin.ip", "") != safe("target.ip", "")) && exists("log.endpointKey") && exists("log.endpointKeyType") && exists("dataSource") +correlation: +- indexPattern: v11-log-antivirus-kaspersky-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: filter_term + value: '{{.log.endpointKey}}' + - field: log.correlationCandidate.lateral_movement_indicators + operator: filter_term + value: match + within: 2h + count: 3 +groupBy: +- adversary.ip +- target.ip diff --git a/rules/antivirus/kaspersky/lolbins_abuse.yml b/rules/antivirus/kaspersky/lolbins_abuse.yml index 1a5f7cd67..cafa34253 100644 --- a/rules/antivirus/kaspersky/lolbins_abuse.yml +++ b/rules/antivirus/kaspersky/lolbins_abuse.yml @@ -1,22 +1,21 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Living Off the Land Binaries (LOLBins) Abuse Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1218 - System Binary Proxy Execution" +technique: T1218 - System Binary Proxy Execution adversary: origin references: - - https://attack.mitre.org/techniques/T1218/ - - https://lolbas-project.github.io/ - - https://www.kaspersky.com/enterprise-security/wiki-section/products/fileless-threats-protection +- https://attack.mitre.org/techniques/T1218/ +- https://lolbas-project.github.io/ +- https://www.kaspersky.com/enterprise-security/wiki-section/products/fileless-threats-protection description: | Detects the malicious use of legitimate Windows system binaries (LOLBins) to execute malicious code. Attackers abuse these trusted tools to bypass security controls and maintain persistence. LOLBins are particularly dangerous because they are signed Microsoft binaries that are trusted by most security products. - + Next Steps: 1. Immediately isolate the affected system from the network to prevent lateral movement 2. Review the full context of the detection including command line parameters and parent processes @@ -26,11 +25,11 @@ description: | 6. Review user account activity for signs of compromise or privilege escalation 7. Consider reimaging the system if fileless malware is confirmed where: | - (equals("log.signatureID", "3") || equals("log.cs1", "DETECT")) && + equals("log.format","cef") && ((equals("log.signatureID", "3") || equals("log.cs1", "DETECT")) && (regexMatch("log.msg", "(?i).*(rundll32|regsvr32|mshta|certutil|bitsadmin|powershell|wmic|cscript|wscript|msiexec|installutil|regasm|regsvcs).*") || contains("log.cs4", ["fileless", "LOLBin", "LOLBas"])) && (contains("log.msg", ["download", "execute", "bypass", "encoded", "obfuscat", "hidden", "malicious"]) || - exists("log.actionResult")) + oneOf("actionResult",["denied","failure"]))) groupBy: - - lastEvent.log.cs4 - - adversary.host +- lastEvent.log.cs4 +- adversary.host diff --git a/rules/antivirus/kaspersky/privilege_escalation_attempts.yml b/rules/antivirus/kaspersky/privilege_escalation_attempts.yml index f1edb9ef3..c06b9efbd 100644 --- a/rules/antivirus/kaspersky/privilege_escalation_attempts.yml +++ b/rules/antivirus/kaspersky/privilege_escalation_attempts.yml @@ -1,22 +1,21 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Application Privilege Escalation Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion, Privilege Escalation -technique: "T1055/T1134 - Process Injection and Access Token Manipulation" +technique: T1055/T1134 - Process Injection and Access Token Manipulation adversary: origin references: - - https://support.kaspersky.com/KLMS/8.2/en-US/151684.htm - - https://attack.mitre.org/techniques/T1055/ - - https://attack.mitre.org/techniques/T1134/ +- https://support.kaspersky.com/KLMS/8.2/en-US/151684.htm +- https://attack.mitre.org/techniques/T1055/ +- https://attack.mitre.org/techniques/T1134/ description: | Detects attempts to escalate privileges through application manipulation, process injection, or unauthorized elevation of permissions monitored by Kaspersky endpoint protection. These techniques are commonly used by attackers to gain higher-level permissions on compromised systems. - + Next Steps: 1. Immediately isolate the affected system to prevent lateral movement 2. Review the process that triggered the alert and its parent process chain @@ -27,15 +26,15 @@ description: | 7. Check for any unauthorized scheduled tasks or services 8. Update Kaspersky signatures and run a full system scan where: | - exists("log.signatureID") && + equals("log.format","cef") && (exists("log.signatureID") && !equals("action", "Allowed") && (contains("log.msg", ["privilege", "elevation", "EXPLOIT", "Exploit", "process injection", "token manipulation"]) || contains("log.cs1", "EXPLOIT") || contains("log.cs2", "Exploit") || contains("log.cs4", "Exploit") || - contains("log.msg", ["privilege", "elevation"])) + contains("log.msg", ["privilege", "elevation"]))) groupBy: - - lastEvent.log.signatureID - - adversary.host - - adversary.user +- lastEvent.log.signatureID +- adversary.host +- adversary.user diff --git a/rules/antivirus/kaspersky/process_hollowing_detection.yml b/rules/antivirus/kaspersky/process_hollowing_detection.yml index 9148ed503..5ff1eb355 100644 --- a/rules/antivirus/kaspersky/process_hollowing_detection.yml +++ b/rules/antivirus/kaspersky/process_hollowing_detection.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Process Hollowing Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion, Privilege Escalation -technique: "T1055.012 - Process Injection: Process Hollowing" +technique: 'T1055.012 - Process Injection: Process Hollowing' adversary: origin references: - - https://attack.mitre.org/techniques/T1055/012/ - - https://www.kaspersky.com/enterprise-security/wiki-section/products/behavior-based-protection +- https://attack.mitre.org/techniques/T1055/012/ +- https://www.kaspersky.com/enterprise-security/wiki-section/products/behavior-based-protection description: | Detects process hollowing attempts where malware creates a new process in suspended state, unmaps its memory, and replaces it with malicious code. This advanced technique is used to evade detection by hiding malicious code within legitimate processes. - + Next Steps: 1. Immediately isolate the affected system to prevent lateral movement 2. Identify the parent process that initiated the hollowing attempt @@ -25,11 +24,11 @@ description: | 6. Search for similar patterns across other endpoints 7. Update endpoint protection policies to block the identified threat where: | - (equals("log.signatureID", "3") || equals("log.cs1", "DETECT")) && + equals("log.format","cef") && ((equals("log.signatureID", "3") || equals("log.cs1", "DETECT")) && (contains("log.msg", ["hollow", "RunPE", "suspend", "NtUnmapViewOfSection", "ZwUnmapViewOfSection"]) || contains("log.cs4", ["hollow", "RunPE", "replace"]) || contains("log.msg", ["hollow", "suspended", "unmap"])) && - greaterOrEqual("log.cefDeviceSeverity", "3") + greaterOrEqual("log.cefDeviceSeverity", "3")) groupBy: - - lastEvent.log.cs5 - - adversary.host +- lastEvent.log.cs5 +- adversary.host diff --git a/rules/antivirus/kaspersky/sandbox_evasion_attempts.yml b/rules/antivirus/kaspersky/sandbox_evasion_attempts.yml index 635902d28..ed1368de4 100644 --- a/rules/antivirus/kaspersky/sandbox_evasion_attempts.yml +++ b/rules/antivirus/kaspersky/sandbox_evasion_attempts.yml @@ -1,22 +1,21 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Sandbox Evasion Attempts Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion, Discovery -technique: "T1497 - Virtualization/Sandbox Evasion" +technique: T1497 - Virtualization/Sandbox Evasion adversary: origin references: - - https://www.kaspersky.com/enterprise-security/malware-sandbox - - https://attack.mitre.org/techniques/T1497/ - - https://attack.mitre.org/techniques/T1497/001/ +- https://www.kaspersky.com/enterprise-security/malware-sandbox +- https://attack.mitre.org/techniques/T1497/ +- https://attack.mitre.org/techniques/T1497/001/ description: | Identifies malware attempting to detect and evade sandbox environments. This includes time-based evasion, environment checks, anti-VM techniques, and other behaviors designed to avoid analysis in controlled environments. - + Next Steps: 1. Immediately isolate the affected system to prevent potential malware spread 2. Review the process that triggered the sandbox evasion detection @@ -26,12 +25,12 @@ description: | 6. Check if similar detection occurred on other systems in the network 7. Consider submitting the sample to Kaspersky for further analysis where: | - exists("log.signatureID") && + equals("log.format","cef") && (exists("log.signatureID") && (contains("log.msg", ["sandbox", "evasion", "anti-VM", "virtualization"]) || contains("log.msg", ["sandbox", "evasion"]) || contains("log.cs1", "SANDBOX_") || contains("log.cs4", ["Evasion", "AntiVM", "environment check", "time delay", "VM detection"]) || (equals("log.cat", "Behavior Detection") && - contains("log.msg", ["delay", "sleep"]))) + contains("log.msg", ["delay", "sleep"])))) deduplicateBy: - - adversary.host \ No newline at end of file +- adversary.host diff --git a/rules/antivirus/kaspersky/suspicious_network_activity.yml b/rules/antivirus/kaspersky/suspicious_network_activity.yml index dcdad092f..11d5adfc3 100644 --- a/rules/antivirus/kaspersky/suspicious_network_activity.yml +++ b/rules/antivirus/kaspersky/suspicious_network_activity.yml @@ -1,22 +1,21 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Suspicious Network Activity Detection 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://support.kaspersky.com/kwts/6.1/267200 - - https://attack.mitre.org/techniques/T1071/ - - https://attack.mitre.org/techniques/T1043/ +- https://support.kaspersky.com/kwts/6.1/267200 +- https://attack.mitre.org/techniques/T1071/ +- https://attack.mitre.org/techniques/T1043/ description: | Detects suspicious network activities including unusual connections, potential C2 communications, or network-based attacks identified by Kaspersky security monitoring. This rule triggers when Kaspersky blocks network connections that match suspicious patterns and multiple similar events occur from the same host. - + Next Steps: 1. Review the source and destination IP addresses for known malicious indicators using threat intelligence feeds 2. Check if the blocked connection was attempting to reach known C2 servers or suspicious domains @@ -28,25 +27,36 @@ description: | 8. Run a full system scan on the affected host and check for persistence mechanisms 9. Review network traffic logs for data exfiltration attempts to the same destination 10. Document the incident and update block lists with confirmed malicious IPs/domains + + History counts only matching CEF candidates from the same collector and source identity. where: | - exists("log.signatureID") && + equals("log.format","cef") && (exists("log.signatureID") && (contains("log.msg", ["suspicious connection", "network attack", "port scan", "unusual traffic"]) || contains("log.msg", "network") || contains("log.cs1", "NETWORK") || contains("log.cs2", "Net-Worm") || contains("log.cs4", "Net-Worm") || - (exists("log.target.ip") && exists("log.dpt"))) && - equals("action", "Blocked") -afterEvents: - - indexPattern: v11-log-antivirus-kaspersky-* - with: - - field: log.src - operator: filter_term - value: '{{.log.src}}' - - field: log.dstIP - operator: filter_term - value: '{{.log.dstIP}}' - within: 30m - count: 5 + (exists("target.ip") && exists("log.dpt"))) && + equals("actionResult", "denied") && exists("origin.ip") && exists("target.ip")) && exists("log.endpointKey") && exists("log.endpointKeyType") && exists("dataSource") groupBy: - - target.ip +- target.ip +correlation: +- indexPattern: v11-log-antivirus-kaspersky-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: filter_term + value: '{{.log.endpointKey}}' + - field: log.correlationCandidate.suspicious_network_activity + operator: filter_term + value: match + - field: target.ip + operator: filter_term + value: '{{.target.ip}}' + within: 30m + count: 5 diff --git a/rules/antivirus/kaspersky/suspicious_packed_executables.yml b/rules/antivirus/kaspersky/suspicious_packed_executables.yml index a57711f1a..123df00ec 100644 --- a/rules/antivirus/kaspersky/suspicious_packed_executables.yml +++ b/rules/antivirus/kaspersky/suspicious_packed_executables.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Suspicious Packed Executable Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1027.002 - Obfuscated Files or Information: Software Packing" +technique: 'T1027.002 - Obfuscated Files or Information: Software Packing' adversary: origin references: - - https://www.kaspersky.com/resource-center/threats/suspicious-packers - - https://attack.mitre.org/techniques/T1027/002/ +- https://www.kaspersky.com/resource-center/threats/suspicious-packers +- https://attack.mitre.org/techniques/T1027/002/ description: | Detects when Kaspersky identifies suspicious packed executables, which are often used by malware to evade detection and analysis. Packed executables use compression or encryption to hide their true content and make reverse engineering more difficult. - + Next Steps: 1. Identify the affected system from origin.host and origin.ip fields 2. Review the detected threat details from log.descMsg and log.msg fields @@ -28,14 +27,14 @@ description: | 9. Submit the sample to Kaspersky or third-party sandbox for detailed analysis 10. Update antivirus signatures and ensure real-time protection is enabled where: | - oneOf("action", ["blocked", "detected"]) && + equals("log.format","cef") && (oneOf("action", ["blocked", "detected"]) && (contains("log.msg", ["Packed", "packer"]) || contains("log.msg", ["packed", "Packed"]) || contains("log.msg", ["NSAnti", "Themida", "VMProtect", "ASPack", "UPX", "PECompact", "Enigma", "Armadillo"]) || contains("log.msg", ["NSAnti", "Themida", "VMProtect", "ASPack", "UPX", "PECompact", "Enigma", "Armadillo"]) || - contains("log.cat", ["Trojan.Packed", "Packed"])) + contains("log.cat", ["Trojan.Packed", "Packed"]))) groupBy: - - adversary.host - - adversary.ip +- adversary.host +- adversary.ip diff --git a/rules/antivirus/kaspersky/suspicious_scheduled_tasks.yml b/rules/antivirus/kaspersky/suspicious_scheduled_tasks.yml index 1442a4412..16d818c3a 100644 --- a/rules/antivirus/kaspersky/suspicious_scheduled_tasks.yml +++ b/rules/antivirus/kaspersky/suspicious_scheduled_tasks.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Suspicious Scheduled Tasks Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Execution, Persistence, Privilege Escalation -technique: "T1053 - Scheduled Task/Job" +technique: T1053 - Scheduled Task/Job adversary: origin references: - - https://attack.mitre.org/techniques/T1053/ - - https://support.kaspersky.com/ScanEngine/1.0/en-US/186767.htm +- https://attack.mitre.org/techniques/T1053/ +- https://support.kaspersky.com/ScanEngine/1.0/en-US/186767.htm description: | Detects suspicious scheduled task creation or modification identified by Kaspersky, which could indicate persistence mechanisms used by malware or attackers. Scheduled tasks are commonly abused by attackers to maintain persistence, execute malicious code at specific times, or escalate privileges. - + Next Steps: 1. Review the scheduled task details in log.msg, log.fname, and log.descMsg fields 2. Check the specific threat signature in log.signatureID to understand the detection @@ -33,7 +32,7 @@ description: | - Check if the malware has spread to other systems - Preserve evidence and initiate incident response procedures where: | - (containsAll("log.msg", ["scheduled", "task"]) || + equals("log.format","cef") && ((containsAll("log.msg", ["scheduled", "task"]) || contains("log.msg", ["schtasks", "schedule"]) || contains("log.msg", ["scheduled", "task"]) || contains("log.cs2", "persist") || @@ -41,6 +40,6 @@ where: | contains("log.cat", "persistence")) && (exists("log.signatureID") || oneOf("log.cs1", ["infected", "suspicious"]) || - exists("log.cefDeviceSeverity")) + exists("log.cefDeviceSeverity"))) groupBy: - - adversary.host +- adversary.host diff --git a/rules/antivirus/kaspersky/suspicious_service_installation.yml b/rules/antivirus/kaspersky/suspicious_service_installation.yml index 84822f5be..ae6b47c7d 100644 --- a/rules/antivirus/kaspersky/suspicious_service_installation.yml +++ b/rules/antivirus/kaspersky/suspicious_service_installation.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Suspicious Service Installation Detection impact: confidentiality: 2 integrity: 2 availability: 2 category: Persistence, Privilege Escalation -technique: "T1543.003 - Create or Modify System Process: Windows Service" +technique: 'T1543.003 - Create or Modify System Process: Windows Service' adversary: origin references: - - https://attack.mitre.org/techniques/T1543/003/ - - https://support.kaspersky.com/ScanEngine/2.1/en-US/186767.htm +- https://attack.mitre.org/techniques/T1543/003/ +- https://support.kaspersky.com/ScanEngine/2.1/en-US/186767.htm description: | Detects suspicious Windows service installation or modification attempts identified by Kaspersky, which could indicate malware persistence mechanisms or privilege escalation attempts. Service manipulation is a common technique used by malware to maintain persistence on compromised systems. - + Next Steps: 1. Identify the service name and executable path from the alert details 2. Verify if the service installation was authorized and legitimate @@ -25,12 +24,12 @@ description: | 6. If confirmed malicious, stop and remove the service, quarantine associated files 7. Perform full system scan and check for additional compromise indicators where: | - (containsAll("log.msg", ["Service", "install"]) || + equals("log.format","cef") && ((containsAll("log.msg", ["Service", "install"]) || containsAll("log.msg", ["sc.exe", "create"]) || containsAll("log.msg", ["New", "Service"]) || contains("log.fname", "\\services.exe") || contains("log.cs2", "Service")) && (oneOf("log.cs1", ["infected", "suspicious"]) || - greaterOrEqual("log.cefDeviceSeverity", "3")) + greaterOrEqual("log.cefDeviceSeverity", "3"))) groupBy: - - adversary.host +- adversary.host diff --git a/rules/antivirus/kaspersky/system_file_tampering_detection.yml b/rules/antivirus/kaspersky/system_file_tampering_detection.yml index 45a81e5ce..df2621bff 100644 --- a/rules/antivirus/kaspersky/system_file_tampering_detection.yml +++ b/rules/antivirus/kaspersky/system_file_tampering_detection.yml @@ -1,22 +1,21 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky System File Tampering Detection impact: confidentiality: 2 integrity: 3 availability: 1 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://support.kaspersky.com/kwts/6.1/267200 - - https://attack.mitre.org/techniques/T1562/001/ - - https://attack.mitre.org/techniques/T1036/ +- https://support.kaspersky.com/kwts/6.1/267200 +- https://attack.mitre.org/techniques/T1562/001/ +- https://attack.mitre.org/techniques/T1036/ description: | Detects attempts to tamper with critical system files, Windows services, or protected system components. This includes unauthorized modifications to system binaries, service configurations, or attempts to manipulate security-critical files. - + Next Steps: 1. Identify the affected system file or component from the log details 2. Check if the modification was authorized (planned maintenance, legitimate software update) @@ -26,11 +25,11 @@ description: | 6. Check for persistence mechanisms that may have been established 7. Consider isolating the system if unauthorized tampering is confirmed where: | - exists("log.signatureID") && + equals("log.format","cef") && (exists("log.signatureID") && (contains("log.msg", ["system file", "critical file", "protected file", "service tamper"]) || contains("log.cs1", "SYSTEM_MOD") || contains("log.cs4", "SystemFile") || contains("log.msg", ["system modification", "unauthorized change"]) || - (equals("log.cat", "Behavior Detection") && contains("log.msg", "modify"))) + (equals("log.cat", "Behavior Detection") && contains("log.msg", "modify")))) groupBy: - - adversary.host +- adversary.host diff --git a/rules/antivirus/kaspersky/trusted_application_compromise.yml b/rules/antivirus/kaspersky/trusted_application_compromise.yml index fe53eb12f..ba2b0f236 100644 --- a/rules/antivirus/kaspersky/trusted_application_compromise.yml +++ b/rules/antivirus/kaspersky/trusted_application_compromise.yml @@ -1,22 +1,21 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky Trusted Application Compromise Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1218 - Signed Binary Proxy Execution" +technique: T1218 - Signed Binary Proxy Execution adversary: origin references: - - https://www.kaspersky.com/enterprise-security/wiki-section/products/kaspersky-anti-targeted-attack-platform - - https://attack.mitre.org/techniques/T1218/ - - https://attack.mitre.org/techniques/T1574/ +- https://www.kaspersky.com/enterprise-security/wiki-section/products/kaspersky-anti-targeted-attack-platform +- https://attack.mitre.org/techniques/T1218/ +- https://attack.mitre.org/techniques/T1574/ description: | Identifies when legitimate or trusted applications exhibit malicious behavior, potentially indicating compromise or exploitation. This includes detecting when signed binaries are used for malicious purposes or when trusted processes perform suspicious activities. This is a critical security event that indicates an attacker may be using living-off-the-land techniques to evade detection. - + Next Steps: 1. Immediately isolate the affected system to prevent lateral movement 2. Identify the compromised trusted application and its process chain @@ -27,13 +26,13 @@ description: | 7. Consider reimaging the system if compromise is confirmed 8. Update security policies to monitor the exploited application more closely where: | - exists("log.signatureID") && + equals("log.format","cef") && (exists("log.signatureID") && (contains("log.msg", ["trusted application", "signed binary", "legitimate program"]) || contains("log.cs1", "TRUSTED_COMP") || contains("log.cs4", "TrustedApp") || contains("log.msg", "whitelisted") || (equals("log.cat", "Exploit Prevention") && contains("log.msg", "exploit")) || containsAll("log.msg", ["behavior", "trusted"])) && - oneOf("log.cefDeviceSeverity", ["High", "Medium"]) + oneOf("log.cefDeviceSeverity", ["High", "Medium"])) groupBy: - - adversary.host +- adversary.host diff --git a/rules/antivirus/kaspersky/wmi_abuse_detection.yml b/rules/antivirus/kaspersky/wmi_abuse_detection.yml index 9a105b7e4..841302781 100644 --- a/rules/antivirus/kaspersky/wmi_abuse_detection.yml +++ b/rules/antivirus/kaspersky/wmi_abuse_detection.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule version v1.1.0 dataTypes: - - antivirus-kaspersky +- antivirus-kaspersky name: Kaspersky WMI Abuse Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Execution -technique: "T1047 - Windows Management Instrumentation" +technique: T1047 - Windows Management Instrumentation adversary: origin references: - - https://attack.mitre.org/techniques/T1047/ - - https://support.kaspersky.com/KLMS/8.2/en-US/151684.htm +- https://attack.mitre.org/techniques/T1047/ +- https://support.kaspersky.com/KLMS/8.2/en-US/151684.htm description: | Detects potential Windows Management Instrumentation (WMI) abuse identified by Kaspersky, including suspicious WMI queries, event subscriptions, or process creation via WMI. WMI is a legitimate Windows component often abused by attackers for lateral movement, persistence, and code execution. - + Next Steps: 1. Identify the affected host and user account involved in the WMI activity 2. Review the specific WMI commands or queries that triggered the alert @@ -24,9 +23,9 @@ description: | 5. Verify if this is legitimate administrative activity or potential malicious behavior 6. If confirmed malicious, isolate the system and perform incident response procedures where: | - (contains("log.msg", ["WMI", "wmi", "wmic", "winmgmt", "scrcons.exe"]) || + equals("log.format","cef") && ((contains("log.msg", ["WMI", "wmi", "wmic", "winmgmt", "scrcons.exe"]) || contains("log.msg", "WMI")) && - (greaterOrEqual("log.cefDeviceSeverity", "3") || equals("log.cat", "blocked")) + (greaterOrEqual("log.cefDeviceSeverity", "3") || equals("log.cat", "blocked"))) groupBy: - - adversary.host - - adversary.user +- adversary.host +- adversary.user From c71986ddf61c15ef0766b23a518bf873667191b8 Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Thu, 17 Sep 2026 18:55:17 -0400 Subject: [PATCH 3/3] fix(kaspersky): align equivalent action spellings in CEF consumers --- filters/antivirus/kaspersky.yml | 2 +- filters/audits/kaspersky.md | 7 +++-- plugins/alerts/testdata/kaspersky_raw.json | 28 +++++++++++++++++++ .../kaspersky/lateral_movement_indicators.yml | 2 +- .../privilege_escalation_attempts.yml | 2 +- .../suspicious_packed_executables.yml | 2 +- 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/filters/antivirus/kaspersky.yml b/filters/antivirus/kaspersky.yml index 4a8d8f3c8..49be40ec3 100644 --- a/filters/antivirus/kaspersky.yml +++ b/filters/antivirus/kaspersky.yml @@ -1112,7 +1112,7 @@ pipeline: key: log.correlationCandidate.lateral_movement_indicators value: match where: |- - equals("log.format","cef") && ((equals("log.cn1", "3") || equals("log.cs1", "DETECT") || equals("action", "blocked")) && + equals("log.format","cef") && ((equals("log.cn1", "3") || equals("log.cs1", "DETECT") || equals("actionResult", "denied")) && (contains("log.msg", ["psexec", "wmi", "rdp", "smb", "admin$", "ipc$", "c$", "remote", "lateral", "pivot"]) || contains("log.cs4", ["exploit", "mimikatz", "bloodhound", "sharphound", "propagat"])) && diff --git a/filters/audits/kaspersky.md b/filters/audits/kaspersky.md index e28a43149..80256ac3f 100644 --- a/filters/audits/kaspersky.md +++ b/filters/audits/kaspersky.md @@ -64,7 +64,8 @@ candidates. No new health or native-malware rule was invented from this sample. CEF consumer repairs include standard source/destination fields, quoted-path `safe` lookups, normalized block outcomes, `log.msg` rather than an unproduced `log.message`, -and the actual standard `actionResult`. The C2 rule requires a C2 indicator; generic +and the actual standard `actionResult`. Lateral, packed-executable and privilege +consumers handle equivalent action spellings consistently. The C2 rule requires a C2 indicator; generic `NetworkThreat` alone is insufficient. Explicit blocks are excluded for all tested spellings, while unknown outcomes are described as unknown, not successful C2. @@ -85,13 +86,13 @@ or changed. Original CEF fields are retained to reduce compatibility losses. ## Verification and limits -- **82 synthetic raw cases** cover native quoting, malformed input, embedded CEF, +- **85 synthetic raw cases** cover native quoting, malformed input, embedded CEF, reporting/managed-device roles, missing identities, semantic zero IPs, ports, severity, outcomes, CEF compatibility and positive/negative predicates for every rule. - Actual SDK v1.1.31 performs CEL evaluation, Event conversion and all four historical query/threshold suites against a local isolated mock. Tests cover threshold boundaries, time windows, collector/source/destination scope, unrelated populations and missing keys. -- Shared contract overlay: **112 passing test/subtest records**, no failures or skips, +- Shared contract overlay: **115 passing test/subtest records**, no failures or skips, including the separate private ten-record replay. - Private raw replay recovers target IP, target host and device time in **10/10** records; all 19 attack predicates remain false. No actor or action result is invented. diff --git a/plugins/alerts/testdata/kaspersky_raw.json b/plugins/alerts/testdata/kaspersky_raw.json index ed147df0b..6c84d4791 100644 --- a/plugins/alerts/testdata/kaspersky_raw.json +++ b/plugins/alerts/testdata/kaspersky_raw.json @@ -1061,5 +1061,33 @@ }, "absent": [], "matches": [] + }, + { + "name": "packed uppercase block", + "raw": "CEF:0|Kaspersky|Security Center|14|1|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 act=BLOCKED msg=Packed executable", + "dataSource": "collector-test", + "expected": {}, + "absent": [], + "matches": [ + "suspicious_packed_executables" + ] + }, + { + "name": "lateral uppercase block", + "raw": "CEF:0|Kaspersky|Security Center|14|1|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 act=BLOCKED msg=psexec", + "dataSource": "collector-test", + "expected": {}, + "absent": [], + "matches": [ + "lateral_movement_indicators" + ] + }, + { + "name": "privilege allowed case", + "raw": "CEF:0|Kaspersky|Security Center|14|1|Synthetic event|3|src=198.51.100.9 dst=192.0.2.8 act=ALLOW msg=token manipulation", + "dataSource": "collector-test", + "expected": {}, + "absent": [], + "matches": [] } ] diff --git a/rules/antivirus/kaspersky/lateral_movement_indicators.yml b/rules/antivirus/kaspersky/lateral_movement_indicators.yml index 688a8b3c9..5fa70f97b 100644 --- a/rules/antivirus/kaspersky/lateral_movement_indicators.yml +++ b/rules/antivirus/kaspersky/lateral_movement_indicators.yml @@ -21,7 +21,7 @@ description: "Detects indicators of lateral movement attempts within the network \ from the same source host\n6. Consider isolating affected systems if lateral movement is confirmed\n7. Review similar patterns from\ \ the same source within the detection window\n\nHistory counts only matching CEF candidates from the same collector and source identity.\n" where: | - equals("log.format","cef") && ((equals("log.cn1", "3") || equals("log.cs1", "DETECT") || equals("action", "blocked")) && + equals("log.format","cef") && ((equals("log.cn1", "3") || equals("log.cs1", "DETECT") || equals("actionResult", "denied")) && (contains("log.msg", ["psexec", "wmi", "rdp", "smb", "admin$", "ipc$", "c$", "remote", "lateral", "pivot"]) || contains("log.cs4", ["exploit", "mimikatz", "bloodhound", "sharphound", "propagat"])) && diff --git a/rules/antivirus/kaspersky/privilege_escalation_attempts.yml b/rules/antivirus/kaspersky/privilege_escalation_attempts.yml index c06b9efbd..65c813c66 100644 --- a/rules/antivirus/kaspersky/privilege_escalation_attempts.yml +++ b/rules/antivirus/kaspersky/privilege_escalation_attempts.yml @@ -27,7 +27,7 @@ description: | 8. Update Kaspersky signatures and run a full system scan where: | equals("log.format","cef") && (exists("log.signatureID") && - !equals("action", "Allowed") && + !equals("actionResult", "success") && (contains("log.msg", ["privilege", "elevation", "EXPLOIT", "Exploit", "process injection", "token manipulation"]) || contains("log.cs1", "EXPLOIT") || diff --git a/rules/antivirus/kaspersky/suspicious_packed_executables.yml b/rules/antivirus/kaspersky/suspicious_packed_executables.yml index 123df00ec..e783743fa 100644 --- a/rules/antivirus/kaspersky/suspicious_packed_executables.yml +++ b/rules/antivirus/kaspersky/suspicious_packed_executables.yml @@ -27,7 +27,7 @@ description: | 9. Submit the sample to Kaspersky or third-party sandbox for detailed analysis 10. Update antivirus signatures and ensure real-time protection is enabled where: | - equals("log.format","cef") && (oneOf("action", ["blocked", "detected"]) && + equals("log.format","cef") && (regexMatch("action", "(?i)^(blocked|detected)$") && (contains("log.msg", ["Packed", "packer"]) || contains("log.msg", ["packed", "Packed"]) || contains("log.msg", ["NSAnti", "Themida", "VMProtect", "ASPack", "UPX",