diff --git a/filters/antivirus/kaspersky.yml b/filters/antivirus/kaspersky.yml index 49be40ec3..9c0653eb6 100644 --- a/filters/antivirus/kaspersky.yml +++ b/filters/antivirus/kaspersky.yml @@ -1,4 +1,6 @@ -# Kaspersky filter v3.1.0; native KSC structured syslog and CEF. +# Kaspersky filter v3.2.0; native KSC structured syslog and CEF. +# The grok plugin trims input before each pattern and rejects empty matches; +# separator spaces therefore belong to nonempty captures, not their own steps. pipeline: - dataTypes: - antivirus-kaspersky @@ -170,65 +172,51 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*act= + pattern: ^(?:.*[ \t])?act= - fieldName: log.act - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*agt= + pattern: ^(?:.*[ \t])?agt= - fieldName: log.agt - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*ahost= + pattern: ^(?:.*[ \t])?ahost= - fieldName: log.ahost - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*amac= + pattern: ^(?:.*[ \t])?amac= - fieldName: log.amac - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cat= + pattern: ^(?:.*[ \t])?cat= - fieldName: log.cat - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn1= + pattern: ^(?:.*[ \t])?cn1= - fieldName: log.cn1 - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn10= + pattern: ^(?:.*[ \t])?cn10= - fieldName: log.cn10 - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: @@ -251,11 +239,9 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn2= + pattern: ^(?:.*[ \t])?cn2= - fieldName: log.cn2 - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: @@ -269,11 +255,9 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn3= + pattern: ^(?:.*[ \t])?cn3= - fieldName: log.cn3 - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: @@ -287,11 +271,9 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn4= + pattern: ^(?:.*[ \t])?cn4= - fieldName: log.cn4 - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: @@ -305,11 +287,9 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn5= + pattern: ^(?:.*[ \t])?cn5= - fieldName: log.cn5 - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: @@ -323,11 +303,9 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn6= + pattern: ^(?:.*[ \t])?cn6= - fieldName: log.cn6 - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: @@ -341,11 +319,9 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn7= + pattern: ^(?:.*[ \t])?cn7= - fieldName: log.cn7 - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: @@ -359,11 +335,9 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn8= + pattern: ^(?:.*[ \t])?cn8= - fieldName: log.cn8 - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: @@ -377,11 +351,9 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cn9= + pattern: ^(?:.*[ \t])?cn9= - fieldName: log.cn9 - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: @@ -395,11 +367,9 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*cs1= + pattern: ^(?:.*[ \t])?cs1= - fieldName: log.cs1 - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: @@ -575,74 +545,58 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*customerURI= + pattern: ^(?:.*[ \t])?customerURI= - fieldName: log.customerURI - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*destinationZoneExternalID= + pattern: ^(?:.*[ \t])?destinationZoneExternalID= - fieldName: log.destinationZoneExternalID - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*destinationZoneURI= + pattern: ^(?:.*[ \t])?destinationZoneURI= - fieldName: log.destinationZoneURI - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*dhost= + pattern: ^(?:.*[ \t])?dhost= - fieldName: log.dhost - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*dmac= + pattern: ^(?:.*[ \t])?dmac= - fieldName: log.dmac - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*dpt= + pattern: ^(?:.*[ \t])?dpt= - fieldName: log.dpt - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*dst= + pattern: ^(?:.*[ \t])?dst= - fieldName: log.dst - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*duser= + pattern: ^(?:.*[ \t])?duser= - fieldName: log.duser - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: @@ -665,74 +619,72 @@ pipeline: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*msg= + pattern: ^(?:.*[ \t])?msg= - fieldName: log.msg - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: (?s:.+)$ + where: regexMatch("log.cefExtension","(?:^|[ \t])msg=[^ \t=]") + # CEF escapes '=' inside values. Stop before the next unescaped key, then + # remove its name when the message is followed by a destination field. + - grok: + source: log.msg + patterns: + - fieldName: log.msg + pattern: '^(?:\\.|[^=])+' + where: equals("log.format","cef") || exists("log.cefExtension") + - trim: + function: suffix + substring: ' dst' + fields: [log.msg] + where: endsWith("log.msg"," dst") - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*originalAgentZoneURI= + pattern: ^(?:.*[ \t])?originalAgentZoneURI= - fieldName: log.originalAgentZoneURI - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*proto= + pattern: ^(?:.*[ \t])?proto= - fieldName: log.proto - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*shost= + pattern: ^(?:.*[ \t])?shost= - fieldName: log.shost - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*smac= + pattern: ^(?:.*[ \t])?smac= - fieldName: log.smac - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*spt= + pattern: ^(?:.*[ \t])?spt= - fieldName: log.spt - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*src= + pattern: ^(?:.*[ \t])?src= - fieldName: log.src - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - grok: source: log.cefExtension patterns: - fieldName: '' - pattern: ^(?:[A-Za-z][A-Za-z0-9_]*=(?:\\.|[^\\=\r\n])*?[ \t]+)*suser= + pattern: ^(?:.*[ \t])?suser= - fieldName: log.suser - pattern: (?:\\.|[^\\=\r\n])*? - - fieldName: '' - pattern: (?:[ \t]+[A-Za-z][A-Za-z0-9_]*=|$) + pattern: '[^ \t=]+' - add: function: string params: @@ -839,6 +791,9 @@ pipeline: - fieldName: action pattern: (?s)^.+$ where: exists("log.act") && !oneOf("log.act",["","-"]) + # A copied or extension-supplied result is not the final operation verdict. + - delete: + fields: [actionResult] - add: function: string params: @@ -850,7 +805,13 @@ pipeline: params: key: actionResult value: denied - where: regexMatch("action","(?i)^(block|blocked)$") + where: regexMatch("action","(?i)^(block|blocked|deny|denied|drop|dropped|reject|rejected|quarantine|quarantined)$") + - add: + function: string + params: + key: actionResult + value: failure + where: regexMatch("action","(?i)^(fail|failed|failure)$") - add: function: string params: @@ -889,31 +850,21 @@ pipeline: - fieldName: '' pattern: '>' - fieldName: log.ksc.syslogVersion - pattern: '[1-9][0-9]*' - - fieldName: '' - pattern: ' ' + pattern: '[1-9][0-9]*[ \t]+' - fieldName: log.ksc.time - pattern: \S+ - - fieldName: '' - pattern: ' ' + pattern: \S+[ \t]+ - fieldName: log.ksc.reportingHost - pattern: \S+ - - fieldName: '' - pattern: ' ' + pattern: \S+[ \t]+ - fieldName: log.ksc.appName - pattern: \S+ - - fieldName: '' - pattern: ' ' + pattern: \S+[ \t]+ - fieldName: log.ksc.procId - pattern: \S+ - - fieldName: '' - pattern: ' ' + pattern: \S+[ \t]+ - fieldName: log.ksc.messageId - pattern: \S+ + pattern: \S+[ \t]+ - fieldName: '' - pattern: ' \[event@23668' + pattern: '\[event@23668' - fieldName: log.ksc.params - pattern: '(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")*' + pattern: '(?:[A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*"[ \t]*)+' - fieldName: '' pattern: '\](?: )?' - fieldName: log.msg @@ -928,65 +879,65 @@ pipeline: source: log.ksc.params patterns: - fieldName: '' - pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* p1="' + pattern: '^(?:.*[ \t])?p1="' - fieldName: log.ksc.p1 - pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + pattern: '(?:\\.|[^\"\\])+' - fieldName: '' - pattern: '"(?: |$)' + pattern: '"' - grok: source: log.ksc.params patterns: - fieldName: '' - pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* et="' + pattern: '^(?:.*[ \t])?et="' - fieldName: log.ksc.et - pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + pattern: '(?:\\.|[^\"\\])+' - fieldName: '' - pattern: '"(?: |$)' + pattern: '"' - grok: source: log.ksc.params patterns: - fieldName: '' - pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* etdn="' + pattern: '^(?:.*[ \t])?etdn="' - fieldName: log.ksc.etdn - pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + pattern: '(?:\\.|[^\"\\])+' - fieldName: '' - pattern: '"(?: |$)' + pattern: '"' - grok: source: log.ksc.params patterns: - fieldName: '' - pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* hdn="' + pattern: '^(?:.*[ \t])?hdn="' - fieldName: log.ksc.hdn - pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + pattern: '(?:\\.|[^\"\\])+' - fieldName: '' - pattern: '"(?: |$)' + pattern: '"' - grok: source: log.ksc.params patterns: - fieldName: '' - pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* hip="' + pattern: '^(?:.*[ \t])?hip="' - fieldName: log.ksc.hip - pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + pattern: '(?:\\.|[^\"\\])+' - fieldName: '' - pattern: '"(?: |$)' + pattern: '"' - grok: source: log.ksc.params patterns: - fieldName: '' - pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* gn="' + pattern: '^(?:.*[ \t])?gn="' - fieldName: log.ksc.gn - pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + pattern: '(?:\\.|[^\"\\])+' - fieldName: '' - pattern: '"(?: |$)' + pattern: '"' - grok: source: log.ksc.params patterns: - fieldName: '' - pattern: '^(?: [A-Za-z][A-Za-z0-9_]*="(?:\\[\"\\\]]|[^\"\\\]\r\n])*")* kscfqdn="' + pattern: '^(?:.*[ \t])?kscfqdn="' - fieldName: log.ksc.kscfqdn - pattern: (?:\\[\"\\\]]|[^\"\\\]\r\n])* + pattern: '(?:\\.|[^\"\\])+' - fieldName: '' - pattern: '"(?: |$)' + pattern: '"' - grok: source: log.ksc.et patterns: @@ -1086,7 +1037,7 @@ pipeline: - add: function: string params: - key: log.correlationCandidate.data_exfiltration_attempts + key: log.correlationCandidate.dataExfiltrationAttempts value: match where: |- equals("log.format","cef") && ((equals("log.cat", "NetworkThreat") || @@ -1098,7 +1049,7 @@ pipeline: - add: function: string params: - key: log.correlationCandidate.kaspersky_ransomware_behavior + key: log.correlationCandidate.kasperskyRansomwareBehavior value: match where: |- equals("log.format","cef") && (exists("log.signatureID") && @@ -1109,7 +1060,7 @@ pipeline: - add: function: string params: - key: log.correlationCandidate.lateral_movement_indicators + key: log.correlationCandidate.lateralMovementIndicators value: match where: |- equals("log.format","cef") && ((equals("log.cn1", "3") || equals("log.cs1", "DETECT") || equals("actionResult", "denied")) && @@ -1122,7 +1073,7 @@ pipeline: - add: function: string params: - key: log.correlationCandidate.suspicious_network_activity + key: log.correlationCandidate.suspiciousNetworkActivity value: match where: |- equals("log.format","cef") && (exists("log.signatureID") && diff --git a/plugins/alerts/kaspersky_action_result_test.go b/plugins/alerts/kaspersky_action_result_test.go new file mode 100644 index 000000000..e2c21dbd7 --- /dev/null +++ b/plugins/alerts/kaspersky_action_result_test.go @@ -0,0 +1,61 @@ +package main + +import ( + "encoding/json" + "os" + "testing" + + "github.com/threatwinds/go-sdk/plugins" + "github.com/tidwall/gjson" +) + +// Synthetic CEF and KSC records cover final decisions and IP roles. The +// isolated EventProcessor parser is replayed separately on the same inputs. +func TestKasperskyActionResultRaw(t *testing.T) { + var cases []struct { + Name string `json:"name"` + Raw string `json:"raw"` + DataSource string `json:"dataSource"` + Result string `json:"result"` + Expected map[string]string `json:"expected"` + Absent []string `json:"absent"` + Initial map[string]any `json:"initial"` + } + data, err := os.ReadFile("testdata/kaspersky_action_result.json") + if err != nil { + t.Fatal(err) + } + if err := json.Unmarshal(data, &cases); err != nil { + t.Fatal(err) + } + if len(cases) < 17 { + t.Fatal("outcome and parser regression classes are missing") + } + config := kaspConfig(t) + cache := plugins.NewCELCache("kaspersky-final-outcome") + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + out := kaspParse(t, config, tc.Raw, tc.DataSource, cache, tc.Initial) + if got := gjson.Get(out, "actionResult").String(); got != tc.Result { + t.Errorf("actionResult = %q; want %q", got, tc.Result) + } + for path, want := range tc.Expected { + got := gjson.Get(out, path) + if !got.Exists() || got.String() != want { + t.Errorf("%s = %q; want %q", path, got.String(), want) + } + } + for _, path := range tc.Absent { + if gjson.Get(out, path).Exists() { + t.Errorf("unexpected %s", path) + } + } + for _, result := range []string{"success", "failure", "denied"} { + matched, err := cache.Eval(`equals("actionResult","`+result+`")`, out) + if err != nil || matched != (tc.Result == result) { + t.Errorf("outcome predicate %s = %v (%v)", result, matched, err) + } + } + }) + } +} diff --git a/plugins/alerts/kaspersky_contract_test.go b/plugins/alerts/kaspersky_contract_test.go index c614c648f..ce9f4c438 100644 --- a/plugins/alerts/kaspersky_contract_test.go +++ b/plugins/alerts/kaspersky_contract_test.go @@ -1,9 +1,8 @@ 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. +// Grok patterns are consumed in order, with whitespace trimmed before each +// match, as the EventProcessor grok plugin does. External geolocation is not run. import ( "bytes" "encoding/json" @@ -77,21 +76,13 @@ func kaspConfig(t *testing.T) *plugins.Config { } return c } -func kaspRegex(t *testing.T, g *plugins.Grok, cfg *plugins.Config) *regexp.Regexp { +func kaspRegex(t *testing.T, pattern string, 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()) + tmpl, e := template.New("grok").Option("missingkey=error").Parse(pattern) if e != nil { t.Fatal(e) } @@ -105,9 +96,25 @@ func kaspRegex(t *testing.T, g *plugins.Grok, cfg *plugins.Config) *regexp.Regex } return r } -func kaspParse(t *testing.T, cfg *plugins.Config, raw string, dataSource string, cache *plugins.CELCache) string { + +// kaspStoredName is the name the parser plugins store for a grok, rename or add target: +// utils.SanitizeField keeps only letters, digits and dots. +func kaspStoredName(name string) string { + utils.SanitizeField(&name) + return name +} + +func kaspParse(t *testing.T, cfg *plugins.Config, raw string, dataSource string, cache *plugins.CELCache, initial ...map[string]any) string { t.Helper() draft := map[string]any{"raw": raw, "dataType": "antivirus-kaspersky", "dataSource": dataSource, "log": map[string]any{}} + if len(initial) > 0 { + for key, value := range initial[0] { + if key != "action" && key != "actionResult" { + t.Fatalf("unexpected seeded field %s", key) + } + draft[key] = value + } + } for _, stage := range cfg.Pipeline { matched := false for _, dataType := range stage.DataTypes { @@ -156,20 +163,34 @@ func kaspParse(t *testing.T, cfg *plugins.Config, raw string, dataSource 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 { + fields := make(map[string]string) + matched := 0 + for _, p := range g.Patterns { + str = strings.TrimSpace(str) + if str == "" { + break + } + r := kaspRegex(t, p.Pattern, cfg) + loc := r.FindStringIndex(str) + if loc == nil || loc[0] != 0 || loc[1] == 0 { + break + } + value := str[:loc[1]] if p.FieldName != "" { - kaspPut(draft, p.FieldName, m[r.SubexpIndex(fmt.Sprintf("f%d", i))], false) + fields[p.FieldName] = strings.TrimSpace(value) + } + str = str[loc[1]:] + matched++ + } + if matched == len(g.Patterns) { + for field, value := range fields { + kaspPut(draft, kaspStoredName(field), value, 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, kaspStoredName(s.Rename.To), v, false) kaspPut(draft, p, nil, true) break } @@ -196,7 +217,7 @@ func kaspParse(t *testing.T, cfg *plugins.Config, raw string, dataSource string, 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) + kaspPut(draft, kaspStoredName(s.Add.Params["key"].GetStringValue()), s.Add.Params["value"].AsInterface(), false) case "delete": for _, p := range s.Delete.Fields { kaspPut(draft, p, nil, true) diff --git a/plugins/alerts/kaspersky_history_test.go b/plugins/alerts/kaspersky_history_test.go index 155c6d12a..82a7a89a6 100644 --- a/plugins/alerts/kaspersky_history_test.go +++ b/plugins/alerts/kaspersky_history_test.go @@ -37,7 +37,10 @@ func TestKasperskySDKHistory(t *testing.T) { 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) + if kasperskyHistoryMarkers[name] == "" { + t.Fatalf("history rule %s has no correlation marker", name) + } + paths = append(paths, "log.correlationCandidate."+kasperskyHistoryMarkers[name]) } } for _, path := range paths { @@ -195,7 +198,8 @@ func TestKasperskySDKHistory(t *testing.T) { 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 + // The filter stores this marker and the rule counts it under the same name. + marker := "log.correlationCandidate." + kasperskyHistoryMarkers[tc.rule] terms = map[string]string{"dataSource": "collector-test", "log.endpointKeyType": "ip", marker: "match"} notTerms = map[string]string{} if tc.cross { diff --git a/plugins/alerts/kaspersky_marker_names_test.go b/plugins/alerts/kaspersky_marker_names_test.go new file mode 100644 index 000000000..972eccade --- /dev/null +++ b/plugins/alerts/kaspersky_marker_names_test.go @@ -0,0 +1,166 @@ +package main + +// The parser plugins keep only letters, digits and dots in the field names they write +// (go-sdk utils.SanitizeField), while rules look names up exactly as written. A marker +// added as log.correlationCandidate.lateral_movement_indicators is stored as +// ...lateralmovementindicators, so a history rule counting the underscored name never fires. +import ( + "encoding/json" + "os" + "path/filepath" + "regexp" + "slices" + "strings" + "testing" + + "github.com/threatwinds/go-sdk/plugins" + "github.com/threatwinds/go-sdk/utils" + "google.golang.org/protobuf/encoding/protojson" +) + +// kasperskyHistoryMarkers maps each Kaspersky history rule (file name without extension) +// to the correlation marker that filters/antivirus/kaspersky.yml adds and the rule counts. +var kasperskyHistoryMarkers = map[string]string{ + "data_exfiltration_attempts": "dataExfiltrationAttempts", + "kaspersky_ransomware_behavior": "kasperskyRansomwareBehavior", + "lateral_movement_indicators": "lateralMovementIndicators", + "suspicious_network_activity": "suspiciousNetworkActivity", +} + +// Every marker the Kaspersky filter adds and every marker a Kaspersky rule reads (where, +// history fields and placeholders, groupBy, deduplicateBy) must be the same string, made +// only of letters, digits and dots, so the stored name is the name the rule looks up. +func TestKasperskyCorrelationMarkerNames(t *testing.T) { + const prefix = "log.correlationCandidate." + clean := regexp.MustCompile(`^[A-Za-z0-9.]+$`) + kept := func(name string) bool { + stored := name + utils.SanitizeField(&stored) + return stored == name && clean.MatchString(name) + } + + written := map[string]string{} + for _, stage := range kaspConfig(t).Pipeline { + if !slices.Contains(stage.DataTypes, "antivirus-kaspersky") { + continue + } + for _, step := range stage.Steps { + names := []string{} + if s := step.Grok; s != nil { + for _, p := range s.Patterns { + if p.FieldName != "" { + names = append(names, p.FieldName) + } + } + } + if s := step.Rename; s != nil { + names = append(names, s.To) + } + if s := step.Csv; s != nil { + names = append(names, s.Headers...) + } + if s := step.Add; s != nil { + key := s.Params["key"].GetStringValue() + names = append(names, key) + if strings.HasPrefix(key, prefix) { + value := s.Params["value"].GetStringValue() + if previous, ok := written[key]; ok && previous != value { + t.Errorf("filter adds %s as %q and %q", key, previous, value) + } + written[key] = value + } + } + for _, name := range names { + if !kept(name) { + t.Errorf("filter writes %q, which the parser stores under another name", name) + } + } + } + } + + reference := regexp.MustCompile(`log\.correlationCandidate\.[^"'\s,()\[\]{}]*`) + read := map[string]map[string]bool{} + err := filepath.WalkDir("../../rules", func(path string, d os.DirEntry, err error) error { + if err != nil || d.IsDir() || (filepath.Ext(path) != ".yml" && filepath.Ext(path) != ".yaml") { + return err + } + b, err := utils.ReadPbYaml(path) + if err != nil { + return err + } + var head struct { + DataTypes []string `json:"dataTypes"` + } + if err = json.Unmarshal(b, &head); err != nil || !slices.Contains(head.DataTypes, "antivirus-kaspersky") { + return err + } + rule := new(plugins.Rule) + if err = protojson.Unmarshal(b, rule); err != nil { + return err + } + name := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)) + texts := []string{rule.Where} + texts = append(texts, rule.GroupBy...) + texts = append(texts, rule.DeduplicateBy...) + var searches func([]*plugins.SearchRequest) + searches = func(list []*plugins.SearchRequest) { + for _, search := range list { + for _, term := range search.With { + value := term.Value.GetStringValue() + texts = append(texts, term.Field, value) + if strings.HasPrefix(term.Field, prefix) && written[term.Field] != value { + t.Errorf("%s counts %s = %q; the filter adds %q", path, term.Field, value, written[term.Field]) + } + } + searches(search.Or) + } + } + searches(rule.AfterEvents) + searches(rule.Correlation) + for _, text := range texts { + for _, marker := range reference.FindAllString(text, -1) { + marker = strings.TrimSuffix(marker, ".keyword") + if read[marker] == nil { + read[marker] = map[string]bool{} + } + read[marker][name] = true + if !kept(marker) { + t.Errorf("%s reads %q, which the parser never stores under that name", path, marker) + } + if _, ok := written[marker]; !ok { + t.Errorf("%s reads %q, which the Kaspersky filter does not add", path, marker) + } + } + } + return nil + }) + if err != nil { + t.Fatal(err) + } + + expected := map[string]bool{} + for rule, marker := range kasperskyHistoryMarkers { + expected[prefix+marker] = true + if _, ok := written[prefix+marker]; !ok { + t.Errorf("filter does not add %s%s for %s", prefix, marker, rule) + } + if !read[prefix+marker][rule] { + t.Errorf("%s does not read %s%s", rule, prefix, marker) + } + } + for marker, rules := range read { + for rule := range rules { + if prefix+kasperskyHistoryMarkers[rule] != marker { + t.Errorf("%s reads %s, expected only %s%s", rule, marker, prefix, kasperskyHistoryMarkers[rule]) + } + } + } + for marker := range written { + if !expected[marker] { + t.Errorf("filter adds %s, which no Kaspersky history rule counts", marker) + } + } + if len(written) != 4 || len(kasperskyHistoryMarkers) != 4 { + t.Errorf("marker coverage: filter adds %d, table lists %d", len(written), len(kasperskyHistoryMarkers)) + } +} diff --git a/plugins/alerts/testdata/filter-contracts/kaspersky.json b/plugins/alerts/testdata/filter-contracts/kaspersky.json index d2150baad..820eb00a7 100644 --- a/plugins/alerts/testdata/filter-contracts/kaspersky.json +++ b/plugins/alerts/testdata/filter-contracts/kaspersky.json @@ -34,7 +34,7 @@ "format": "cef", "endpointKey": "forged", "correlationCandidate": { - "kaspersky_ransomware_behavior": "match" + "kasperskyRansomwareBehavior": "match" } } }, diff --git a/plugins/alerts/testdata/kaspersky_action_result.json b/plugins/alerts/testdata/kaspersky_action_result.json new file mode 100644 index 000000000..843a25526 --- /dev/null +++ b/plugins/alerts/testdata/kaspersky_action_result.json @@ -0,0 +1,218 @@ +[ + { + "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": "synthetic-kaspersky", + "result": "success", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "Allow" + } + }, + { + "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": "synthetic-kaspersky", + "result": "success", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "Allowed" + } + }, + { + "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": "synthetic-kaspersky", + "result": "denied", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "BLOCKED" + } + }, + { + "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": "synthetic-kaspersky", + "result": "denied", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "Block" + } + }, + { + "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": "synthetic-kaspersky", + "result": "", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "delete" + }, + "absent": [ + "actionResult" + ] + }, + { + "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": "synthetic-kaspersky", + "result": "", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "detected" + }, + "absent": [ + "actionResult" + ] + }, + { + "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": "synthetic-kaspersky", + "result": "", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "unknown" + }, + "absent": [ + "actionResult" + ] + }, + { + "name": "cef-denied", + "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=Denied", + "dataSource": "synthetic-kaspersky", + "result": "denied", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "Denied" + } + }, + { + "name": "cef-drop", + "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=drop", + "dataSource": "synthetic-kaspersky", + "result": "denied", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "drop" + } + }, + { + "name": "cef-rejected", + "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=Rejected", + "dataSource": "synthetic-kaspersky", + "result": "denied", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "Rejected" + } + }, + { + "name": "cef-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": "synthetic-kaspersky", + "result": "denied", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "quarantine" + } + }, + { + "name": "cef-explicit-failed-operation", + "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=Failed", + "dataSource": "synthetic-kaspersky", + "result": "failure", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "action": "Failed" + } + }, + { + "name": "native-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": "synthetic-kaspersky", + "result": "", + "expected": { + "target.ip": "192.0.2.8", + "log.eventType": "KLSRV_HOST_STATUS_CRITICAL", + "log.format": "ksc-syslog" + }, + "absent": [ + "actionResult", + "origin.ip" + ] + }, + { + "name": "native-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": "synthetic-kaspersky", + "result": "", + "expected": { + "target.ip": "192.0.2.8", + "log.eventType": "KLSRV_HOST_OUT_CONTROL", + "log.format": "ksc-syslog" + }, + "absent": [ + "actionResult", + "origin.ip" + ] + }, + { + "name": "native-health-clears-stale-success", + "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": "synthetic-kaspersky", + "result": "", + "initial": { + "actionResult": "success" + }, + "expected": { + "target.ip": "192.0.2.8", + "log.format": "ksc-syslog" + }, + "absent": [ + "actionResult", + "origin.ip" + ] + }, + { + "name": "cef-last-message-keeps-full-text", + "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": "synthetic-kaspersky", + "result": "", + "expected": { + "origin.ip": "198.51.100.9", + "target.ip": "192.0.2.8", + "log.msg": "injection into lsass" + }, + "absent": [ + "actionResult" + ] + }, + { + "name": "cef-escaped-equals-cannot-spoof-source", + "raw": "CEF:0|Kaspersky|Security Center|14|3|Synthetic event|3|msg=text src\\=198.51.100.99 dst=192.0.2.8", + "dataSource": "synthetic-kaspersky", + "result": "", + "expected": { + "log.msg": "text src\\=198.51.100.99", + "target.ip": "192.0.2.8" + }, + "absent": [ + "origin.ip", + "actionResult" + ] + } +] diff --git a/plugins/alerts/testdata/kaspersky_raw.json b/plugins/alerts/testdata/kaspersky_raw.json index 6c84d4791..bfd5a556e 100644 --- a/plugins/alerts/testdata/kaspersky_raw.json +++ b/plugins/alerts/testdata/kaspersky_raw.json @@ -717,11 +717,10 @@ "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" + "action": "quarantine", + "actionResult": "denied" }, - "absent": [ - "actionResult" - ], + "absent": [], "matches": [] }, { diff --git a/rules/antivirus/kaspersky/data_exfiltration_attempts.yml b/rules/antivirus/kaspersky/data_exfiltration_attempts.yml index e42ae55b0..a0ae15629 100644 --- a/rules/antivirus/kaspersky/data_exfiltration_attempts.yml +++ b/rules/antivirus/kaspersky/data_exfiltration_attempts.yml @@ -1,4 +1,4 @@ -# Rule version v1.1.0 +# Rule version v1.1.1 dataTypes: - antivirus-kaspersky name: Kaspersky Data Exfiltration Attempts Detection @@ -52,7 +52,7 @@ correlation: - field: log.endpointKey operator: filter_term value: '{{.log.endpointKey}}' - - field: log.correlationCandidate.data_exfiltration_attempts + - field: log.correlationCandidate.dataExfiltrationAttempts operator: filter_term value: match - field: log.cat diff --git a/rules/antivirus/kaspersky/kaspersky_ransomware_behavior.yml b/rules/antivirus/kaspersky/kaspersky_ransomware_behavior.yml index 1ef910a16..a46d8636e 100644 --- a/rules/antivirus/kaspersky/kaspersky_ransomware_behavior.yml +++ b/rules/antivirus/kaspersky/kaspersky_ransomware_behavior.yml @@ -1,4 +1,4 @@ -# Rule version v1.1.0 +# Rule version v1.1.1 dataTypes: - antivirus-kaspersky name: Kaspersky Ransomware Behavior Detection @@ -43,7 +43,7 @@ correlation: - field: log.endpointKey operator: filter_term value: '{{.log.endpointKey}}' - - field: log.correlationCandidate.kaspersky_ransomware_behavior + - field: log.correlationCandidate.kasperskyRansomwareBehavior operator: filter_term value: match within: 10m diff --git a/rules/antivirus/kaspersky/lateral_movement_indicators.yml b/rules/antivirus/kaspersky/lateral_movement_indicators.yml index 5fa70f97b..e1c61496e 100644 --- a/rules/antivirus/kaspersky/lateral_movement_indicators.yml +++ b/rules/antivirus/kaspersky/lateral_movement_indicators.yml @@ -1,4 +1,4 @@ -# Rule version v1.1.0 +# Rule version v1.1.1 dataTypes: - antivirus-kaspersky name: Lateral Movement Indicators Detection @@ -40,7 +40,7 @@ correlation: - field: log.endpointKey operator: filter_term value: '{{.log.endpointKey}}' - - field: log.correlationCandidate.lateral_movement_indicators + - field: log.correlationCandidate.lateralMovementIndicators operator: filter_term value: match within: 2h diff --git a/rules/antivirus/kaspersky/suspicious_network_activity.yml b/rules/antivirus/kaspersky/suspicious_network_activity.yml index 11d5adfc3..139933df7 100644 --- a/rules/antivirus/kaspersky/suspicious_network_activity.yml +++ b/rules/antivirus/kaspersky/suspicious_network_activity.yml @@ -1,4 +1,4 @@ -# Rule version v1.1.0 +# Rule version v1.1.1 dataTypes: - antivirus-kaspersky name: Kaspersky Suspicious Network Activity Detection @@ -52,7 +52,7 @@ correlation: - field: log.endpointKey operator: filter_term value: '{{.log.endpointKey}}' - - field: log.correlationCandidate.suspicious_network_activity + - field: log.correlationCandidate.suspiciousNetworkActivity operator: filter_term value: match - field: target.ip