From 5c9af6d256e1e904f23f654414f7974bf6a6dfbb Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Wed, 16 Sep 2026 20:14:27 -0400 Subject: [PATCH] fix: align filters and correlation rules with the event schema --- filters/DICTIONARY_AUDIT.md | 116 + filters/antivirus/bitdefender_gz.yml | 59 +- filters/antivirus/deceptive-bytes.yml | 40 +- filters/antivirus/esmc-eset.yml | 128 +- filters/antivirus/kaspersky.yml | 94 +- filters/antivirus/sentinel-one.yml | 12 +- filters/aws/aws.yml | 26 +- filters/azure/azure-eventhub.yml | 194 +- filters/cisco/asa.yml | 469 ++-- filters/cisco/cs_switch.yml | 54 +- filters/cisco/firepower.yml | 447 ++-- filters/cisco/meraki.yml | 211 +- filters/crowdstrike/crowdstrike.yml | 28 +- filters/fortinet/fortinet.yml | 111 +- filters/fortinet/fortiweb.yml | 18 +- filters/generic/generic.yml | 2 +- filters/github/github.yml | 14 +- filters/google/gcp.yml | 113 +- filters/ibm/ibm_aix.yml | 72 +- filters/ibm/ibm_as_400.yml | 9 +- filters/linux/linux.yml | 106 +- filters/mikrotik/mikrotik-fw.yml | 71 +- filters/netflow/netflow.yml | 67 +- filters/office365/o365.yml | 62 +- filters/paloalto/pa_firewall.yml | 207 +- filters/pfsense/pfsense_fw.yml | 75 +- filters/sonicwall/sonic_wall.yml | 65 + filters/sophos/sophos_central.yml | 20 +- filters/sophos/sophos_xg_firewall.yml | 134 +- filters/suricata/suricata.yml | 151 +- filters/syslog/syslog-generic.yml | 2 +- filters/vmware/vmware-esxi.yml | 14 +- filters/windows/windows-events.yml | 73 +- plugins/alerts/filter_contract_test.go | 188 ++ plugins/alerts/filter_normalization_test.go | 230 ++ plugins/alerts/grouping.go | 38 + plugins/alerts/grouping_test.go | 68 + plugins/alerts/main.go | 25 +- .../alerts/testdata/filter-normalization.json | 1964 +++++++++++++++++ .../bitdefender_gz/apt_detection.yml | 2 +- .../high_severity_threat_detection.yml | 2 +- .../malware_outbreak_multiple_hosts.yml | 2 +- .../multiple_malware_from_single_source.yml | 2 +- .../network_threat_detection.yml | 2 +- .../phishing_access_blocked.yaml | 2 +- .../quarantine_failure_detection.yml | 2 +- .../esmc-eset/exploit_detection_events.yml | 2 +- .../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 +- .../advanced_malware_protection_alerts.yml | 2 +- rules/cloud/google/gcp_iam_policy_changed.yml | 4 +- .../google/gcp_logging_sink_modified.yml | 2 +- rules/crowdstrike/inhibit_system_recovery.yml | 4 +- ...failures_(possible_brute_force_attack).yml | 2 +- .../os_credential_dumping_activity.yml | 4 +- ...y_defenses_impaired_or_policy_disabled.yml | 2 +- ...cious_downloader_execution_linux_macos.yml | 2 +- ...uspicious_encoded_powershell_execution.yml | 2 +- .../suspicious_native_downloaders.yml | 2 +- .../windows_event_log_clearing.yml | 4 +- .../fortiweb/fortiweb_webshell_upload.yml | 2 +- .../json-input/json_injection_attempts.yml | 2 +- .../attempt_to_disable_syslog_service.yml | 4 +- rules/linux/chattr_immutable_file.yml | 4 +- .../debian_family/auditd_syslog_disabling.yml | 4 +- .../container_escape_techniques.yml | 4 +- .../debian_family/debian_kernel_exploits.yml | 4 +- .../debian_specific_rootkits.yml | 2 +- .../linux/debian_family/etc_shadow_access.yml | 4 +- .../kernel_exploit_indicators.yml | 4 +- .../suid_sgid_binary_creation.yml | 4 +- .../suspicious_binary_in_tmp.yml | 2 +- rules/linux/disable_selinux_attempt.yml | 4 +- rules/linux/insmod_kernel_module_load.yml | 4 +- rules/linux/linux_hping_activity.yml | 4 +- rules/linux/linux_nping_activity.yml | 4 +- rules/linux/log_files_deleted.yml | 4 +- .../openshift_security_violations.yml | 4 +- .../rhel_family/rhel_kernel_exploits.yml | 4 +- .../rhel_family/rhel_specific_malware.yml | 2 +- .../rhel_family/rpm_database_tampering.yml | 2 +- .../rhel_family/secure_boot_violations.yml | 4 +- .../rhel_family/selinux_policy_violations.yml | 2 +- .../yum_dnf_repository_attacks.yml | 2 +- rules/linux/tc_bpf_filter.yml | 4 +- rules/macos/endpoint_security_bypass.yml | 6 +- .../mikrotik_fw/ssh_brute_force_attempts.yml | 6 +- rules/netflow/tor_usage_detection.yml | 2 +- rules/nids/suricata/ddos_attack_patterns.yml | 16 +- .../anti_phishing_policy_bypasses.yml | 8 +- ...365_potential_password_spraying_attack.yml | 8 +- rules/office365/dlp_policy_violations.yml | 2 +- .../information_barriers_violations.yml | 2 +- ...ible_succesfull_password_guessing_o365.yml | 6 +- rules/office365/safe_links_click_patterns.yml | 2 +- ...severity_suricata_alerts_were_detected.yml | 2 +- ...severity_suricata_alerts_were_detected.yml | 2 +- .../vmware-esxi/esxi_account_manipulation.yml | 2 +- rules/vmware/vmware-esxi/esxi_disk_theft.yml | 2 +- .../esxi_firewall_modification.yml | 4 +- .../vmware-esxi/esxi_host_compromise.yml | 4 +- .../vmware-esxi/esxi_ransomware_detection.yml | 2 +- rules/vmware/vmware-esxi/esxi_ssh_access.yml | 2 +- .../vmware-esxi/esxi_syslog_disruption.yml | 2 +- .../vmware-esxi/esxi_vib_sideloading.yml | 2 +- .../hypervisor_escape_attempts.yml | 2 +- .../vmware-esxi/powercli_script_execution.yml | 2 +- .../vmware-esxi/vcenter_server_attacks.yml | 6 +- .../vmware-esxi/vm_escape_detection.yml | 2 +- .../vmware_tools_vulnerabilities.yml | 6 +- .../vmware/vmware-esxi/vsphere_api_abuse.yml | 6 +- .../windows/adfs_authentication_anomalies.yml | 2 +- rules/windows/asrep_roasting_detection.yml | 4 +- rules/windows/audit_log_was_cleared.yml | 2 +- rules/windows/bruteforce_attack.yml | 4 +- ...iple_logon_failure_followed_by_success.yml | 4 +- rules/windows/certificate_services_abuse.yml | 2 +- rules/windows/golden_ticket_detection.yml | 2 +- rules/windows/kerberoasting_detection.yml | 2 +- rules/windows/lsass_memdump_handle_access.yml | 2 +- ...rse_tunneling_using_stolen_credentials.yml | 2 +- .../windows/ransom_multiple_file_deletion.yml | 2 +- rules/windows/sam_database_access.yml | 2 +- rules/windows/sid_history_injection.yml | 2 +- rules/windows/silver_ticket_detection.yml | 4 +- rules/windows/smbv1_usage_detection.yml | 4 +- 138 files changed, 5034 insertions(+), 1065 deletions(-) create mode 100644 filters/DICTIONARY_AUDIT.md create mode 100644 plugins/alerts/filter_contract_test.go create mode 100644 plugins/alerts/filter_normalization_test.go create mode 100644 plugins/alerts/grouping.go create mode 100644 plugins/alerts/grouping_test.go create mode 100644 plugins/alerts/testdata/filter-normalization.json diff --git a/filters/DICTIONARY_AUDIT.md b/filters/DICTIONARY_AUDIT.md new file mode 100644 index 000000000..2382b62d8 --- /dev/null +++ b/filters/DICTIONARY_AUDIT.md @@ -0,0 +1,116 @@ +# Filter and correlation-rule dictionary audit + +Draft review only. No production deployment or merge is part of this change. + +## Scope and authority + +Reviewed all **36 filter configurations and 635 rule files**, including `.yaml` as well as `.yml`, from UTMStack v11 commit `6c3af7eba9c8ec9b5ede5101feb308b2fca661d0`. Compared them with both supplied data dictionaries, the ThreatWinds SDK schema, the filter/rule wiki, and vendor documentation for the disputed outcomes. + +The checked-in plugins pin **go-sdk v1.1.31**. Tests use the alerts plugin's existing module and dependency versions. The current SDK snapshot (`36461913c164f40176a7da881706970e9fd7c84d`) has the same `plugins.proto`. Wiki snapshot: `c18b54bd5ea5a34abb0e690458d73f89835edd29`. + +The SDK determines names and types; the wiki supplies conventions and transformation semantics. In particular: matching pipeline stages run sequentially; `rename` moves a field; grok/CSV targets are literal; JSON/KV extraction lives under `log`; event paths and alert paths differ. `afterEvents` is a supported alias for `correlation`. These were considered before identifying defects. + +## Confirmed defects and changes + +| Area | Before | Draft behavior | +|---|---|---| +| Filter configuration | Azure supplies a scalar `dataTypes`; generic/syslog/IBM use misspelled grok keys; Palo Alto has misnested grok steps and CSV `from` keys | Configurations decode against the real SDK, including strict unknown-key validation | +| Lost standard fields | Top-level `command`, `from.host`, `origin.hostname`, and incorrectly cased Palo Alto counters disappear during Event conversion | Use `origin.command`, `origin.host`, and the exact counter names; Palo Alto client/server counters belong to the originating side | +| Outcome vocabulary | Filters emit `accepted`, `failed`, `blocked`, `Succeeded` and other aliases | Emit `success`, `failure`, or `denied` for known outcomes; dependent rules retain legacy aliases where needed | +| Cisco ASA/FTD | Invalid IPsec receives are accepted; a scan report is treated as success; an ACL result is overwritten after its source value is mutated | IPsec failures remain failures, scan reports have no invented outcome, and ACL results are classified from preserved vendor values. Correct the invalid `lgreaterOrEqual` condition | +| FortiGate | Quoted denial can miss classification; resets are treated as policy blocks; security-profile blocks can coexist with policy acceptance | Classify after trimming, give explicit UTM denial precedence, and record closure separately from outcome | +| Sophos XG | A blocked request with a block-page HTTP 200 can become successful | Explicit denial wins; HTTP status is a fallback, and non-policy HTTP failures are failures | +| Palo Alto | Traffic outcome is conditional on unrelated status fields; `Submitted` becomes successful | Honor explicit network denial, preserve unknown/pending outcomes, and classify administrative completion separately | +| Suricata | Per-signature `allowed` is treated as success even when final verdict drops traffic; protocol presence can create `action=success`; priorities 1 and 3 are inverted | Final packet/flow denial wins; `allowed` alone proves no successful connection; explicit pass/established flow evidence is required. Priority 1 is highest | +| ESET | Event names are placed in `actionResult` | Keep event type separately and classify actual vendor result/action values | +| Windows/Linux | Account disable/delete/lock success is labeled blocked; NTLM zero hex status is failure; negative audit exit is written into unsigned `statusCode` | Correct event outcomes; retain signed exit in `log.exitCode`; put executable path in `origin.path` and working directory under `log` | +| Address/actor mapping | Kaspersky reporting-agent metadata overwrites source identity; CrowdStrike LocalIP overwrites an explicit SourceIp; Bitdefender source fallback overwrites attacker IP | Preserve reporting metadata separately, retain explicit source/attacker addresses, and map standard CEF side identities | +| IP values | Placeholders, hostnames and compound endpoints are stored as IPs | Preserve unparseable values under `log.unparsed*Ip`; split O365 IPv4-with-port and bracketed IPv6 endpoints without truncating bare IPv6; preserve the reported endpoint | +| Cloud standard fields | Azure region/account name/status text are treated as country/hostname/connection status; AWS and GitHub actors remain only in vendor fields | Keep Azure metadata under appropriate `log` fields; promote known AWS/GitHub/Windows actor fields without consuming fields used by rules; handle Azure's common top-level `resultType` | +| Severity/protocol | Multiple severity scales and numeric JSON protocol values enter standard string fields | Map known priorities to wiki severity values; retain Bitdefender CEF priority for threshold rules; translate known numeric IP protocols and preserve unknown numbers separately | +| Rule grouping | `origin.*` and `*.hostname` are used on Alerts; raw grouping fields lack `lastEvent.` | Use mapped `adversary.*`/`target.*`, exact `host` names, and the documented `lastEvent.*` prefix. Respect each rule's adversary setting | +| Alert plugin | Documented `lastEvent.*` cannot resolve on the wire Alert; non-scalar values can enable a name-only search | Resolve `lastEvent` to the same final event indexed by `newAlert`; skip arrays/maps unless a scalar member is selected | +| Rule predicates | Two GCP rules call `oneof`; FortiWeb has invalid CEL regex escapes; Suricata uses unsupported integer `safe` defaults | Correct spelling/escaping/overloads; use actual EVE flow age and support raw/sanitized counter names | +| O365 correlation | A rule titled successful password guessing triggers on failure and references absent `log.clientIP`; password-spray history counts unrelated activity | Trigger successful guessing on `UserLoggedIn` success, correlate prior `UserLoginFailed` by standard user/IP, and constrain spray history to the triggering failure action/result | +| Other rule consumers | Anti-phish policy rule reads renamed O365 fields; Bitdefender phishing rule matches blocked pages; protocol comparisons disagree with source output | Use surviving standard fields, match Bitdefender `reportOnly` for the rule explicitly describing an unblocked page, and fix protocol comparisons | + +The outcome convention describes the action being logged. A firewall policy allowing a packet is **not proof of a completed TCP handshake**. Successful account disabling is the success of an administrative operation. An absent outcome must not be interpreted as success by downstream detection. + +## Validation + +- Strict SDK decoding passes for all **36 filters and 635 rules**. Baseline had five configurations with schema/unknown-key diagnostics; Azure's scalar `dataTypes` is a hard decoding failure. +- All filter/rule conditions compile using the SDK's actual CEL implementation. Five baseline compilation defects are corrected. Missing nested fields in a deliberately empty compile-check sample are not treated as parser defects. +- **124 synthetic normalization cases pass across 17 filters**, with positive and negative detection-predicate assertions, final SDK Event conversion, and before/after outputs retained in the local audit report. +- Alerts plugin unit tests cover standard side fields, `lastEvent` selection, nested array members, missing/empty events, and rejecting non-scalar grouping terms. +- `go test ./...` in `plugins/alerts` and `git diff --check` pass. Regression fixtures and tests are included in this draft. + +Run from the repository root: + +```sh +cd plugins/alerts +go test ./... +``` + +The normalization test is explicitly a **model of documented normalization steps**, supplied with synthetic extraction results. It uses the real SDK YAML, CEL, casting helpers and protobuf finalization, but skips JSON/KV/XML/CSV extraction, complex grok, timestamp reformatting, and dynamic plugins. The single greedy copy pattern is modeled. It does not exercise the closed parsing engine, OpenSearch historical searches, the threat-intelligence feed engine, or customer environments. Predicate assertions test trigger conditions, not full historical alert generation. + +## Choices preserved and remaining validation + +- `log.*` is open. The absence of an explicit writer is not proof that JSON/KV data can never supply a field. Empty grok field names used for separators are valid and are not flagged. +- Numeric strings accepted by SDK comparisons/protobuf are not reported as type defects. Integral floating-point JSON values are not automatically invalid unsigned integers. +- The dictionaries disagree about protocol casing and some file/email meanings. Existing textual protocol casing and vendor `action` vocabulary are preserved, except concrete consumer mismatches. A platform-wide action migration would require coordinated changes to many more rules. +- No blanket `adversary: origin`/`target` reversal was applied to endpoint detections. An agent, a compromised endpoint, and a remote attacker are different roles; event-specific evidence is needed for further changes. +- The macOS rule's impossible `system.hostname` path is corrected to `origin.host`. The shipped macOS filters do not themselves supply a host identity, so its availability from upstream metadata still needs a representative event. Missing identity does not justify broadening the search across hosts. +- Review custom customer rules that match legacy `actionResult` or numeric `severity` values before any eventual rollout. Included rules are updated, and Bitdefender retains its CEF priority under `log.cefSeverity`. Short correlation windows may span historical/new outcome spellings during migration. +- Confirm filter ordering/extraction with representative raw logs and verify the resulting alerts in a staging engine. The reported customer threat-intelligence noise has **not** been reproduced against a live customer instance; these fixes address demonstrated causes, not a measured reduction in that customer's alert volume. + +## Coverage by filter + +All entries received schema, literal-value and condition review. “No edit” means no confirmed change in this draft, not certification against every vendor log variant. + +| Filter | Disposition | Synthetic normalization cases | +|---|---|---:| +| `antivirus/bitdefender_gz.yml` | Corrected | 8 | +| `antivirus/deceptive-bytes.yml` | Corrected | 0 | +| `antivirus/esmc-eset.yml` | Corrected | 2 | +| `antivirus/kaspersky.yml` | Corrected | 2 | +| `antivirus/sentinel-one.yml` | Corrected | 0 | +| `aws/aws.yml` | Corrected | 2 | +| `azure/azure-eventhub.yml` | Corrected | 11 | +| `cisco/asa.yml` | Corrected | 12 | +| `cisco/cs_switch.yml` | Corrected | 0 | +| `cisco/firepower.yml` | Corrected | 12 | +| `cisco/meraki.yml` | Corrected | 0 | +| `crowdstrike/crowdstrike.yml` | Corrected | 2 | +| `fortinet/fortinet.yml` | Corrected | 6 | +| `fortinet/fortiweb.yml` | Corrected | 0 | +| `generic/generic.yml` | Corrected | 0 | +| `github/github.yml` | Corrected | 0 | +| `google/gcp.yml` | Corrected | 13 | +| `ibm/ibm_aix.yml` | Corrected | 0 | +| `ibm/ibm_as_400.yml` | Corrected | 0 | +| `json/json-input.yml` | No edit | 0 | +| `linux/linux.yml` | Corrected | 3 | +| `macos/macos-syslog.yml` | No edit | 0 | +| `macos/macos.yml` | No edit | 0 | +| `mikrotik/mikrotik-fw.yml` | Corrected | 0 | +| `netflow/netflow.yml` | Corrected | 0 | +| `office365/o365.yml` | Corrected | 14 | +| `paloalto/pa_firewall.yml` | Corrected | 4 | +| `pfsense/pfsense_fw.yml` | Corrected | 2 | +| `sonicwall/sonic_wall.yml` | Corrected | 0 | +| `sophos/sophos_central.yml` | Corrected | 0 | +| `sophos/sophos_xg_firewall.yml` | Corrected | 5 | +| `suricata/suricata.yml` | Corrected | 18 | +| `syslog/syslog-generic.yml` | Corrected | 0 | +| `utmstack/utmstack.yml` | No edit | 0 | +| `vmware/vmware-esxi.yml` | Corrected | 0 | +| `windows/windows-events.yml` | Corrected | 8 | + +## References used + +- [SDK v1.1.31 schema](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/plugins.proto), [CEL implementation](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/cel.go), [correlation implementation and alias normalization](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/rules.go). +- [Filter implementation](https://github.com/threatwinds/go-sdk/wiki/Implementing-Filters), [step reference](https://github.com/threatwinds/go-sdk/wiki/Filter-Steps-Reference), [standard schema](https://github.com/threatwinds/go-sdk/wiki/Standard-Event-Schema), [rule implementation](https://github.com/threatwinds/go-sdk/wiki/Implementing-Rules), [advanced features](https://github.com/threatwinds/go-sdk/wiki/Advanced-Features), and [CEL overloads](https://github.com/threatwinds/go-sdk/wiki/CEL-Overloads). Architecture, components, examples, best-practice and troubleshooting pages were also reviewed. +- [Cisco invalid IPsec messages](https://www.cisco.com/c/en/us/td/docs/security/asa/syslog/asa-syslog/syslog-messages-400000-to-450001.html), [Cisco scan/shun messages](https://www.cisco.com/c/en/us/td/docs/security/asa/syslog/asa-syslog/syslog-messages-722001-to-776020.html). +- [FortiOS 7.4.9 log reference](https://fortinetweb.s3.amazonaws.com/docs.fortinet.com/v2/attachments/514718ad-8f65-11f0-9bfd-6af4c3636dc7/FortiOS_7.4.9_Log_Reference.pdf), [Palo Alto traffic fields](https://docs.paloaltonetworks.com/ngfw/administration/monitoring/use-syslog-for-monitoring/syslog-field-descriptions/traffic-log-fields), [Sophos syslog reference](https://docs.sophos.com/nsg/sophos-firewall/19.0/syslog/index.html). +- [Suricata EVE format, verdicts and flows](https://docs.suricata.io/en/suricata-8.0.3/output/eve/eve-json-format.html), [Bitdefender syslog events](https://www.bitdefender.com/business/support/en/77212-237090-syslog-events.html). +- [Windows account-disabled event](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4725), [Azure resource-log schema](https://learn.microsoft.com/en-us/azure/azure-monitor/platform/resource-logs-schema), [O365 audit properties](https://learn.microsoft.com/en-us/purview/audit-log-detailed-properties). diff --git a/filters/antivirus/bitdefender_gz.yml b/filters/antivirus/bitdefender_gz.yml index bc73922ea..ea2df8ed2 100644 --- a/filters/antivirus/bitdefender_gz.yml +++ b/filters/antivirus/bitdefender_gz.yml @@ -504,8 +504,8 @@ pipeline: - rename: from: - - log.severity - to: severity + - log.severity + to: log.cefSeverity - rename: from: @@ -560,23 +560,30 @@ pipeline: - rename: from: - - log.BitdefenderGZEventSourceIP + - log.BitdefenderGZEventSourceIP to: origin.ip + where: '!exists("origin.ip")' # Adding actionResult field to indicate whether the action was successful or failed + - add: + function: string + params: + key: actionResult + value: denied + where: oneOf("action", ["blocked", "block", "aph_blocked", "portscan_blocked", "quarantined"]) - add: function: string params: key: actionResult value: success - where: 'oneOf("action", ["blocked", "block", "aph_blocked", "portscan_blocked", "deleted", "disinfected", "quarantined", "restored"])' + where: oneOf("action", ["deleted", "disinfected", "restored"]) - add: function: string params: key: actionResult - value: failed - where: 'oneOf("action", ["still present", "ignored", "no action", "reportOnly"])' + value: failure + where: oneOf("action", ["still present", "ignored", "no action", "reportOnly"]) # Adding geolocation to origin ip - dynamic: @@ -620,4 +627,42 @@ pipeline: - log.dvc - log.request - log.suser - - log.fname \ No newline at end of file + - log.fname + + # Keep addresses in IP fields and retain other source values under log. + - rename: + from: + - origin.ip + to: log.unparsedOriginIp + where: exists("origin.ip") && (!(inCIDR("origin.ip","0.0.0.0/0") || inCIDR("origin.ip","::/0")) || oneOf("origin.ip",["0.0.0.0","::"])) + - rename: + from: + - target.ip + to: log.unparsedTargetIp + where: exists("target.ip") && (!(inCIDR("target.ip","0.0.0.0/0") || inCIDR("target.ip","::/0")) || oneOf("target.ip",["0.0.0.0","::"])) + + # Normalize the source event severity. + - add: + function: string + params: + key: severity + value: info + where: (greaterOrEqual("log.cefSeverity",0) && lessOrEqual("log.cefSeverity",3)) || oneOf("log.cefSeverity",["Low","low","Unknown"]) + - add: + function: string + params: + key: severity + value: warning + where: (greaterOrEqual("log.cefSeverity",4) && lessOrEqual("log.cefSeverity",6)) || oneOf("log.cefSeverity",["Medium","medium"]) + - add: + function: string + params: + key: severity + value: error + where: (greaterOrEqual("log.cefSeverity",7) && lessOrEqual("log.cefSeverity",8)) || oneOf("log.cefSeverity",["High","high"]) + - add: + function: string + params: + key: severity + value: critical + where: (greaterOrEqual("log.cefSeverity",9) && lessOrEqual("log.cefSeverity",10)) || oneOf("log.cefSeverity",["Very-High","Very High","very-high"]) diff --git a/filters/antivirus/deceptive-bytes.yml b/filters/antivirus/deceptive-bytes.yml index c44723e0c..7159e9131 100644 --- a/filters/antivirus/deceptive-bytes.yml +++ b/filters/antivirus/deceptive-bytes.yml @@ -184,7 +184,7 @@ pipeline: pattern: '\,{{.data}}\,' - fieldName: origin.path pattern: '{{.greedy}}\,' - - fieldName: command + - fieldName: origin.command pattern: '{{.greedy}}' source: log.restMessage @@ -412,14 +412,12 @@ pipeline: function: prefix substring: '"' fields: - - command - + - origin.command - trim: function: suffix substring: '"' fields: - - command - + - origin.command - trim: function: prefix substring: '[' @@ -455,29 +453,29 @@ pipeline: function: string params: key: actionResult - value: "blocked" - where: 'exists("log.action") && oneOf("log.action", ["blocked", "prevented"])' + value: denied + where: exists("log.action") && oneOf("log.action", ["blocked", "prevented"]) # Adding severity based on log.severityLabelCharacter - add: - function: 'string' + function: string params: key: severity - value: 'high' + value: error where: oneOf("log.severityLabelCharacter", ["C", "A", "E"]) - add: - function: 'string' + function: string params: key: severity - value: 'medium' + value: warning where: equals("log.severityLabelCharacter", "W") - add: - function: 'string' + function: string params: key: severity - value: 'low' + value: info where: oneOf("log.severityLabelCharacter", ["D", "V", "N", "I", "T"]) # Removing unused fields @@ -493,4 +491,18 @@ pipeline: - log.restMessageToKv - log.pidStatusToKv - log.userWithTrash - - log.severityLabelCharacter \ No newline at end of file + - log.severityLabelCharacter + + # Normalize the source event severity. + - add: + function: string + params: + key: severity + value: critical + where: oneOf("log.severityLabelCharacter",["C","A"]) + - add: + function: string + params: + key: severity + value: debug + where: oneOf("log.severityLabelCharacter",["D","V","T"]) diff --git a/filters/antivirus/esmc-eset.yml b/filters/antivirus/esmc-eset.yml index b270dd182..1205b5024 100644 --- a/filters/antivirus/esmc-eset.yml +++ b/filters/antivirus/esmc-eset.yml @@ -59,11 +59,6 @@ pipeline: from: - log.action to: action - - rename: - from: - - log.event - to: actionResult - where: '!regexMatch("log.result", "(?i)\b(?:denied|blocked|failed)\b")' - rename: from: - log.sourceaddress @@ -86,47 +81,29 @@ pipeline: to: target.port # Adding action result - - add: - function: 'string' - params: - key: actionResult - value: 'denied' - where: regexMatch("log.event", "(?i)\bdenied\b") - - add: - function: 'string' - params: - key: actionResult - value: 'blocked' - where: regexMatch("log.event", "(?i)\bblocked\b") - - add: - function: 'string' - params: - key: actionResult - value: 'failed' - where: regexMatch("log.event", "(?i)\bfailed\b") # Adding severity field based on log.severity - add: - function: 'string' + function: string params: key: severity - value: 'low' + value: info where: oneOf("log.severity", ["INFO", "Info"]) - add: - function: 'string' + function: string params: key: severity - value: 'medium' + value: warning where: oneOf("log.severity", ["WARNING", "Warning"]) - add: - function: 'string' + function: string params: key: severity - value: 'high' + value: error where: oneOf("log.severity", ["ERROR", "Error"]) # Adding geolocation to origin.ip @@ -149,4 +126,95 @@ pipeline: - delete: fields: - log.jsonMessage - - log.severity \ No newline at end of file + - log.severity + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: success + where: regexMatch("log.result", "(?i)^(success|succeeded|successful|ok|done|accepted|accept|allowed|allow|permitted|permit|passed|pass|true)$") + - add: + function: string + params: + key: actionResult + value: failure + where: regexMatch("log.result", "(?i)^(failure|failed|fail|error|invalid|timeout|false)$") + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("log.result", "(?i)^(denied|deny|blocked|block|dropped|drop|rejected|reject|forbidden|unauthorized|quarantined)$") + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("action", "(?i)^(deny|denied|block|blocked)$") || regexMatch("log.event", "(?i)^(deny|denied|block|blocked)$") + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) 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/antivirus/sentinel-one.yml b/filters/antivirus/sentinel-one.yml index e1cf94824..9c19ae9eb 100644 --- a/filters/antivirus/sentinel-one.yml +++ b/filters/antivirus/sentinel-one.yml @@ -368,20 +368,20 @@ pipeline: function: string params: key: actionResult - value: "failed" - where: 'equals("log.threatStatus", "mitigation_failed")' + value: failure + where: equals("log.threatStatus", "mitigation_failed") - add: function: string params: key: actionResult - value: "failed" - where: 'equals("log.status", "failed") && !exists("actionResult")' + value: failure + where: equals("log.status", "failed") && !exists("actionResult") - add: function: string params: key: actionResult - value: "failed" - where: 'contains("log.mitigationStatus", "fail") && !exists("actionResult")' + value: failure + where: contains("log.mitigationStatus", "fail") && !exists("actionResult") # Removing unused fields - delete: diff --git a/filters/aws/aws.yml b/filters/aws/aws.yml index 811ad01b8..bc14b5700 100644 --- a/filters/aws/aws.yml +++ b/filters/aws/aws.yml @@ -234,12 +234,32 @@ pipeline: function: string params: key: actionResult - value: "failed" - where: 'exists("log.errorCode") && !equals("log.errorCode", "AccessDenied")' + value: failure + where: exists("log.errorCode") && !equals("log.errorCode", "AccessDenied") - delete: fields: - log.requestParameters - log.responseElements - log.userIdentity - - log.additionalEventData \ No newline at end of file + - log.additionalEventData + + # Promote standard fields while retaining vendor fields used by rules. + - grok: + source: log.sourceIPAddress + patterns: + - fieldName: origin.ip + pattern: '{{.greedy}}' + where: inCIDR("log.sourceIPAddress","0.0.0.0/0") || inCIDR("log.sourceIPAddress","::/0") + - grok: + source: log.userIdentityArn + patterns: + - fieldName: origin.user + pattern: '{{.greedy}}' + where: exists("log.userIdentityArn") && !exists("origin.user") + - grok: + source: log.eventTime + patterns: + - fieldName: deviceTime + pattern: '{{.greedy}}' + where: exists("log.eventTime") && !exists("deviceTime") diff --git a/filters/azure/azure-eventhub.yml b/filters/azure/azure-eventhub.yml index 460c25a22..777ef3e1c 100644 --- a/filters/azure/azure-eventhub.yml +++ b/filters/azure/azure-eventhub.yml @@ -7,7 +7,7 @@ # 4- https://learn.microsoft.com/en-us/azure/event-grid/system-topics # pipeline: - - dataTypes: azure + - dataTypes: [azure] steps: - json: source: raw @@ -17,13 +17,13 @@ pipeline: # .......................................................................# - rename: from: - - log.ResponseBodySize - to: origin.bytesSent + - log.ResponseBodySize + to: origin.bytesReceived - rename: from: - - log.ResponseHeaderSize - to: origin.bytesReceived + - log.ResponseHeaderSize + to: log.responseHeaderSize - rename: from: @@ -32,8 +32,8 @@ pipeline: - rename: from: - - log.AccountName - to: origin.host + - log.AccountName + to: log.accountName - rename: from: @@ -47,13 +47,13 @@ pipeline: - rename: from: - - log.StatusText - to: connectionStatus + - log.StatusText + to: log.statusText - rename: from: - - log.Location - to: origin.geolocation.country + - log.Location + to: log.location - rename: from: @@ -795,23 +795,31 @@ pipeline: - log.properties.upstreamSourcePort to: log.propertiesUpstreamSourcePort + # Common Azure resource-log result (kept under log for existing consumers). + - grok: + source: log.resultType + patterns: + - fieldName: log.vendorActionResult + pattern: '{{.greedy}}' + where: exists("log.resultType") + - rename: from: - - log.properties.result - to: actionResult - where: '!regexMatch("log.properties.result", "(?i)\b(?:denied|blocked|failed)\b")' + - log.properties.result + to: log.vendorActionResult + where: '!exists("log.vendorActionResult") && exists("log.properties.result")' - rename: from: - - log.Properties.Result - to: actionResult - where: '!regexMatch("log.properties.result", "(?i)\b(?:denied|blocked|failed)\b")' + - log.Properties.Result + to: log.vendorActionResult + where: '!exists("log.vendorActionResult") && exists("log.Properties.Result")' - rename: from: - - log.properties.resultType - to: actionResult - where: '!regexMatch("log.properties.result", "(?i)\b(?:denied|blocked|failed)\b")' + - log.properties.resultType + to: log.vendorActionResult + where: '!exists("log.vendorActionResult") && exists("log.properties.resultType")' - rename: from: @@ -984,10 +992,10 @@ pipeline: where: '!exists("actionResult") && equalsIgnoreCase("log.data.status", "Succeeded")' - add: - function: 'string' + function: string params: key: actionResult - value: 'failed' + value: failure where: '!exists("actionResult") && equalsIgnoreCase("log.data.status", "Failed")' - add: @@ -998,25 +1006,19 @@ pipeline: where: '!exists("actionResult") && endsWith("log.eventGridEventType", "Success")' - add: - function: 'string' + function: string params: key: actionResult - value: 'failed' + value: failure where: '!exists("actionResult") && endsWith("log.eventGridEventType", "Failure")' - - add: - function: 'string' - params: - key: actionResult - value: 'blocked' - where: '!exists("actionResult") && endsWith("log.eventGridEventType", "Cancel")' - add: - function: 'string' + function: string params: key: severity - value: 'medium' - where: '!exists("severity") && oneOf("actionResult", ["failed", "blocked"])' + value: warning + where: '!exists("severity") && oneOf("actionResult", ["failure", "denied"])' - rename: from: @@ -1046,46 +1048,46 @@ pipeline: # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'denied' - where: regexMatch("log.properties.result", "(?i)\bdenied\b") + value: denied + where: regexMatch("log.vendorActionResult", "(?i)\\bdenied\\b") - add: - function: 'string' + function: string params: key: actionResult - value: 'blocked' - where: regexMatch("log.properties.result", "(?i)\bblocked\b") + value: denied + where: regexMatch("log.vendorActionResult", "(?i)\\bblocked\\b") - add: - function: 'string' + function: string params: key: actionResult - value: 'failed' - where: regexMatch("log.properties.result", "(?i)\bfailed\b") + value: failure + where: regexMatch("log.vendorActionResult", "(?i)\\bfailed\\b") # .......................................................................# # Adding severity based on log.level # .......................................................................# - add: - function: 'string' + function: string params: key: severity - value: 'high' + value: error where: oneOf("log.level", ["ERROR", "Error", "FATAL", "CRITICAL", "Critical"]) - add: - function: 'string' + function: string params: key: severity - value: 'medium' + value: warning where: oneOf("log.level", ["WARN", "Warning"]) - add: - function: 'string' + function: string params: key: severity - value: 'low' + value: info where: oneOf("log.level", ["Information", "Informational", "INFO", "DEBUG", "TRACE"]) # .......................................................................# @@ -1163,4 +1165,98 @@ pipeline: fields: - origin.bytesSent - origin.bytesReceived - to: float \ No newline at end of file + to: float + + # Normalize the source event severity. + - add: + function: string + params: + key: severity + value: critical + where: oneOf("log.level",["FATAL","CRITICAL","Critical"]) + - add: + function: string + params: + key: severity + value: debug + where: oneOf("log.level",["DEBUG","TRACE"]) + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: success + where: '!exists("actionResult") && regexMatch("log.vendorActionResult", "(?i)^(success|succeeded|successful|ok|done|accepted|accept|allowed|allow|permitted|permit|passed|pass|true)$")' + - add: + function: string + params: + key: actionResult + value: failure + where: '!equals("actionResult","denied") && regexMatch("log.vendorActionResult", "(?i)^(failure|failed|fail|error|invalid|timeout|false)$")' + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("log.vendorActionResult", "(?i)^(denied|deny|blocked|block|dropped|drop|rejected|reject|forbidden|unauthorized|quarantined)$") + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/cisco/asa.yml b/filters/cisco/asa.yml index 1b0f54d0e..e022a4434 100644 --- a/filters/cisco/asa.yml +++ b/filters/cisco/asa.yml @@ -206,73 +206,73 @@ pipeline: # First variant - grok: patterns: - - fieldName: log.irrelevant - pattern: 'access-list' - - fieldName: log.accessList - pattern: '{{.data}}\s' - - fieldName: actionResult - pattern: '{{.word}}' - - fieldName: protocol - pattern: '{{.word}}' - - fieldName: log.irrelevant - pattern: 'for user' - - fieldName: origin.user - pattern: '(''{{.data}}''|{{.data}})\s' - - fieldName: log.srcInterface - pattern: '{{.data}}/' - - fieldName: origin.ip - pattern: '({{.ipv4}}|{{.ipv6}})' - - fieldName: origin.port - pattern: '{{.integer}}' - - fieldName: log.dstInterface - pattern: '{{.data}}/' - - fieldName: target.ip - pattern: '({{.ipv4}}|{{.ipv6}})' - - fieldName: target.port - pattern: '{{.integer}}' - - fieldName: log.irrelevant - pattern: 'hit-cnt' - - fieldName: log.hitCount - pattern: '{{.integer}}' - - fieldName: log.rest - pattern: '{{.greedy}}' + - fieldName: log.irrelevant + pattern: access-list + - fieldName: log.accessList + pattern: '{{.data}}\s' + - fieldName: log.ciscoResult + pattern: '{{.word}}' + - fieldName: protocol + pattern: '{{.word}}' + - fieldName: log.irrelevant + pattern: for user + - fieldName: origin.user + pattern: ('{{.data}}'|{{.data}})\s + - fieldName: log.srcInterface + pattern: '{{.data}}/' + - fieldName: origin.ip + pattern: ({{.ipv4}}|{{.ipv6}}) + - fieldName: origin.port + pattern: '{{.integer}}' + - fieldName: log.dstInterface + pattern: '{{.data}}/' + - fieldName: target.ip + pattern: ({{.ipv4}}|{{.ipv6}}) + - fieldName: target.port + pattern: '{{.integer}}' + - fieldName: log.irrelevant + pattern: hit-cnt + - fieldName: log.hitCount + pattern: '{{.integer}}' + - fieldName: log.rest + pattern: '{{.greedy}}' source: log.msg where: log.messageId==106102 || log.messageId==106103 # Second variant - grok: patterns: - - fieldName: log.irrelevant - pattern: 'access-list' - - fieldName: log.accessList - pattern: '{{.data}}\s' - - fieldName: actionResult - pattern: '{{.word}}' - - fieldName: protocol - pattern: '{{.word}}' - - fieldName: log.irrelevant - pattern: 'for user' - - fieldName: origin.user - pattern: '(''{{.data}}''|{{.data}})\s' - - fieldName: log.srcInterface - pattern: '{{.data}}/' - - fieldName: origin.ip - pattern: '({{.ipv4}}|{{.ipv6}})' - - fieldName: origin.port - pattern: '\({{.data}}\)' - - fieldName: log.irrelevant - pattern: '-\>' - - fieldName: log.dstInterface - pattern: '{{.data}}/' - - fieldName: target.ip - pattern: '({{.ipv4}}|{{.ipv6}})' - - fieldName: target.port - pattern: '\({{.data}}\)' - - fieldName: log.irrelevant - pattern: 'hit-cnt' - - fieldName: log.hitCount - pattern: '{{.integer}}' - - fieldName: log.rest - pattern: '{{.greedy}}' + - fieldName: log.irrelevant + pattern: access-list + - fieldName: log.accessList + pattern: '{{.data}}\s' + - fieldName: log.ciscoResult + pattern: '{{.word}}' + - fieldName: protocol + pattern: '{{.word}}' + - fieldName: log.irrelevant + pattern: for user + - fieldName: origin.user + pattern: ('{{.data}}'|{{.data}})\s + - fieldName: log.srcInterface + pattern: '{{.data}}/' + - fieldName: origin.ip + pattern: ({{.ipv4}}|{{.ipv6}}) + - fieldName: origin.port + pattern: \({{.data}}\) + - fieldName: log.irrelevant + pattern: -\> + - fieldName: log.dstInterface + pattern: '{{.data}}/' + - fieldName: target.ip + pattern: ({{.ipv4}}|{{.ipv6}}) + - fieldName: target.port + pattern: \({{.data}}\) + - fieldName: log.irrelevant + pattern: hit-cnt + - fieldName: log.hitCount + pattern: '{{.integer}}' + - fieldName: log.rest + pattern: '{{.greedy}}' source: log.msg where: log.messageId==106102 || log.messageId==106103 - trim: @@ -310,18 +310,18 @@ pipeline: where: log.messageId==106102 || log.messageId==106103 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' - where: exists("actionResult") && (equals("log.messageId", 106102) || equals("log.messageId", 106103)) && equalsIgnoreCase("actionResult", "Permitted") + value: success + where: exists("log.ciscoResult") && (equals("log.messageId", 106102) || equals("log.messageId", 106103)) && equalsIgnoreCase("log.ciscoResult", "Permitted") # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'denied' - where: exists("actionResult") && (equals("log.messageId", 106102) || equals("log.messageId", 106103)) && !equalsIgnoreCase("actionResult", "Permitted") + value: denied + where: exists("log.ciscoResult") && (equals("log.messageId", 106102) || equals("log.messageId", 106103)) && equalsIgnoreCase("log.ciscoResult", "Denied") #......................................................................# # ASA-4-109017 - grok: @@ -388,10 +388,10 @@ pipeline: - origin.user where: log.messageId==109101 - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==109101 #......................................................................# # ASA-4-109102 @@ -419,10 +419,10 @@ pipeline: source: action where: log.messageId==109102 - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==109102 #......................................................................# # ASA-3-109103 @@ -460,10 +460,10 @@ pipeline: - origin.user where: log.messageId==109103 - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==109103 #......................................................................# # ASA-109201 to 109213 @@ -496,10 +496,10 @@ pipeline: where: log.messageId>=109201 && log.messageId<=109213 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId>=109201 && log.messageId<=109213 #......................................................................# # ASA-6-113004 @@ -541,10 +541,10 @@ pipeline: - target.ip where: log.messageId==113004 - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==113004 #......................................................................# # ASA-6-113005 @@ -607,10 +607,10 @@ pipeline: where: log.messageId==113008 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==113008 #......................................................................# # ASA-6-113009 @@ -660,10 +660,10 @@ pipeline: where: log.messageId==113009 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==113009 #......................................................................# # ASA-6-113010 @@ -751,10 +751,10 @@ pipeline: source: action where: log.messageId==113012 - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==113012 #......................................................................# # ASA-6-113013 @@ -943,10 +943,10 @@ pipeline: where: log.messageId==113019 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==113019 #......................................................................# # ASA-113031,113032,113033 @@ -1056,10 +1056,10 @@ pipeline: where: log.messageId==113034 || log.messageId==113035 || log.messageId==113036 || log.messageId==113038 || log.messageId==113039 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==113039 # Adding action result - add: @@ -1174,10 +1174,10 @@ pipeline: where: log.messageId==201003 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==201003 #......................................................................# # ASA-4-209003 @@ -1220,10 +1220,10 @@ pipeline: where: log.messageId==209003 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==209003 #......................................................................# # ASA-3-316001 @@ -1384,10 +1384,10 @@ pipeline: where: log.messageId==302003 || log.messageId==302004 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302003 || log.messageId==302004 #......................................................................# # ASA-6-302012 @@ -1415,10 +1415,10 @@ pipeline: where: log.messageId==302012 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302012 #......................................................................# # ASA-6-302013 @@ -1551,10 +1551,10 @@ pipeline: where: log.messageId==302013 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302013 #......................................................................# # ASA-6-302014 @@ -1664,10 +1664,10 @@ pipeline: where: log.messageId==302014 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302014 #......................................................................# # ASA-6-302015 @@ -1800,10 +1800,10 @@ pipeline: where: log.messageId==302015 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302015 #......................................................................# # ASA-6-302016 @@ -1922,10 +1922,10 @@ pipeline: where: log.messageId==302016 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302016 #......................................................................# # ASA-6-302017 @@ -2062,10 +2062,10 @@ pipeline: where: log.messageId==302017 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302017 #......................................................................# # ASA-6-302018 @@ -2194,10 +2194,10 @@ pipeline: where: log.messageId==302018 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302018 #......................................................................# # ASA-6-302020, 302021 @@ -2422,10 +2422,10 @@ pipeline: where: log.messageId==302020 || log.messageId==302021 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302020 || log.messageId==302021 #......................................................................# # ASA-6-302022, 302024, 302026 @@ -2540,10 +2540,10 @@ pipeline: where: log.messageId==302022 || log.messageId==302024 || log.messageId==302026 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302022 || log.messageId==302024 || log.messageId==302026 #......................................................................# # ASA-6-302023, 302025, 302027 @@ -2598,10 +2598,10 @@ pipeline: where: log.messageId==302023 || log.messageId==302025 || log.messageId==302027 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302023 || log.messageId==302025 || log.messageId==302027 #......................................................................# # ASA-302033,302034 @@ -2669,10 +2669,10 @@ pipeline: where: log.messageId==302033 || log.messageId==302034 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302033 - add: function: 'string' @@ -2853,10 +2853,10 @@ pipeline: where: log.messageId==302035 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302035 #......................................................................# # ASA-6-302036 @@ -3003,10 +3003,10 @@ pipeline: where: log.messageId==302036 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302036 #......................................................................# # ASA-6-302303 @@ -3084,10 +3084,10 @@ pipeline: where: log.messageId==302303 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302303 #......................................................................# # ASA-6-302304 @@ -3157,10 +3157,10 @@ pipeline: where: log.messageId==302304 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302304 #......................................................................# # ASA-6-302305 @@ -3318,10 +3318,10 @@ pipeline: where: log.messageId==302305 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302305 #......................................................................# # ASA-6-302306 @@ -3452,10 +3452,10 @@ pipeline: where: log.messageId==302306 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==302306 #......................................................................# # ASA-6-305009 @@ -4035,10 +4035,10 @@ pipeline: where: log.messageId==402114 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: log.messageId==402114 #......................................................................# # ASA-4-402115 @@ -4070,10 +4070,10 @@ pipeline: where: log.messageId==402115 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: log.messageId==402115 #......................................................................# # ASA-4-402116 @@ -4189,10 +4189,10 @@ pipeline: where: log.messageId==402116 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: log.messageId==402116 #......................................................................# # ASA-4-402117 @@ -4225,10 +4225,10 @@ pipeline: where: log.messageId==402117 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: log.messageId==402117 #......................................................................# # ASA-4-402118 @@ -4295,10 +4295,10 @@ pipeline: where: log.messageId==402118 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: log.messageId==402118 #......................................................................# # ASA-4-402119, 402120 @@ -4359,10 +4359,10 @@ pipeline: where: log.messageId==402119 || log.messageId==402120 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: log.messageId==402119 || log.messageId==402120 #......................................................................# # ASA-5-402128 @@ -4636,10 +4636,10 @@ pipeline: where: log.messageId==603109 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==603109 #......................................................................# # ASA-6-605004 @@ -4739,10 +4739,10 @@ pipeline: where: log.messageId==617100 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==617100 #......................................................................# # ASA-2-106018 @@ -4945,10 +4945,10 @@ pipeline: where: log.messageId==611307 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==611307 #......................................................................# # ASA-6-611309 @@ -4966,10 +4966,10 @@ pipeline: where: log.messageId==611309 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==611309 #......................................................................# # ASA-6-611310,611311 @@ -4989,10 +4989,10 @@ pipeline: where: log.messageId==611310 || log.messageId==611311 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==611310 - add: function: 'string' @@ -5023,10 +5023,10 @@ pipeline: where: log.messageId==611314 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==611314 #......................................................................# # ASA-6-611315 @@ -5047,10 +5047,10 @@ pipeline: where: log.messageId==611315 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==611315 #......................................................................# # ASA-6-611318 @@ -5181,10 +5181,10 @@ pipeline: value: 'denied' where: log.messageId==713252 - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==713253 #......................................................................# # ASA-6-716001,716002,716003 @@ -5245,10 +5245,10 @@ pipeline: where: log.messageId==716001 || log.messageId==716002 || log.messageId==716003 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==716001 || log.messageId==716002 #......................................................................# # ASA-6-716004,716005,716006,716007,716009 @@ -5305,10 +5305,10 @@ pipeline: value: 'denied' where: log.messageId==716004 || log.messageId==716007 || log.messageId==716009 - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==716006 #......................................................................# # ASA-6-716038 @@ -5330,10 +5330,10 @@ pipeline: where: log.messageId==716038 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==716038 # Cleaning fields - trim: @@ -5442,10 +5442,10 @@ pipeline: value: 'denied' where: log.messageId==719019 || log.messageId==719023 - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==719020 || log.messageId==719022 - add: function: 'string' @@ -5574,10 +5574,10 @@ pipeline: where: log.messageId==609002 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: log.messageId==609002 #......................................................................# # ASA-6-611305 @@ -5730,12 +5730,6 @@ pipeline: to: target.ip where: log.messageId==733101 && contains("action", "targeted") # Adding action result - - add: - function: 'string' - params: - key: actionResult - value: 'accepted' - where: log.messageId==733101 #......................................................................# # ASA-4-733102, 733103 - grok: @@ -5763,24 +5757,6 @@ pipeline: where: (equals("log.messageId", 733102) || equals("log.messageId", 733103)) && contains("log.msg", "removes") #......................................................................# # Decoding severity - - add: - function: 'string' - params: - key: severity - value: 'high' - where: oneOf("log.severity", ["1", "2", "3"]) - - add: - function: 'string' - params: - key: severity - value: 'medium' - where: log.severity=="4" - - add: - function: 'string' - params: - key: severity - value: 'low' - where: oneOf("log.severity", ["5", "6", "7"]) #......................................................................# # Adding common geolocation - dynamic: @@ -5914,4 +5890,115 @@ pipeline: - log.ciscoSeparator - log.irrelevant - log.syslogPri - - log.rest \ No newline at end of file + - log.rest + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: denied + where: oneOf("log.messageId", [106001, 106017, 106023, 106100]) && (equalsIgnoreCase("log.ciscoResult", "Deny") || equalsIgnoreCase("log.ciscoResult", "Denied")) + - add: + function: string + params: + key: actionResult + value: denied + where: equals("log.messageId", 733102) + + # Normalize the source event severity. + - add: + function: string + params: + key: severity + value: critical + where: oneOf("log.severity", [0,1,2]) + - add: + function: string + params: + key: severity + value: error + where: oneOf("log.severity", [3]) + - add: + function: string + params: + key: severity + value: warning + where: oneOf("log.severity", [4]) + - add: + function: string + params: + key: severity + value: info + where: oneOf("log.severity", [5,6]) + - add: + function: string + params: + key: severity + value: debug + where: oneOf("log.severity", [7]) + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/cisco/cs_switch.yml b/filters/cisco/cs_switch.yml index bb06b6def..25258a177 100644 --- a/filters/cisco/cs_switch.yml +++ b/filters/cisco/cs_switch.yml @@ -174,7 +174,7 @@ pipeline: function: string params: key: actionResult - value: failed + value: failure where: equals("log.facility", "DOT1X") && equals("log.facilityMnemonic", "FAIL") - add: @@ -188,34 +188,48 @@ pipeline: function: string params: key: actionResult - value: blocked + value: denied where: equals("log.facility", "SW_DAI") && oneOf("log.facilityMnemonic", ["DHCP_SNOOPING_DENY", "INVALID_ARP", "ACL_DENY"]) #......................................................................# # Decoding severity + #......................................................................# + # Removing unused fields + - delete: + fields: + - log.switchHeader + - log.irrelevant + - log.tmpFacilityMnemonic + - log.aclName + - log.aclAction + + # Normalize the source event severity. - add: - function: 'string' + function: string params: key: severity - value: 'high' - where: oneOf("log.severity", ["0", "1", "2", "3"]) + value: critical + where: oneOf("log.severity", [0,1,2]) - add: - function: 'string' + function: string params: key: severity - value: 'medium' - where: log.severity=="4" + value: error + where: oneOf("log.severity", [3]) - add: - function: 'string' + function: string params: key: severity - value: 'low' - where: oneOf("log.severity", ["5", "6", "7"]) - #......................................................................# - # Removing unused fields - - delete: - fields: - - log.switchHeader - - log.irrelevant - - log.tmpFacilityMnemonic - - log.aclName - - log.aclAction \ No newline at end of file + value: warning + where: oneOf("log.severity", [4]) + - add: + function: string + params: + key: severity + value: info + where: oneOf("log.severity", [5,6]) + - add: + function: string + params: + key: severity + value: debug + where: oneOf("log.severity", [7]) diff --git a/filters/cisco/firepower.yml b/filters/cisco/firepower.yml index 2c37bf00e..edba096ce 100644 --- a/filters/cisco/firepower.yml +++ b/filters/cisco/firepower.yml @@ -204,73 +204,73 @@ pipeline: # First variant - grok: patterns: - - fieldName: log.irrelevant - pattern: 'access-list' - - fieldName: log.accessList - pattern: '{{.data}}\s' - - fieldName: actionResult - pattern: '{{.word}}' - - fieldName: protocol - pattern: '{{.word}}' - - fieldName: log.irrelevant - pattern: 'for user' - - fieldName: origin.user - pattern: '(''{{.data}}''|{{.data}})\s' - - fieldName: log.srcInterface - pattern: '{{.data}}/' - - fieldName: origin.ip - pattern: '({{.ipv4}}|{{.ipv6}})' - - fieldName: origin.port - pattern: '{{.integer}}' - - fieldName: log.dstInterface - pattern: '{{.data}}/' - - fieldName: target.ip - pattern: '({{.ipv4}}|{{.ipv6}})' - - fieldName: target.port - pattern: '{{.integer}}' - - fieldName: log.irrelevant - pattern: 'hit-cnt' - - fieldName: log.hitCount - pattern: '{{.integer}}' - - fieldName: log.rest - pattern: '{{.greedy}}' + - fieldName: log.irrelevant + pattern: access-list + - fieldName: log.accessList + pattern: '{{.data}}\s' + - fieldName: log.ciscoResult + pattern: '{{.word}}' + - fieldName: protocol + pattern: '{{.word}}' + - fieldName: log.irrelevant + pattern: for user + - fieldName: origin.user + pattern: ('{{.data}}'|{{.data}})\s + - fieldName: log.srcInterface + pattern: '{{.data}}/' + - fieldName: origin.ip + pattern: ({{.ipv4}}|{{.ipv6}}) + - fieldName: origin.port + pattern: '{{.integer}}' + - fieldName: log.dstInterface + pattern: '{{.data}}/' + - fieldName: target.ip + pattern: ({{.ipv4}}|{{.ipv6}}) + - fieldName: target.port + pattern: '{{.integer}}' + - fieldName: log.irrelevant + pattern: hit-cnt + - fieldName: log.hitCount + pattern: '{{.integer}}' + - fieldName: log.rest + pattern: '{{.greedy}}' source: log.msg where: equals("log.messageId", 106102) || equals("log.messageId", 106103) # Second variant - grok: patterns: - - fieldName: log.irrelevant - pattern: 'access-list' - - fieldName: log.accessList - pattern: '{{.data}}\s' - - fieldName: actionResult - pattern: '{{.word}}' - - fieldName: protocol - pattern: '{{.word}}' - - fieldName: log.irrelevant - pattern: 'for user' - - fieldName: origin.user - pattern: '(''{{.data}}''|{{.data}})\s' - - fieldName: log.srcInterface - pattern: '{{.data}}/' - - fieldName: origin.ip - pattern: '({{.ipv4}}|{{.ipv6}})' - - fieldName: origin.port - pattern: '\({{.data}}\)' - - fieldName: log.irrelevant - pattern: '-\>' - - fieldName: log.dstInterface - pattern: '{{.data}}/' - - fieldName: target.ip - pattern: '({{.ipv4}}|{{.ipv6}})' - - fieldName: target.port - pattern: '\({{.data}}\)' - - fieldName: log.irrelevant - pattern: 'hit-cnt' - - fieldName: log.hitCount - pattern: '{{.integer}}' - - fieldName: log.rest - pattern: '{{.greedy}}' + - fieldName: log.irrelevant + pattern: access-list + - fieldName: log.accessList + pattern: '{{.data}}\s' + - fieldName: log.ciscoResult + pattern: '{{.word}}' + - fieldName: protocol + pattern: '{{.word}}' + - fieldName: log.irrelevant + pattern: for user + - fieldName: origin.user + pattern: ('{{.data}}'|{{.data}})\s + - fieldName: log.srcInterface + pattern: '{{.data}}/' + - fieldName: origin.ip + pattern: ({{.ipv4}}|{{.ipv6}}) + - fieldName: origin.port + pattern: \({{.data}}\) + - fieldName: log.irrelevant + pattern: -\> + - fieldName: log.dstInterface + pattern: '{{.data}}/' + - fieldName: target.ip + pattern: ({{.ipv4}}|{{.ipv6}}) + - fieldName: target.port + pattern: \({{.data}}\) + - fieldName: log.irrelevant + pattern: hit-cnt + - fieldName: log.hitCount + pattern: '{{.integer}}' + - fieldName: log.rest + pattern: '{{.greedy}}' source: log.msg where: equals("log.messageId", 106102) || equals("log.messageId", 106103) - trim: @@ -308,18 +308,18 @@ pipeline: where: equals("log.messageId", 106102) || equals("log.messageId", 106103) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' - where: exists("actionResult") && (equals("log.messageId", 106102) || equals("log.messageId", 106103)) && equalsIgnoreCase("actionResult", "Permitted") + value: success + where: exists("log.ciscoResult") && (equals("log.messageId", 106102) || equals("log.messageId", 106103)) && equalsIgnoreCase("log.ciscoResult", "Permitted") # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'denied' - where: exists("actionResult") && (equals("log.messageId", 106102) || equals("log.messageId", 106103)) && !equalsIgnoreCase("actionResult", "Permitted") + value: denied + where: exists("log.ciscoResult") && (equals("log.messageId", 106102) || equals("log.messageId", 106103)) && equalsIgnoreCase("log.ciscoResult", "Denied") #......................................................................# # FTD-6-109101 - grok: @@ -358,10 +358,10 @@ pipeline: - origin.user where: equals("log.messageId", 109101) - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 109101) #......................................................................# # FTD-4-109102 @@ -389,10 +389,10 @@ pipeline: source: action where: equals("log.messageId", 109102) - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 109102) #......................................................................# # FTD-3-109103 @@ -430,10 +430,10 @@ pipeline: - origin.user where: equals("log.messageId", 109103) - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 109103) #......................................................................# # FTD-109201 to 109213 @@ -463,13 +463,13 @@ pipeline: fields: - log.session - origin.user - where: lgreaterOrEqual("log.messageId", 109201) && log.messageId<=109213 + where: greaterOrEqual("log.messageId", 109201) && lessOrEqual("log.messageId", 109213) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: greaterOrEqual("log.messageId", 109201) && log.messageId<=109213 #......................................................................# # FTD-6-113004 @@ -511,10 +511,10 @@ pipeline: - target.ip where: equals("log.messageId", 113004) - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 113004) #......................................................................# # FTD-6-113005 @@ -577,10 +577,10 @@ pipeline: where: equals("log.messageId", 113008) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 113008) #......................................................................# # FTD-6-113009 @@ -630,10 +630,10 @@ pipeline: where: equals("log.messageId", 113009) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 113009) #......................................................................# # FTD-6-113010 @@ -721,10 +721,10 @@ pipeline: source: action where: equals("log.messageId", 113012) - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 113012) #......................................................................# # FTD-6-113013 @@ -913,10 +913,10 @@ pipeline: where: equals("log.messageId", 113019) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 113019) #......................................................................# # FTD-113031,113032,113033 @@ -1026,10 +1026,10 @@ pipeline: where: equals("log.messageId", 113034) || log.messageId==113035 || log.messageId==113036 || log.messageId==113038 || log.messageId==113039 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 113039) # Adding action result - add: @@ -1144,10 +1144,10 @@ pipeline: where: equals("log.messageId", 201003) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 201003) #......................................................................# # FTD-4-209003 @@ -1190,10 +1190,10 @@ pipeline: where: equals("log.messageId", 209003) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 209003) #......................................................................# # FTD-3-316001 @@ -1354,10 +1354,10 @@ pipeline: where: equals("log.messageId", 302003) || log.messageId==302004 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302003) || log.messageId==302004 #......................................................................# # FTD-6-302012 @@ -1385,10 +1385,10 @@ pipeline: where: equals("log.messageId", 302012) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302012) #......................................................................# # FTD-6-302013 @@ -1521,10 +1521,10 @@ pipeline: where: equals("log.messageId", 302013) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302013) #......................................................................# # FTD-6-302014 @@ -1634,10 +1634,10 @@ pipeline: where: equals("log.messageId", 302014) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302014) #......................................................................# # FTD-6-302015 @@ -1770,10 +1770,10 @@ pipeline: where: equals("log.messageId", 302015) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302015) #......................................................................# # FTD-6-302016 @@ -1892,10 +1892,10 @@ pipeline: where: equals("log.messageId", 302016) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302016) #......................................................................# # FTD-6-302017 @@ -2032,10 +2032,10 @@ pipeline: where: equals("log.messageId", 302017) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302017) #......................................................................# # FTD-6-302018 @@ -2164,10 +2164,10 @@ pipeline: where: equals("log.messageId", 302018) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302018) #......................................................................# # FTD-6-302020, 302021 @@ -2392,10 +2392,10 @@ pipeline: where: equals("log.messageId", 302020) || log.messageId==302021 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302020) || log.messageId==302021 #......................................................................# # FTD-6-302022, 302024, 302026 @@ -2510,10 +2510,10 @@ pipeline: where: equals("log.messageId", 302022) || log.messageId==302024 || log.messageId==302026 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302022) || log.messageId==302024 || log.messageId==302026 #......................................................................# # FTD-6-302023, 302025, 302027 @@ -2568,10 +2568,10 @@ pipeline: where: equals("log.messageId", 302023) || log.messageId==302025 || log.messageId==302027 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302023) || log.messageId==302025 || log.messageId==302027 #......................................................................# # FTD-302033,302034 @@ -2639,10 +2639,10 @@ pipeline: where: equals("log.messageId", 302033) || log.messageId==302034 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302033) - add: function: 'string' @@ -2726,10 +2726,10 @@ pipeline: where: equals("log.messageId", 302303) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302303) #......................................................................# # FTD-6-302304 @@ -2799,10 +2799,10 @@ pipeline: where: equals("log.messageId", 302304) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 302304) #......................................................................# # FTD-6-305009 @@ -3335,10 +3335,10 @@ pipeline: where: equals("log.messageId", 402114) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: equals("log.messageId", 402114) #......................................................................# # FTD-4-402115 @@ -3370,10 +3370,10 @@ pipeline: where: equals("log.messageId", 402115) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: equals("log.messageId", 402115) #......................................................................# # FTD-4-402116 @@ -3489,10 +3489,10 @@ pipeline: where: equals("log.messageId", 402116) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: equals("log.messageId", 402116) #......................................................................# # FTD-4-402117 @@ -3525,10 +3525,10 @@ pipeline: where: equals("log.messageId", 402117) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: equals("log.messageId", 402117) #......................................................................# # FTD-4-402118 @@ -3595,10 +3595,10 @@ pipeline: where: equals("log.messageId", 402118) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: equals("log.messageId", 402118) #......................................................................# # FTD-4-402119, 402120 @@ -3659,10 +3659,10 @@ pipeline: where: equals("log.messageId", 402119) || log.messageId==402120 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: failure where: equals("log.messageId", 402119) || log.messageId==402120 #......................................................................# # FTD-5-402128 @@ -4169,10 +4169,10 @@ pipeline: where: equals("log.messageId", 611307) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 611307) #......................................................................# # FTD-6-611309 @@ -4190,10 +4190,10 @@ pipeline: where: equals("log.messageId", 611309) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 611309) #......................................................................# # FTD-6-611310,611311 @@ -4213,10 +4213,10 @@ pipeline: where: equals("log.messageId", 611310) || log.messageId==611311 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 611310) - add: function: 'string' @@ -4247,10 +4247,10 @@ pipeline: where: equals("log.messageId", 611314) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 611314) #......................................................................# # FTD-6-611315 @@ -4271,10 +4271,10 @@ pipeline: where: equals("log.messageId", 611315) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 611315) #......................................................................# # FTD-6-611318 @@ -4405,10 +4405,10 @@ pipeline: value: 'denied' where: equals("log.messageId", 713252) - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 713253) #......................................................................# # FTD-6-716001,716002,716003 @@ -4469,10 +4469,10 @@ pipeline: where: equals("log.messageId", 716001) || log.messageId==716002 || log.messageId==716003 # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 716001) || log.messageId==716002 #......................................................................# # FTD-6-716004,716005,716006,716007,716009 @@ -4529,10 +4529,10 @@ pipeline: value: 'denied' where: equals("log.messageId", 716004) || log.messageId==716007 || log.messageId==716009 - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 716006) #......................................................................# # FTD-6-716038 @@ -4554,10 +4554,10 @@ pipeline: where: equals("log.messageId", 716038) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 716038) # Cleaning fields - trim: @@ -4666,10 +4666,10 @@ pipeline: value: 'denied' where: equals("log.messageId", 719019) || log.messageId==719023 - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 719020) || log.messageId==719022 - add: function: 'string' @@ -4798,10 +4798,10 @@ pipeline: where: equals("log.messageId", 609002) # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.messageId", 609002) #......................................................................# # FTD-6-611305 @@ -4954,12 +4954,6 @@ pipeline: to: target.ip where: equals("log.messageId", 733101) && contains("action", "targeted") # Adding action result - - add: - function: 'string' - params: - key: actionResult - value: 'accepted' - where: equals("log.messageId", 733101) #......................................................................# # FTD-4-733102, 733103 - grok: @@ -4987,24 +4981,6 @@ pipeline: where: (equals("log.messageId", 733102) || log.messageId==733103) && contains("log.msg", "removes") #......................................................................# # Decoding severity - - add: - function: 'string' - params: - key: severity - value: 'high' - where: log.severity=="1" || log.severity=="2" || log.severity=="3" - - add: - function: 'string' - params: - key: severity - value: 'medium' - where: log.severity=="4" - - add: - function: 'string' - params: - key: severity - value: 'low' - where: log.severity=="5" || log.severity=="6" || log.severity=="7" #......................................................................# # Adding common geolocation - dynamic: @@ -5138,4 +5114,115 @@ pipeline: - log.ciscoSeparator - log.irrelevant - log.syslogPri - - log.rest \ No newline at end of file + - log.rest + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: denied + where: oneOf("log.messageId", [106001, 106017, 106023, 106100]) && (equalsIgnoreCase("log.ciscoResult", "Deny") || equalsIgnoreCase("log.ciscoResult", "Denied")) + - add: + function: string + params: + key: actionResult + value: denied + where: equals("log.messageId", 733102) + + # Normalize the source event severity. + - add: + function: string + params: + key: severity + value: critical + where: oneOf("log.severity", [0,1,2]) + - add: + function: string + params: + key: severity + value: error + where: oneOf("log.severity", [3]) + - add: + function: string + params: + key: severity + value: warning + where: oneOf("log.severity", [4]) + - add: + function: string + params: + key: severity + value: info + where: oneOf("log.severity", [5,6]) + - add: + function: string + params: + key: severity + value: debug + where: oneOf("log.severity", [7]) + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/cisco/meraki.yml b/filters/cisco/meraki.yml index 66bb8d610..bfb6e9b37 100644 --- a/filters/cisco/meraki.yml +++ b/filters/cisco/meraki.yml @@ -103,10 +103,10 @@ pipeline: where: '!equals("log.controlFlag", "Init")' # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equals("log.connectivity", "true") # ........................................ # event uplink connectivity change @@ -169,10 +169,10 @@ pipeline: where: '!equals("log.controlFlag", "Init") && contains("action", "Cellular connection")' # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: equalsIgnoreCase("log.connectionState", "up") # ........................................ # Event: event, dhcp no offers @@ -236,10 +236,10 @@ pipeline: where: '!equals("log.controlFlag", "Init") && contains("log.genericEvent", "dhcp lease")' # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: contains("log.genericEvent", "dhcp lease") # ........................................ # Event: event, HTTP GET requests in Meraki MX Security Appliance @@ -270,12 +270,6 @@ pipeline: source: log.genericEvent where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "src") && log.merakiGroup=="urls"' # Adding action result - - add: - function: 'string' - params: - key: actionResult - value: 'accepted' - where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "src") && log.merakiGroup=="urls"' # ........................................ # Event: flows, IP session initiated in Meraki MX Security Appliance # First variant @@ -342,10 +336,10 @@ pipeline: where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "src") && equals("log.merakiGroup", "flows")' # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "src") && equals("log.merakiGroup", "flows") && (startsWith("log.pattern", "0") || startsWith("log.pattern", "allow") || startsWith("log.pattern", "Allow"))' - add: function: 'string' @@ -501,31 +495,31 @@ pipeline: where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "Site") && equals("log.merakiGroup", "events")' # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' - where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "Site") && equals("log.merakiGroup", "events") && (contains("log.genericEvent", "queued due to no phase 1") || contains("log.genericEvent", "queued due to no phase1") || contains("log.genericEvent", "established"))' + value: success + where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "Site") && equals("log.merakiGroup", "events") && contains("log.genericEvent", "established")' # ........................................ # Event: event spanning-tree guard state change in Meraki MS Switches - grok: patterns: - - fieldName: log.irrelevant - pattern: 'Port' - - fieldName: origin.port - pattern: '{{.integer}}' - - fieldName: log.irrelevant - pattern: 'received an' - - fieldName: protocol - pattern: '{{.data}}BPDU' - - fieldName: log.irrelevant - pattern: 'from' - - fieldName: origin.mac - pattern: '{{.data}}so' - - fieldName: log.irrelevant - pattern: 'the port was' - - fieldName: actionResult - pattern: '{{.greedy}}' + - fieldName: log.irrelevant + pattern: Port + - fieldName: origin.port + pattern: '{{.integer}}' + - fieldName: log.irrelevant + pattern: received an + - fieldName: protocol + pattern: '{{.data}}BPDU' + - fieldName: log.irrelevant + pattern: from + - fieldName: origin.mac + pattern: '{{.data}}so' + - fieldName: log.irrelevant + pattern: the port was + - fieldName: log.merakiResult + pattern: '{{.greedy}}' source: log.genericEvent where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "Port") && equals("log.merakiGroup", "events")' - trim: @@ -853,10 +847,10 @@ pipeline: where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "type") && equals("log.merakiGroup", "events") && contains("log.genericEvent", "radio") && contains("log.genericEvent", "channel") && contains("log.genericEvent", "auth_neg_dur") && contains("log.genericEvent", "last_auth_ago")' # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "type") && equals("log.merakiGroup", "events") && contains("log.genericEvent", "radio") && contains("log.genericEvent", "channel") && contains("log.genericEvent", "auth_neg_dur") && contains("log.genericEvent", "last_auth_ago")' # ........................................ # Event: event 802.1X (all events) in Meraki MR Access Points @@ -1008,10 +1002,10 @@ pipeline: where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "type") && equals("log.merakiGroup", "events") && contains("log.genericEvent", "ip") && contains("log.genericEvent", "vap") && contains("log.genericEvent", "duration") && contains("log.genericEvent", "download") && contains("log.genericEvent", "upload")' # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "type") && equals("log.merakiGroup", "events") && contains("log.genericEvent", "ip") && contains("log.genericEvent", "vap") && contains("log.genericEvent", "duration") && contains("log.genericEvent", "download") && contains("log.genericEvent", "upload")' # ........................................ # Event: event wireless packet flood detected in Meraki MR Access Points @@ -1123,32 +1117,32 @@ pipeline: # Event: flows, flow allowed by Layer 3 firewall in Meraki MR Access Points - grok: patterns: - - fieldName: actionResult - pattern: '{{.word}}' - - fieldName: log.irrelevant - pattern: 'src(\s)?=' - - fieldName: origin.ip - pattern: '{{.ipv4}}|{{.ipv6}}' - - fieldName: log.irrelevant - pattern: 'dst(\s)?=' - - fieldName: target.ip - pattern: '{{.ipv4}}|{{.ipv6}}' - - fieldName: log.irrelevant - pattern: 'mac(\s)?=' - - fieldName: origin.mac - pattern: '{{.data}}protocol' - - fieldName: log.irrelevant - pattern: '=' - - fieldName: protocol - pattern: '{{.data}}sport' - - fieldName: log.irrelevant - pattern: '=' - - fieldName: origin.port - pattern: '{{.integer}}' - - fieldName: log.irrelevant - pattern: 'dport(\s)?=' - - fieldName: target.port - pattern: '{{.integer}}' + - fieldName: log.merakiResult + pattern: '{{.word}}' + - fieldName: log.irrelevant + pattern: src(\s)?= + - fieldName: origin.ip + pattern: '{{.ipv4}}|{{.ipv6}}' + - fieldName: log.irrelevant + pattern: dst(\s)?= + - fieldName: target.ip + pattern: '{{.ipv4}}|{{.ipv6}}' + - fieldName: log.irrelevant + pattern: mac(\s)?= + - fieldName: origin.mac + pattern: '{{.data}}protocol' + - fieldName: log.irrelevant + pattern: '=' + - fieldName: protocol + pattern: '{{.data}}sport' + - fieldName: log.irrelevant + pattern: '=' + - fieldName: origin.port + pattern: '{{.integer}}' + - fieldName: log.irrelevant + pattern: dport(\s)?= + - fieldName: target.port + pattern: '{{.integer}}' source: log.genericEvent where: '!equals("log.controlFlag", "Init") && (startsWith("log.genericEvent", "allow") || startsWith("log.genericEvent", "deny")) && equals("log.merakiGroup", "flows") && contains("log.genericEvent", "src") && contains("log.genericEvent", "dst") && contains("log.genericEvent", "sport") && contains("log.genericEvent", "dport") && contains("log.genericEvent", "mac")' - trim: @@ -1165,10 +1159,10 @@ pipeline: where: '!equals("log.controlFlag", "Init") && (startsWith("log.genericEvent", "allow") || startsWith("log.genericEvent", "deny")) && equals("log.merakiGroup", "flows") && contains("log.genericEvent", "src") && contains("log.genericEvent", "dst") && contains("log.genericEvent", "sport") && contains("log.genericEvent", "dport") && contains("log.genericEvent", "mac")' # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'accepted' + value: success where: '!equals("log.controlFlag", "Init") && startsWith("log.genericEvent", "allow") && equals("log.merakiGroup", "flows") && contains("log.genericEvent", "src") && contains("log.genericEvent", "dst") && contains("log.genericEvent", "sport") && contains("log.genericEvent", "dport") && contains("log.genericEvent", "mac")' # Adding action result - add: @@ -1388,12 +1382,6 @@ pipeline: - log.vap where: startsWith("log.genericEvent", "airmarshal_events") && contains("log.genericEvent", "type") && contains("log.genericEvent", "ssid") && contains("log.genericEvent", "bssid") # Adding action result - - add: - function: 'string' - params: - key: actionResult - value: 'accepted' - where: startsWith("log.genericEvent", "airmarshal_events") && contains("log.genericEvent", "type") && contains("log.genericEvent", "ssid") && contains("log.genericEvent", "bssid") # ........................................ # Event: security_event ids_alerted, ids signature matched in Meraki MX Security Appliance - grok: @@ -1469,12 +1457,6 @@ pipeline: - protocol where: startsWith("log.genericEvent", "signature") && contains("log.genericEvent", "priority") && contains("log.genericEvent", "timestamp") && contains("log.genericEvent", "direction") && contains("log.genericEvent", "dhost") # Adding action result - - add: - function: 'string' - params: - key: actionResult - value: 'accepted' - where: startsWith("log.genericEvent", "signature") && contains("log.genericEvent", "priority") && contains("log.genericEvent", "timestamp") && contains("log.genericEvent", "direction") && contains("log.genericEvent", "dhost") # ............................................................................ # Cleaning common fields - trim: @@ -1546,4 +1528,77 @@ pipeline: fields: - log.controlFlag - log.irrelevant - - log.genericEvent \ No newline at end of file + - log.genericEvent + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: denied + where: equalsIgnoreCase("log.merakiResult","blocked") + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/crowdstrike/crowdstrike.yml b/filters/crowdstrike/crowdstrike.yml index 560140d6e..3cffe91dc 100644 --- a/filters/crowdstrike/crowdstrike.yml +++ b/filters/crowdstrike/crowdstrike.yml @@ -343,6 +343,12 @@ pipeline: from: - log.event.LocalIP to: origin.ip + where: '!exists("origin.ip")' + + - rename: + from: + - log.event.LocalIP + to: log.eventLocalIP - rename: from: @@ -680,8 +686,8 @@ pipeline: function: string params: key: actionResult - value: "failed" - where: 'exists("log.eventSuccess") && oneOf("log.eventSuccess", [false, "false"])' + value: failure + where: exists("log.eventSuccess") && oneOf("log.eventSuccess", [false, "false"]) - add: function: string params: @@ -692,8 +698,8 @@ pipeline: function: string params: key: actionResult - value: "failed" - where: 'exists("statusCode") && greaterOrEqual("statusCode", 400)' + value: failure + where: exists("statusCode") && greaterOrEqual("statusCode", 400) # .......................................................................# # Normalizing request method and renaming to action @@ -741,4 +747,16 @@ pipeline: - log.statusCode - log.event.UserIp - log.event.Attributes.user_ip - - log.event.Attributes.action_target_name \ No newline at end of file + - log.event.Attributes.action_target_name + + # 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","::"])) diff --git a/filters/fortinet/fortinet.yml b/filters/fortinet/fortinet.yml index 54ecdb9bf..49325dd4d 100644 --- a/filters/fortinet/fortinet.yml +++ b/filters/fortinet/fortinet.yml @@ -305,24 +305,6 @@ pipeline: to: origin.packagesSent # Adding action result - - add: - function: string - params: - key: actionResult - value: "accept" - where: 'equals("action", "accept")' - - add: - function: string - params: - key: actionResult - value: "denied" - where: 'equals("action", "deny")' - - add: - function: string - params: - key: actionResult - value: "blocked" - where: 'oneOf("action", ["client-rst", "server-rst", "ip-conn"])' # Type casting for numeric fields. - cast: @@ -1315,4 +1297,95 @@ pipeline: - delete: fields: - log.kvMessage - - log.proto \ No newline at end of file + - log.proto + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: success + where: equalsIgnoreCase("action", "accept") + - add: + function: string + params: + key: actionResult + value: failure + where: oneOf("action", ["ip-conn", "dns"]) + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("action", "(?i)^(deny|denied|drop|dropped|block|blocked)$") || regexMatch("log.utmaction", "(?i)^(deny|denied|drop|dropped|block|blocked)$") || regexMatch("log.FTNTFGTutmaction", "(?i)^(deny|denied|drop|dropped|block|blocked)$") + - add: + function: string + params: + key: connectionStatus + value: closed + where: oneOf("action", ["close", "client-rst", "server-rst", "timeout"]) + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/fortinet/fortiweb.yml b/filters/fortinet/fortiweb.yml index 686a9356e..fa136b068 100644 --- a/filters/fortinet/fortiweb.yml +++ b/filters/fortinet/fortiweb.yml @@ -100,8 +100,8 @@ pipeline: function: string params: key: actionResult - value: "blocked" - where: 'exists("action") && equalsIgnoreCase("action", "Deny")' + value: denied + where: exists("action") && equalsIgnoreCase("action", "Deny") # Removing unused caracters - trim: @@ -135,4 +135,16 @@ pipeline: - delete: fields: - log.kvMessage - - log.irrelevant \ No newline at end of file + - log.irrelevant + + # 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","::"])) diff --git a/filters/generic/generic.yml b/filters/generic/generic.yml index 5dd70cdd9..0220def80 100644 --- a/filters/generic/generic.yml +++ b/filters/generic/generic.yml @@ -6,7 +6,7 @@ pipeline: steps: - grok: patterns: - - field_name: log.message + - fieldName: log.message pattern: '(.*)' source: raw - json: diff --git a/filters/github/github.yml b/filters/github/github.yml index 7439a3d51..79aa967ba 100644 --- a/filters/github/github.yml +++ b/filters/github/github.yml @@ -679,8 +679,8 @@ pipeline: function: string params: key: actionResult - value: "failed" - where: 'oneOf("log.conclusion", ["failure", "timed_out", "startup_failure", "action_required"])' + value: failure + where: oneOf("log.conclusion", ["failure", "timed_out", "startup_failure", "action_required"]) # Removing unused fields - delete: @@ -696,4 +696,12 @@ pipeline: - log.sender - log.pusher - log.repository.owner - - log.repository \ No newline at end of file + - log.repository + + # Promote standard fields while retaining vendor fields used by rules. + - grok: + source: log.senderLogin + patterns: + - fieldName: origin.user + pattern: '{{.greedy}}' + where: exists("log.senderLogin") && !exists("origin.user") diff --git a/filters/google/gcp.yml b/filters/google/gcp.yml index 46b69fb9b..222ac9ac0 100644 --- a/filters/google/gcp.yml +++ b/filters/google/gcp.yml @@ -380,24 +380,24 @@ pipeline: # Adding severity field based on log.severity - add: - function: "string" + function: string params: key: severity - value: "low" + value: info where: oneOf("log.severity", ["INFO", "NOTICE"]) - add: - function: "string" + function: string params: key: severity - value: "medium" + value: warning where: equals("log.severity", "WARNING") - add: - function: "string" + function: string params: key: severity - value: "high" + value: error where: equals("log.severity", "ERROR") # Adding action field based on log.httpRequestMethod @@ -445,10 +445,10 @@ pipeline: # Adding actionResult field based on log.jsonPayloadEnforcedEdgeSecurityPolicyOutcome - add: - function: "string" + function: string params: key: actionResult - value: "accepted" + value: success where: equals("log.jsonPayloadEnforcedEdgeSecurityPolicyOutcome", "ACCEPT") - add: @@ -480,11 +480,11 @@ pipeline: where: 'exists("log.protoPayloadMethodName") && equals("log.protoPayloadStatusCode", 0)' - add: - function: "string" + function: string params: key: actionResult - value: "failed" - where: 'exists("log.protoPayloadMethodName") && greaterThan("log.protoPayloadStatusCode", 0)' + value: failure + where: exists("log.protoPayloadMethodName") && greaterThan("log.protoPayloadStatusCode", 0) # Adding actionResult for Cloud DNS query logs (dns.googleapis.com). - add: @@ -502,10 +502,10 @@ pipeline: where: '!exists("actionResult") && equals("log.jsonPayloadResponseCode", "REFUSED")' - add: - function: "string" + function: string params: key: actionResult - value: "failed" + value: failure where: '!exists("actionResult") && exists("log.jsonPayloadResponseCode")' # Adding actionResult for plain HTTP request logs (Cloud Run, Load @@ -525,11 +525,11 @@ pipeline: where: 'exists("statusCode") && !exists("actionResult") && oneOf("statusCode", [401, 403])' - add: - function: "string" + function: string params: key: actionResult - value: "failed" - where: 'exists("statusCode") && !exists("actionResult") && greaterOrEqual("statusCode", 400)' + value: failure + where: exists("statusCode") && !exists("actionResult") && greaterOrEqual("statusCode", 400) # Adding geolocation to origin.ip - dynamic: @@ -547,6 +547,20 @@ pipeline: destination: target.geolocation where: exists("target.ip") + # Normalize the source event severity. + - add: + function: string + params: + key: severity + value: critical + where: oneOf("log.severity",["EMERGENCY","ALERT","CRITICAL"]) + - add: + function: string + params: + key: severity + value: debug + where: equals("log.severity","DEBUG") + # Removing unused fields - delete: fields: @@ -556,4 +570,69 @@ pipeline: - log.httpRequestMethod - log.jsonPayloadEnforcedEdgeSecurityPolicyOutcome - log.severity - - log.jsonPayloadStructuredRdata \ No newline at end of file + - log.jsonPayloadStructuredRdata + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/ibm/ibm_aix.yml b/filters/ibm/ibm_aix.yml index 90c9c0bbb..c81a78bec 100644 --- a/filters/ibm/ibm_aix.yml +++ b/filters/ibm/ibm_aix.yml @@ -46,9 +46,9 @@ pipeline: # .......................................................................# - grok: patterns: - - fildName: log.irrelevant + - fieldName: log.irrelevant pattern: 'Message(\s)forwarded(\s)from' - - fieldName: from.host + - fieldName: origin.host pattern: '{{.data}}(\:)' - fieldName: log.msgAll pattern: '{{.greedy}}' @@ -60,7 +60,7 @@ pipeline: function: suffix substring: ":" fields: - - from.host + - origin.host #......................................................................# # Checking that the message contains TTY= or PWD= or COMMAND= or USER= @@ -549,8 +549,8 @@ pipeline: function: string params: key: actionResult - value: "failed" - where: 'exists("log.returnCode") && !equals("log.returnCode", "0")' + value: failure + where: exists("log.returnCode") && !equals("log.returnCode", "0") # Adding geolocation to origin.ip - dynamic: @@ -577,4 +577,64 @@ pipeline: - log.msgWithTTY - log.msgAll - log.msgInit - - log.restMsg \ No newline at end of file + - log.restMsg + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/ibm/ibm_as_400.yml b/filters/ibm/ibm_as_400.yml index 11522baef..0ccdf0ef8 100644 --- a/filters/ibm/ibm_as_400.yml +++ b/filters/ibm/ibm_as_400.yml @@ -85,4 +85,11 @@ pipeline: params: source: origin.ip destination: origin.geolocation - where: exists("origin.ip") \ No newline at end of file + where: exists("origin.ip") + + # 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","::"])) diff --git a/filters/linux/linux.yml b/filters/linux/linux.yml index 78110c46f..202f3ac0a 100644 --- a/filters/linux/linux.yml +++ b/filters/linux/linux.yml @@ -273,9 +273,27 @@ pipeline: - rename: from: - - log.JOBRESULT - to: actionResult - where: exists("log.JOBRESULT") && !regexMatch("log.JOBRESULT", "(?i)\b(?:denied|blocked|failed)\b") + - log.JOBRESULT + to: log.vendorActionResult + where: exists("log.JOBRESULT") + - add: + function: string + params: + key: actionResult + value: success + where: regexMatch("log.vendorActionResult", "(?i)^(success|succeeded|successful|ok|done|accepted|accept|allowed|allow|permitted|permit|passed|pass|true)$") + - add: + function: string + params: + key: actionResult + value: failure + where: regexMatch("log.vendorActionResult", "(?i)^(failure|failed|fail|error|invalid|timeout|false)$") + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("log.vendorActionResult", "(?i)^(denied|deny|blocked|block|dropped|drop|rejected|reject|forbidden|unauthorized|quarantined)$") - rename: from: @@ -341,15 +359,15 @@ pipeline: function: string params: key: severity - value: "emergency" - where: 'equals("log.priority", "0")' + value: critical + where: equals("log.priority", "0") - add: function: string params: key: severity - value: "alert" - where: 'equals("log.priority", "1")' + value: critical + where: equals("log.priority", "1") - add: function: string @@ -376,8 +394,8 @@ pipeline: function: string params: key: severity - value: "notice" - where: 'equals("log.priority", "5")' + value: info + where: equals("log.priority", "5") - add: function: string @@ -416,39 +434,57 @@ pipeline: # Map result to actionResult (success/failure) - rename: from: - - log.result - to: actionResult - where: 'equals("log.type", "auditd") && exists("log.result") && !regexMatch("log.result", "(?i)\b(?:denied|blocked|failed)\b")' + - log.result + to: log.vendorActionResult + where: exists("log.result") + - add: + function: string + params: + key: actionResult + value: success + where: regexMatch("log.vendorActionResult", "(?i)^(success|succeeded|successful|ok|done|accepted|accept|allowed|allow|permitted|permit|passed|pass|true)$") + - add: + function: string + params: + key: actionResult + value: failure + where: regexMatch("log.vendorActionResult", "(?i)^(failure|failed|fail|error|invalid|timeout|false)$") + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("log.vendorActionResult", "(?i)^(denied|deny|blocked|block|dropped|drop|rejected|reject|forbidden|unauthorized|quarantined)$") # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'denied' - where: regexMatch("log.result", "(?i)\bdenied\b") + value: denied + where: regexMatch("log.result", "(?i)\\bdenied\\b") - add: - function: 'string' + function: string params: key: actionResult - value: 'blocked' - where: regexMatch("log.result", "(?i)\bblocked\b") + value: denied + where: regexMatch("log.result", "(?i)\\bblocked\\b") - add: - function: 'string' + function: string params: key: actionResult - value: 'failed' - where: regexMatch("log.result", "(?i)\bfailed\b") + value: failure + where: regexMatch("log.result", "(?i)\\bfailed\\b") # Map exe to origin.process (full path to executable) # Only if journald's COMM didn't already set origin.process - rename: from: - - log.exe - to: origin.process - where: 'equals("log.type", "auditd") && exists("log.exe") && !exists("origin.process")' + - log.exe + to: origin.path + where: equals("log.type", "auditd") && exists("log.exe") # Fallback: map auditd comm to origin.process if exe not mapped - rename: @@ -474,19 +510,27 @@ pipeline: # Map cwd to origin.path (current working directory) - rename: from: - - log.cwd - to: origin.path - where: 'equals("log.type", "auditd") && exists("log.cwd")' + - log.cwd + to: log.workingDirectory + where: equals("log.type", "auditd") && exists("log.cwd") # Map exit code to statusCode (for correlation and alerting) - rename: from: - - log.exit - to: statusCode - where: 'equals("log.type", "auditd") && exists("log.exit")' + - log.exit + to: log.exitCode + where: equals("log.type", "auditd") && exists("log.exit") # Cast statusCode to integer (proto schema expects uint32) - cast: fields: [statusCode] to: int - where: 'equals("log.type", "auditd") && exists("statusCode")' \ No newline at end of file + where: 'equals("log.type", "auditd") && exists("statusCode")' + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: failure + where: '!exists("actionResult") && lessThan("log.exitCode",0)' diff --git a/filters/mikrotik/mikrotik-fw.yml b/filters/mikrotik/mikrotik-fw.yml index 411cd1242..f9c2b0058 100644 --- a/filters/mikrotik/mikrotik-fw.yml +++ b/filters/mikrotik/mikrotik-fw.yml @@ -210,8 +210,8 @@ pipeline: function: string params: key: actionResult - value: "blocked" - where: 'exists("log.action") && equals("log.action", "drop")' + value: denied + where: exists("log.action") && equals("log.action", "drop") # Removing unused fields - delete: @@ -221,4 +221,69 @@ pipeline: - log.trash3 - log.trash4 - log.trash5 - - log.restData \ No newline at end of file + - log.restData + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/netflow/netflow.yml b/filters/netflow/netflow.yml index 402618f72..b87d3377f 100644 --- a/filters/netflow/netflow.yml +++ b/filters/netflow/netflow.yml @@ -1159,4 +1159,69 @@ pipeline: - log.irrelevant8 - log.irrelevant9 - log.irrelevant10 - - log.irrelevant11 \ No newline at end of file + - log.irrelevant11 + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/office365/o365.yml b/filters/office365/o365.yml index 4e4986df6..ea5bbc8af 100755 --- a/filters/office365/o365.yml +++ b/filters/office365/o365.yml @@ -95,26 +95,57 @@ pipeline: # Adding action result - add: - function: 'string' + function: string params: key: actionResult - value: 'success' - where: oneOf("log.ResultStatus", ["Succeeded", "Success", "Successful", "PartiallySucceeded", "True"]) + value: success + where: oneOf("log.ResultStatus", ["Succeeded", "Success", "Successful", "True"]) - add: - function: 'string' + function: string params: key: actionResult - value: 'failed' + value: failure where: oneOf("log.ResultStatus", ["Failure", "Failed", "False"]) - add: - function: 'string' + function: string params: key: actionResult - value: 'blocked' + value: denied where: equals("log.ResultStatus", "Blocked") + # ClientIP may include a port; retain the reported endpoint for auditing. + - grok: + source: origin.ip + patterns: + - fieldName: log.originEndpoint + pattern: '{{.greedy}}' + where: regexMatch("origin.ip", "^([0-9]{1,3}[.]){3}[0-9]{1,3}:[0-9]+$") || regexMatch("origin.ip", "^\\[[0-9a-fA-F:]+\\]:[0-9]+$") + - grok: + source: log.originEndpoint + patterns: + - fieldName: log.originEndpointPort + pattern: '{{.greedy}}' + where: exists("log.originEndpoint") + - trim: + fields: + - log.originEndpointPort + function: regex + substring: '^.*:' + where: exists("log.originEndpoint") + - trim: + fields: + - origin.ip + function: regex + substring: ^\[|\]?:[0-9]+$ + where: exists("log.originEndpoint") + - rename: + from: + - log.originEndpointPort + to: origin.port + where: exists("log.originEndpoint") && (inCIDR("origin.ip","0.0.0.0/0") || inCIDR("origin.ip","::/0")) && lessOrEqual("log.originEndpointPort",65535) + # Adding geolocation to origin ip - dynamic: plugin: com.utmstack.geolocation @@ -130,4 +161,19 @@ pipeline: # Removing unused fields - delete: fields: - - log.AppAccessContext \ No newline at end of file + - log.AppAccessContext + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: failure + where: equals("action", "UserLoginFailed") + + # 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","::"])) diff --git a/filters/paloalto/pa_firewall.yml b/filters/paloalto/pa_firewall.yml index 8503e8cde..ab7f79eab 100644 --- a/filters/paloalto/pa_firewall.yml +++ b/filters/paloalto/pa_firewall.yml @@ -1543,15 +1543,15 @@ pipeline: source: log.cefOrLeefMsgAll - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(panOSContainerNameSpace=)' - - fieldName: log.panOSContainerNameSpace - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefOrLeefMsgAll - + patterns: + - fieldName: log.irrelevant + pattern: '{{.data}}(panOSContainerNameSpace=)' + - fieldName: log.panOSContainerNameSpace + pattern: '{{.data}}({{.word}}=)' + - fieldName: log.irrelevant + pattern: '{{.greedy}}' + source: log.cefOrLeefMsgAll + - grok: patterns: - fieldName: log.irrelevant @@ -1743,14 +1743,14 @@ pipeline: source: log.cefOrLeefMsgAll - grok: - patterns: - - fieldName: log.irrelevant - pattern: '{{.data}}(panOSDGl1=)' - - fieldName: log.panOSDGl1 - pattern: '{{.data}}({{.word}}=)' - - fieldName: log.irrelevant - pattern: '{{.greedy}}' - source: log.cefOrLeefMsgAll + patterns: + - fieldName: log.irrelevant + pattern: '{{.data}}(panOSDGl1=)' + - fieldName: log.panOSDGl1 + pattern: '{{.data}}({{.word}}=)' + - fieldName: log.irrelevant + pattern: '{{.greedy}}' + source: log.cefOrLeefMsgAll - grok: patterns: @@ -6381,7 +6381,7 @@ pipeline: # Detect if its a TUNNEL INSPECTION log # ..........................................................................# - csv: - from: log.csvMsgAll + source: log.csvMsgAll separator: "," headers: - log.receive_time @@ -6472,7 +6472,7 @@ pipeline: # Detect if its a SCTP log # ..........................................................................# - csv: - from: log.csvMsgAll + source: log.csvMsgAll separator: "," headers: - log.receive_time @@ -6545,7 +6545,7 @@ pipeline: # Detect if its a CONFIG log #......................................................................# - csv: - from: log.csvMsgAll + source: log.csvMsgAll separator: ',' headers: - log.receive_time @@ -6580,7 +6580,7 @@ pipeline: # Detect if its a AUTHENTICATION log #......................................................................# - csv: - from: log.csvMsgAll + source: log.csvMsgAll separator: ',' headers: - log.receive_time @@ -6633,7 +6633,7 @@ pipeline: # Detect if its a SYSTEM log #......................................................................# - csv: - from: log.csvMsgAll + source: log.csvMsgAll separator: ',' headers: - log.receive_time @@ -6667,7 +6667,7 @@ pipeline: # Detect if its a CORRELATED EVENTS log #......................................................................# - csv: - from: log.csvMsgAll + source: log.csvMsgAll separator: "," headers: - log.receive_time @@ -6697,7 +6697,7 @@ pipeline: # Detect if its a GTP log #......................................................................# - csv: - from: log.csvMsgAll + source: log.csvMsgAll separator: "," headers: - log.receive_time @@ -6810,22 +6810,22 @@ pipeline: - rename: from: - log.bytessent - to: target.bytesSent + to: origin.bytesSent - rename: from: - log.bytesreceived - to: target.bytesReceived + to: origin.bytesReceived - rename: from: - log.pktssent - to: target.packagesSent + to: origin.packagesSent - rename: from: - log.pktsreceived - to: target.packagesReceived + to: origin.packagesReceived - rename: from: @@ -6850,69 +6850,27 @@ pipeline: # ................................................# # Adding action result # ................................................# - - add: - function: string - params: - key: actionResult - value: "allow" - where: '!exists("log.status") && equalsIgnoreCase("action", "allow")' - - add: - function: string - params: - key: actionResult - value: "denied" - where: '!exists("log.status") && oneOf("action", ["deny", "Deny"])' - - add: - function: string - params: - key: actionResult - value: "blocked" - where: '!exists("log.status") && oneOf("action", ["drop", "Drop", "reset-client", "reset-server", "reset-both", "block-url", "block-ip", "random-drop", "sinkhole"])' - - add: - function: string - params: - key: actionResult - value: "Succeeded" - where: '!exists("log.status") && oneOf("log.result", ["Succeeded", "Submitted"])' - - add: - function: string - params: - key: actionResult - value: "failed" - where: '!exists("log.status") && oneOf("log.result", ["Failed", "Unauthorized"])' - - add: - function: string - params: - key: actionResult - value: "success" - where: 'equals("log.status", "success")' - - add: - function: string - params: - key: actionResult - value: "failed" - where: 'equals("log.status", "failure")' # ................................................# # Fileds conversions # ................................................# - cast: fields: - - target.bytessent - - target.bytesreceived + - origin.bytesSent + - origin.bytesReceived to: float - cast: fields: - - target.pktssent - - target.packagesReceived - to: int64 + - origin.packagesSent + - origin.packagesReceived + to: int - cast: fields: @@ -6941,4 +6899,101 @@ pipeline: # ..........................................................................# - delete: fields: - - log.csvMsgAll \ No newline at end of file + - log.csvMsgAll + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("action", "(?i)^(deny|drop|drop ICMP|reset[- ]client|reset[- ]server|reset[- ]both|block-url|block-ip|random-drop|sinkhole)$") + - add: + function: string + params: + key: actionResult + value: success + where: '!exists("actionResult") && equalsIgnoreCase("action", "allow")' + - add: + function: string + params: + key: actionResult + value: denied + where: '!exists("actionResult") && equalsIgnoreCase("log.result", "Unauthorized")' + - add: + function: string + params: + key: actionResult + value: failure + where: '!exists("actionResult") && (equalsIgnoreCase("log.result", "Failed") || equalsIgnoreCase("log.status", "failure"))' + - add: + function: string + params: + key: actionResult + value: success + where: '!exists("actionResult") && (equalsIgnoreCase("log.result", "Succeeded") || equalsIgnoreCase("log.status", "success"))' + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/pfsense/pfsense_fw.yml b/filters/pfsense/pfsense_fw.yml index 2d402b794..154bba6ff 100644 --- a/filters/pfsense/pfsense_fw.yml +++ b/filters/pfsense/pfsense_fw.yml @@ -342,14 +342,14 @@ pipeline: function: string params: key: actionResult - value: "pass" - where: 'equalsIgnoreCase("action", "pass")' + value: success + where: equalsIgnoreCase("action", "pass") - add: function: string params: key: actionResult - value: "blocked" - where: 'equalsIgnoreCase("action", "block")' + value: denied + where: equalsIgnoreCase("action", "block") # ................................................# # Fileds conversions @@ -381,4 +381,69 @@ pipeline: # ..........................................................................# - delete: fields: - - log.csvMsg \ No newline at end of file + - log.csvMsg + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/sonicwall/sonic_wall.yml b/filters/sonicwall/sonic_wall.yml index d20d7b247..f9034a804 100644 --- a/filters/sonicwall/sonic_wall.yml +++ b/filters/sonicwall/sonic_wall.yml @@ -852,3 +852,68 @@ pipeline: - log.cefVersion - log.fwaction - log.grokTrash + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/sophos/sophos_central.yml b/filters/sophos/sophos_central.yml index d915b2fc5..d1b0272e4 100755 --- a/filters/sophos/sophos_central.yml +++ b/filters/sophos/sophos_central.yml @@ -55,33 +55,33 @@ pipeline: function: string params: key: actionResult - value: "blocked" - where: 'exists("log.action") && equals("log.action", "blocked")' + value: denied + where: exists("log.action") && equals("log.action", "blocked") - add: function: string params: key: actionResult - value: "blocked" - where: 'exists("log.type") && contains("log.type", "Prevented") && !exists("actionResult")' + value: denied + where: exists("log.type") && contains("log.type", "Prevented") && !exists("actionResult") - add: function: string params: key: actionResult - value: "blocked" - where: 'exists("log.type") && oneOf("log.type", ["Event::Endpoint::HmpaApplicationHijacking", "Event::Endpoint::HmpaPrivGuard"]) && !exists("actionResult")' + value: denied + where: exists("log.type") && oneOf("log.type", ["Event::Endpoint::HmpaApplicationHijacking", "Event::Endpoint::HmpaPrivGuard"]) && !exists("actionResult") - add: function: string params: key: actionResult - value: "blocked" - where: 'exists("log.type") && contains("log.type", "HmpaCred") && !exists("actionResult")' + value: denied + where: exists("log.type") && contains("log.type", "HmpaCred") && !exists("actionResult") - add: function: string params: key: actionResult - value: "failed" - where: 'exists("log.type") && contains("log.type", "AuthenticationFailure") && !exists("actionResult")' \ No newline at end of file + value: failure + where: exists("log.type") && contains("log.type", "AuthenticationFailure") && !exists("actionResult") diff --git a/filters/sophos/sophos_xg_firewall.yml b/filters/sophos/sophos_xg_firewall.yml index fbd6d1ff1..968365aee 100644 --- a/filters/sophos/sophos_xg_firewall.yml +++ b/filters/sophos/sophos_xg_firewall.yml @@ -423,8 +423,7 @@ pipeline: - rename: from: - log.ftpcommand - to: command - + to: origin.command - rename: from: - log.FTPurl @@ -546,7 +545,7 @@ pipeline: - log.outInterface - log.vLanId - log.deviceType - - command + - origin.command - log.clientHostName - log.ipAddress - log.clientPhysicalAddress @@ -604,7 +603,7 @@ pipeline: - log.outInterface - log.vLanId - log.deviceType - - command + - origin.command - log.clientHostName - log.ipAddress - log.clientPhysicalAddress @@ -684,19 +683,7 @@ pipeline: to: float # Adding actionResult based on log.subtype value - - add: - function: 'string' - params: - key: actionResult - value: 'denied' - where: exists("log.subType") && regexMatch("log.subType", "(?i)\bdenied\b") - - add: - function: 'string' - params: - key: actionResult - value: 'accepted' - where: exists("log.subType") && oneOf("log.subType", ["Accepted", "Allowed"]) # Renaming "log.statusCode" to "statusCode" to add it to the event structure - rename: @@ -705,22 +692,6 @@ pipeline: to: statusCode where: exists("log.statusCode") - # Adding actionResult - # denied by default - - add: - function: 'string' - params: - key: actionResult - value: 'denied' - where: exists("statusCode") - - - add: - function: 'string' - params: - key: actionResult - value: 'accepted' - where: exists("statusCode") && ((greaterOrEqual("statusCode", 200) && lessOrEqual("statusCode", 299)) || (greaterOrEqual("statusCode", 300) && lessOrEqual("statusCode", 399) && greaterThan("origin.bytesReceived", 0))) - # Removing unused fields - delete: fields: @@ -756,4 +727,101 @@ pipeline: - log.logcomponent - log.logsubtype - log.name - - log.logtype \ No newline at end of file + - log.logtype + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("log.subType", "(?i)^(denied|blocked|dropped)$") || regexMatch("log.status", "(?i)^(deny|denied|block|blocked|drop|dropped)$") + - add: + function: string + params: + key: actionResult + value: success + where: '!exists("actionResult") && (regexMatch("log.subType", "(?i)^(accepted|allowed)$") || regexMatch("log.status", "(?i)^(allow|allowed|accept|accepted)$"))' + - add: + function: string + params: + key: actionResult + value: denied + where: '!exists("actionResult") && oneOf("statusCode", [401,403])' + - add: + function: string + params: + key: actionResult + value: failure + where: '!exists("actionResult") && greaterOrEqual("statusCode",400) && lessThan("statusCode",600)' + - add: + function: string + params: + key: actionResult + value: success + where: '!exists("actionResult") && greaterOrEqual("statusCode",200) && lessThan("statusCode",400)' + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/suricata/suricata.yml b/filters/suricata/suricata.yml index 5635ebcaa..484cdcabb 100644 --- a/filters/suricata/suricata.yml +++ b/filters/suricata/suricata.yml @@ -31,6 +31,9 @@ pipeline: - json: source: log.suricataJson where: regexMatch("log.suricataJson", "^\\{.*\\}$") + - json: + source: raw + where: '!exists("log.suricataJson") && regexMatch("raw", "^\\s*\\{")' # Rename fields to conversion v11 schema - rename: @@ -91,24 +94,24 @@ pipeline: # Adding severity field based on log.alert.severity - add: - function: "string" + function: string params: key: severity - value: "low" + value: critical where: equals("log.alert.severity", 1) - add: - function: "string" + function: string params: key: severity - value: "medium" + value: warning where: equals("log.alert.severity", 2) - add: - function: "string" + function: string params: key: severity - value: "high" + value: info where: equals("log.alert.severity", 3) #....................................................................... @@ -124,46 +127,9 @@ pipeline: to: log.fileInfo # Implementing action field used for established connections - - add: - function: "string" - params: - key: action - value: "success" - where: (equals("log.eventType", "tls") && exists("log.tls.sessionresumed")) || - (equals("log.eventType", "dns") && equals("log.dns.type", "answer")) || - (equals("log.eventType", "flow") && exists("origin.ip") && exists("target.ip") && greaterThan("log.flow.bytestoserver", 0) && greaterThan("log.flow.bytestoclient", 0)) || - (equals("log.eventType", "ssh") && exists("log.ssh.server") && exists("log.ssh.client")) || - (equals("log.eventType", "alert") && exists("origin.ip") && exists("target.ip") && equals("log.alert.action", "allowed") && greaterThan("log.flow.bytestoserver", 0) && greaterThan("log.flow.bytestoclient", 0)) || - (equals("log.eventType", "http") && exists("origin.ip") && exists("target.ip") && exists("log.http.status")) || - (equals("log.eventType", "ftp") && exists("origin.ip") && exists("target.ip") && exists("log.ftp.completioncode")) || - (equals("log.eventType", "tftp") && exists("origin.ip") && exists("target.ip") && !equals("log.tftp.packet", "error")) || - (equals("log.eventType", "smb") && exists("origin.ip") && exists("target.ip") && oneOf("log.smb.command", ["NEGOTIATE"]) && oneOf("log.smb.status", ["SUCCESS", "GRANTED", "CONNECTED"])) || - (equals("log.eventType", "rdp") && exists("origin.ip") && exists("target.ip") && (equals("log.rdp.eventType", "connectresponse") || equals("log.rdp.eventType", "tlshandshake"))) || - (equals("log.eventType", "rfb") && exists("origin.ip") && exists("target.ip") && equals("log.rfb.authentication.security-result", "OK")) || - (equals("log.eventType", "mqtt") && exists("origin.ip") && exists("target.ip") && (equals("log.mqtt.connack.returncode", "0") || equals("log.mqtt.connack.returncode", "0x00"))) || - (equals("log.eventType", "pgsql") && exists("origin.ip") && exists("target.ip") && (exists("log.pgsql.request.simplequery") || exists("log.pgsql.response.commandcompleted") || equals("log.pgsql.response.sslaccepted", "true") || equals("log.pgsql.response.accepted", "true") || exists("log.pgsql.response.authenticationmd5password"))) || - ((equals("log.eventType", "ike") || equals("log.eventType", "ikev1") || equals("log.eventType", "ikev2")) && exists("origin.ip") && exists("target.ip")) || - (equals("log.eventType", "modbus") && exists("origin.ip") && exists("target.ip")) || - (equals("log.eventType", "sip") && exists("origin.ip") && exists("target.ip")) || - (equals("log.eventType", "quic") && exists("origin.ip") && exists("target.ip")) || - (equals("log.eventType", "fileinfo") && exists("origin.ip") && exists("target.ip")) || - (equals("log.eventType", "snmp") && exists("origin.ip") && exists("target.ip")) || - (equals("log.eventType", "dhcp") && exists("origin.ip") && exists("target.ip") && !equals("log.dhcp.assignedip", "0")) # Adding action result - - add: - function: string - params: - key: actionResult - value: "allowed" - where: 'exists("log.alert.action") && equals("log.alert.action", "allowed")' - - add: - function: string - params: - key: actionResult - value: "blocked" - where: 'exists("log.alert.action") && equals("log.alert.action", "blocked")' # Adding geolocation to origin.ip - dynamic: @@ -188,4 +154,101 @@ pipeline: - log.syslogPri - log.syslogTimestamp - log.syslogProgram - - log.syslogPid \ No newline at end of file + - log.syslogPid + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: denied + where: oneOf("log.verdict.action",["drop","reject"]) || exists("log.verdict.reject") || equals("log.flow.action","drop") || equals("log.alert.action","blocked") + - add: + function: string + params: + key: actionResult + value: success + where: '!exists("actionResult") && (equals("log.verdict.action","pass") || equals("log.flow.action","pass"))' + - add: + function: string + params: + key: actionResult + value: success + where: '!exists("actionResult") && equals("log.flow.state","established") && greaterThan("log.flow.bytestoserver",0) && greaterThan("log.flow.bytestoclient",0)' + - add: + function: string + params: + key: connectionStatus + value: established + where: equals("log.flow.state","established") && !equals("actionResult","denied") + - add: + function: string + params: + key: connectionStatus + value: closed + where: equals("log.flow.state","closed") + + # 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","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/syslog/syslog-generic.yml b/filters/syslog/syslog-generic.yml index 718d496dc..1b93efb42 100644 --- a/filters/syslog/syslog-generic.yml +++ b/filters/syslog/syslog-generic.yml @@ -6,6 +6,6 @@ pipeline: steps: - grok: patterns: - - field_name: log.message + - fieldName: log.message pattern: '(.*)' source: raw \ No newline at end of file diff --git a/filters/vmware/vmware-esxi.yml b/filters/vmware/vmware-esxi.yml index b2f58acc1..47f2dd0a9 100644 --- a/filters/vmware/vmware-esxi.yml +++ b/filters/vmware/vmware-esxi.yml @@ -19,7 +19,7 @@ pipeline: pattern: '\<{{.data}}\>' - fieldName: log.deviceTime pattern: '{{.year}}(-){{.monthNumber}}(-){{.monthDay}}(T){{.time}}(Z)' - - fieldName: origin.hostname + - fieldName: origin.host pattern: '{{.hostname}}' - fieldName: log.process pattern: '{{.hostname}}(\:)' @@ -40,7 +40,7 @@ pipeline: pattern: '\<{{.data}}\>' - fieldName: log.deviceTime pattern: '{{.year}}(-){{.monthNumber}}(-){{.monthDay}}(T){{.time}}(Z)' - - fieldName: origin.hostname + - fieldName: origin.host pattern: '{{.hostname}}' - fieldName: log.process pattern: '{{.hostname}}' @@ -55,7 +55,7 @@ pipeline: pattern: '\<{{.data}}\>' - fieldName: log.deviceTime pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}T{{.time}}Z' - - fieldName: origin.hostname + - fieldName: origin.host pattern: '{{.hostname}}' - fieldName: log.process pattern: '{{.hostname}}' @@ -136,14 +136,14 @@ pipeline: function: string params: key: actionResult - value: "failed" - where: 'exists("log.message") && contains("log.message", "authentication failed")' + value: failure + where: exists("log.message") && contains("log.message", "authentication failed") - add: function: string params: key: actionResult - value: "failed" - where: 'exists("log.message") && contains("log.message", "authentication of user") && contains("log.message", "failed") && !exists("actionResult")' + value: failure + where: exists("log.message") && contains("log.message", "authentication of user") && contains("log.message", "failed") && !exists("actionResult") - add: function: string params: diff --git a/filters/windows/windows-events.yml b/filters/windows/windows-events.yml index 1cea949aa..d96d9f10d 100644 --- a/filters/windows/windows-events.yml +++ b/filters/windows/windows-events.yml @@ -3037,29 +3037,29 @@ pipeline: value: "success" where: 'oneOf("log.eventCode", [4624, 4648, 4672, 4720, 4722, 4728, 4732, 4756, 4767])' - add: - function: 'string' + function: string params: key: actionResult - value: "blocked" - where: 'oneOf("log.eventCode", [4725, 4726, 4740])' + value: success + where: oneOf("log.eventCode", [4725, 4726, 4740]) - add: - function: 'string' + function: string params: key: actionResult - value: "failed" - where: 'oneOf("log.eventCode", [4625, 4771])' + value: failure + where: oneOf("log.eventCode", [4625, 4771]) - add: - function: 'string' + function: string params: key: actionResult - value: "success" - where: 'equals("log.eventCode", 4776) && equals("log.eventDataStatus", "0")' + value: success + where: equals("log.eventCode", 4776) && regexMatch("log.eventDataStatus", "(?i)^(0|0x0+)$") - add: - function: 'string' + function: string params: key: actionResult - value: "failed" - where: 'equals("log.eventCode", 4776) && exists("log.eventDataStatus") && !equals("log.eventDataStatus", "0")' + value: failure + where: equals("log.eventCode", 4776) && exists("log.eventDataStatus") && !regexMatch("log.eventDataStatus", "(?i)^(0|0x0+)$") - delete: fields: @@ -3070,4 +3070,51 @@ pipeline: - log.metadata - log.event - log.ecs - - log.log \ No newline at end of file + - log.log + + # 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","::"])) + + # Promote standard fields while retaining vendor fields used by rules. + - grok: + source: log.eventDataSubjectUserName + patterns: + - fieldName: origin.user + pattern: '{{.greedy}}' + where: exists("log.eventDataSubjectUserName") && !oneOf("log.eventDataSubjectUserName",["-",""]) + - grok: + source: target.user + patterns: + - fieldName: origin.user + pattern: '{{.greedy}}' + where: oneOf("log.eventCode",[4624,4625,4634,4647,4771,4776]) && exists("target.user") && !oneOf("target.user",["-",""]) + + # Normalize source fields to the standard event schema. + - delete: + fields: + - origin.user + where: oneOf("origin.user",["-",""]) + - delete: + fields: + - target.user + where: oneOf("target.user",["-",""]) + - delete: + fields: + - origin.host + where: oneOf("origin.host",["-",""]) + - delete: + fields: + - target.host + where: oneOf("target.host",["-",""]) + - delete: + fields: + - origin.domain + where: oneOf("origin.domain",["-",""]) + - delete: + fields: + - target.domain + where: oneOf("target.domain",["-",""]) diff --git a/plugins/alerts/filter_contract_test.go b/plugins/alerts/filter_contract_test.go new file mode 100644 index 000000000..3fb2e0529 --- /dev/null +++ b/plugins/alerts/filter_contract_test.go @@ -0,0 +1,188 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "regexp" + "strings" + "testing" + "time" + + "github.com/threatwinds/go-sdk/plugins" + "github.com/threatwinds/go-sdk/utils" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/reflect/protoreflect" +) + +func contractPaths(d protoreflect.MessageDescriptor, prefix string, out map[string]bool) { + for i := 0; i < d.Fields().Len(); i++ { + f := d.Fields().Get(i) + p := prefix + f.JSONName() + out[p] = true + if f.Message() != nil && !f.IsMap() && !strings.HasPrefix(string(f.Message().FullName()), "google.protobuf.") { + contractPaths(f.Message(), p+".", out) + } + } +} +func TestFilterAndRuleContracts(t *testing.T) { + eventPaths := map[string]bool{} + alertPaths := map[string]bool{} + contractPaths(new(plugins.Event).ProtoReflect().Descriptor(), "", eventPaths) + contractPaths(new(plugins.Alert).ProtoReflect().Descriptor(), "", alertPaths) + arrayIndex := regexp.MustCompile(`\.[0-9]+(\.|$)`) + eventPath := func(p string) bool { + p = strings.TrimSuffix(p, ".keyword") + p = arrayIndex.ReplaceAllString(p, "$1") + return eventPaths[p] || strings.HasPrefix(p, "log.") || strings.HasPrefix(p, "compliance.") + } + alertPath := func(p string) bool { + p = strings.TrimSuffix(p, ".keyword") + if strings.HasPrefix(p, "lastEvent.") { + return eventPath(strings.TrimPrefix(p, "lastEvent.")) + } + p = arrayIndex.ReplaceAllString(p, "$1") + return alertPaths[p] + } + cache := plugins.NewCELCache("filter-rule-contract-test") + sample := `{"log":{"messageId":0,"severity":0},"origin":{},"target":{},"action":"","actionResult":"","protocol":"","severity":"","connectionStatus":"","raw":"","dataType":"","dataSource":"","deviceTime":"","tenantId":"","tenantName":"","statusCode":0}` + var expressions func(*testing.T, any) + expressions = func(t *testing.T, v any) { + switch n := v.(type) { + case map[string]any: + for k, x := range n { + if k == "where" { + if w, ok := x.(string); ok && w != "" { + _, err := cache.Eval(w, sample) + // Direct selectors can fail on this empty sample after a successful compile. + // Their presence is not a syntax error or evidence that parsed logs fail. + if err != nil && !strings.Contains(err.Error(), "failed to evaluate program") { + t.Errorf("CEL compilation: %v", err) + } + } + } else { + expressions(t, x) + } + } + case []any: + for _, x := range n { + expressions(t, x) + } + } + } + for _, dir := range []string{"filters", "rules"} { + err := filepath.WalkDir(filepath.Join("../..", dir), func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() || (filepath.Ext(path) != ".yml" && filepath.Ext(path) != ".yaml") { + return nil + } + t.Run(path, func(t *testing.T) { + b, err := utils.ReadPbYaml(path) + if err != nil { + t.Fatal(err) + } + var doc any + if err = json.Unmarshal(b, &doc); err != nil { + t.Fatal(err) + } + expressions(t, doc) + if dir == "filters" { + cfg := new(plugins.Config) + if err = protojson.Unmarshal(b, cfg); err != nil { + t.Fatal(err) + } + for _, stage := range cfg.Pipeline { + for _, step := range stage.Steps { + fields := []string{} + if s := step.Rename; s != nil { + fields = append(fields, s.To) + } + if s := step.Grok; s != nil { + for _, p := range s.Patterns { + if p.FieldName != "" { // Empty grok names are non-capturing separators. + fields = append(fields, p.FieldName) + } + } + } + if s := step.Csv; s != nil { + fields = append(fields, s.Headers...) + } + if s := step.Add; s != nil { + fields = append(fields, s.Params["key"].GetStringValue()) + } + if s := step.Cast; s != nil { + fields = append(fields, s.Fields...) + } + for _, p := range fields { + if !eventPath(p) { + t.Errorf("unknown event write/cast field %q", p) + } + } + } + } + } else { + rule := new(plugins.Rule) + if err = protojson.Unmarshal(b, rule); err != nil { + t.Fatal(err) + } + rule.Normalize() + if len(rule.GroupBy) > 0 && len(rule.DeduplicateBy) > 0 { + t.Error("groupBy and deduplicateBy are mutually exclusive") + } + if rule.Adversary != "" && rule.Adversary != "origin" && rule.Adversary != "target" { + t.Errorf("unknown adversary %s", rule.Adversary) + } + for _, p := range append(rule.GroupBy, rule.DeduplicateBy...) { + if !alertPath(p) { + t.Errorf("unknown alert grouping field %q", p) + } + } + var checkSearch func([]*plugins.SearchRequest) + checkSearch = func(searches []*plugins.SearchRequest) { + for _, s := range searches { + if s.Within != "" { + if _, err := time.ParseDuration(s.Within); err != nil { + t.Error(err) + } + } + for _, x := range s.With { + valid := eventPath(x.Field) + if strings.Contains(s.IndexPattern, "-alert-") { + valid = alertPath(x.Field) + } + if !valid { + t.Errorf("unknown search field %q", x.Field) + } + if x.Value == nil { + t.Errorf("missing search value for %s", x.Field) + continue + } + v := x.Value.GetStringValue() + if strings.HasPrefix(v, "{{.") && strings.HasSuffix(v, "}}") { + p := strings.TrimSuffix(strings.TrimPrefix(v, "{{."), "}}") + if !eventPath(p) { + t.Errorf("unknown event placeholder %q", p) + } + } + switch x.Operator { + case "filter_term", "filter_match", "must_not_term", "must_not_match": + default: + t.Error(fmt.Sprintf("unknown search operator %s", x.Operator)) + } + } + checkSearch(s.Or) + } + } + checkSearch(rule.Correlation) + } + }) + return nil + }) + if err != nil { + t.Fatal(err) + } + } +} diff --git a/plugins/alerts/filter_normalization_test.go b/plugins/alerts/filter_normalization_test.go new file mode 100644 index 000000000..cf22870dd --- /dev/null +++ b/plugins/alerts/filter_normalization_test.go @@ -0,0 +1,230 @@ +package main + +// This is a normalization-stage model, NOT the closed EventProcessor parser. +// It uses the real SDK for YAML decoding, CEL evaluation and final Event conversion. +// Complex grok, CSV, KV, JSON extraction and dynamic plugins are deliberately skipped. +import ( + "encoding/json" + "fmt" + "github.com/threatwinds/go-sdk/plugins" + "github.com/threatwinds/go-sdk/utils" + "github.com/tidwall/gjson" + "google.golang.org/protobuf/encoding/protojson" + "os" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "testing" +) + +type Fixture struct { + Name string `json:"name"` + Filter string `json:"filter"` + Input map[string]any `json:"input"` + Expected map[string]any `json:"expected"` + Absent []string `json:"absent"` + Rules map[string]bool `json:"rules"` +} + +func valueAt(m map[string]any, p string) (any, bool) { + var v any = m + for _, k := range strings.Split(p, ".") { + switch n := v.(type) { + case map[string]any: + var ok bool + v, ok = n[k] + if !ok { + return nil, false + } + case []any: + i, e := strconv.Atoi(k) + if e != nil || i < 0 || i >= len(n) { + return nil, false + } + v = n[i] + default: + return nil, false + } + } + return v, true +} +func put(m map[string]any, p string, v any, remove bool) { + parts := strings.Split(p, ".") + for _, k := range parts[:len(parts)-1] { + next, ok := m[k].(map[string]any) + if !ok { + if remove { + return + } + next = map[string]any{} + m[k] = next + } + m = next + } + if remove { + delete(m, parts[len(parts)-1]) + } else { + m[parts[len(parts)-1]] = v + } +} +func normalize(root string, f Fixture, cache *plugins.CELCache) (string, []string, error) { + b, err := utils.ReadPbYaml(filepath.Join(root, "filters", f.Filter)) + if err != nil { + return "", nil, err + } + cfg := new(plugins.Config) + if err = (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(b, cfg); err != nil { + return "", nil, err + } + data, _ := json.Marshal(f.Input) + draft := map[string]any{} + json.Unmarshal(data, &draft) + issues := []string{} + for _, stage := range cfg.Pipeline { + for i, step := range stage.Steps { + sb, _ := protojson.Marshal(step) + obj := map[string]map[string]any{} + json.Unmarshal(sb, &obj) + for kind, body := range obj { + if kind == "dynamic" || kind == "json" || kind == "kv" || kind == "csv" || kind == "xml" || kind == "reformat" { + continue + } + if kind == "grok" && !(step.Grok != nil && len(step.Grok.Patterns) == 1 && step.Grok.Patterns[0].Pattern == "{{.greedy}}") { + continue + } + if w, ok := body["where"].(string); ok && w != "" { + snapshot, _ := json.Marshal(draft) + match, e := cache.Eval(w, string(snapshot)) + if e != nil { + issues = append(issues, fmt.Sprintf("step %d %s: %v", i, kind, e)) + continue + } + if !match { + continue + } + } + switch kind { + case "rename": + for _, src := range step.Rename.From { + if v, ok := valueAt(draft, src); ok { + put(draft, step.Rename.To, v, false) + put(draft, src, nil, true) + break + } + } + case "add": + p := step.Add.Params + put(draft, p["key"].GetStringValue(), p["value"].AsInterface(), false) + case "delete": + for _, p := range step.Delete.Fields { + put(draft, p, nil, true) + } + case "cast": + for _, p := range step.Cast.Fields { + if v, ok := valueAt(draft, p); ok { + switch step.Cast.To { + case "int": + put(draft, p, utils.CastInt64(v), false) + case "float": + put(draft, p, utils.CastFloat64(v), false) + case "string": + put(draft, p, utils.CastString(v), false) + } + } + } + case "trim": + for _, p := range step.Trim.Fields { + if v, ok := valueAt(draft, p); ok { + str, isString := v.(string) + if !isString { + continue + } + switch step.Trim.Function { + case "prefix": + str = strings.TrimPrefix(str, step.Trim.Substring) + case "suffix": + str = strings.TrimSuffix(str, step.Trim.Substring) + case "substring": + str = strings.ReplaceAll(str, step.Trim.Substring, "") + case "regex": + r, e := regexp.Compile(step.Trim.Substring) + if e == nil { + str = r.ReplaceAllString(str, "") + } + } + put(draft, p, str, false) + } + } + case "grok": + g := step.Grok + if v, ok := valueAt(draft, g.Source); ok { + if str, ok := v.(string); ok { + put(draft, g.Patterns[0].FieldName, str, false) + } + } + case "drop": + return "", issues, fmt.Errorf("fixture dropped") + } + } + } + } + data, _ = json.Marshal(draft) + str := string(data) + event := new(plugins.Event) + if err = utils.StringToProtoMessage(&str, event); err != nil { + return str, issues, err + } + out, err := utils.ProtoMessageToString(event) + if err != nil { + return "", issues, err + } + return *out, issues, nil +} + +// TestFilterNormalization supplies synthetic extraction results to the documented +// normalization steps. It is not a raw-log or dynamic-plugin integration test. +func TestFilterNormalization(t *testing.T) { + b, err := os.ReadFile("testdata/filter-normalization.json") + if err != nil { + t.Fatal(err) + } + var fixtures []Fixture + if err = json.Unmarshal(b, &fixtures); err != nil { + t.Fatal(err) + } + cache := plugins.NewCELCache("filter-normalization-test") + for _, f := range fixtures { + t.Run(f.Name, func(t *testing.T) { + out, issues, err := normalize("../..", f, cache) + if err != nil { + t.Fatal(err) + } + for _, issue := range issues { + t.Error(issue) + } + for p, want := range f.Expected { + got := gjson.Get(out, p) + if !got.Exists() || !reflect.DeepEqual(got.Value(), want) { + t.Errorf("%s: got %v, want %v", p, got.Value(), want) + } + } + for _, p := range f.Absent { + if gjson.Get(out, p).Exists() { + t.Errorf("unexpected %s in %s", p, out) + } + } + for path, want := range f.Rules { + b, err := utils.ReadPbYaml(filepath.Join("../..", path)) + if err != nil { + t.Fatal(err) + } + got, err := cache.Eval(gjson.GetBytes(b, "where").String(), out) + if err != nil || got != want { + t.Errorf("%s: got %v (%v), want %v", path, got, err, want) + } + } + }) + } +} diff --git a/plugins/alerts/grouping.go b/plugins/alerts/grouping.go new file mode 100644 index 000000000..ebec59267 --- /dev/null +++ b/plugins/alerts/grouping.go @@ -0,0 +1,38 @@ +package main + +import ( + "strconv" + "strings" + + "github.com/tidwall/gjson" +) + +// alertGroupingValue resolves the fields that rules can use in groupBy and +// deduplicateBy. The wire Alert has events, while the indexed document exposes +// the final event as lastEvent. Resolve that alias before building the search, +// using the same event as newAlert. Keep the search field itself as lastEvent.*. +func alertGroupingValue(alertJSON, field string) gjson.Result { + if strings.HasPrefix(field, "lastEvent.") { + count := gjson.Get(alertJSON, "events.#").Int() + if count == 0 { + return gjson.Result{} + } + field = "events." + strconv.FormatInt(count-1, 10) + "." + strings.TrimPrefix(field, "lastEvent.") + } + return gjson.Get(alertJSON, field) +} + +// A map or array is not an exact-match grouping term. In particular, finding only +// a non-scalar must not enable a name-only search that groups unrelated alerts. +func scalarGroupingValue(value gjson.Result) (any, bool) { + switch value.Type { + case gjson.String: + return value.String(), true + case gjson.Number: + return value.Float(), true + case gjson.True, gjson.False: + return value.Bool(), true + default: + return nil, false + } +} diff --git a/plugins/alerts/grouping_test.go b/plugins/alerts/grouping_test.go new file mode 100644 index 000000000..f27c70289 --- /dev/null +++ b/plugins/alerts/grouping_test.go @@ -0,0 +1,68 @@ +package main + +import ( + "github.com/tidwall/gjson" + "testing" + + "github.com/threatwinds/go-sdk/plugins" + "github.com/threatwinds/go-sdk/utils" + "google.golang.org/protobuf/types/known/structpb" +) + +func TestAlertGroupingValue(t *testing.T) { + eventLog, err := structpb.NewStruct(map[string]any{"eventCode": 4625, "isFailure": true, "accounts": []any{"alice", "bob"}}) + if err != nil { + t.Fatal(err) + } + alert := &plugins.Alert{ + Adversary: &plugins.Side{Ip: "203.0.113.7"}, + Target: &plugins.Side{Host: "dc01"}, + Events: []*plugins.Event{ + {Action: "previous", Origin: &plugins.Side{User: "previous-user"}}, + {Action: "login", Origin: &plugins.Side{User: "alice"}, Log: eventLog.Fields}, + }, + } + serialized, err := utils.ProtoMessageToString(alert) + if err != nil { + t.Fatal(err) + } + for _, tc := range []struct{ field, want string }{ + {"adversary.ip", "203.0.113.7"}, {"target.host", "dc01"}, + {"lastEvent.action", "login"}, {"lastEvent.origin.user", "alice"}, + {"lastEvent.log.eventCode", "4625"}, {"lastEvent.log.isFailure", "true"}, + {"lastEvent.log.accounts.1", "bob"}, + } { + t.Run(tc.field, func(t *testing.T) { + got := alertGroupingValue(*serialized, tc.field) + if !got.Exists() || got.String() != tc.want { + t.Fatalf("got %v, want %s", got, tc.want) + } + }) + } + for _, field := range []string{"lastEvent.log.missing", "origin.ip"} { + if alertGroupingValue(*serialized, field).Exists() { + t.Errorf("unexpected value for %s", field) + } + } +} +func TestAlertGroupingValueWithoutEvents(t *testing.T) { + for _, input := range []string{`{}`, `{"events":[]}`, `{"events":[null]}`} { + if alertGroupingValue(input, "lastEvent.origin.ip").Exists() { + t.Errorf("unexpected lastEvent for %s", input) + } + } +} + +func TestScalarGroupingValue(t *testing.T) { + for _, tc := range []struct { + input string + valid bool + }{ + {`"alice"`, true}, {`0`, true}, {`false`, true}, {`true`, true}, + {`null`, false}, {`[]`, false}, {`["alice"]`, false}, {`{"user":"alice"}`, false}, + } { + if _, ok := scalarGroupingValue(gjson.Parse(tc.input)); ok != tc.valid { + t.Errorf("scalarGroupingValue(%s) = %v, want %v", tc.input, ok, tc.valid) + } + } +} diff --git a/plugins/alerts/main.go b/plugins/alerts/main.go index 064926285..49cfff420 100644 --- a/plugins/alerts/main.go +++ b/plugins/alerts/main.go @@ -11,7 +11,6 @@ import ( sdkos "github.com/threatwinds/go-sdk/os" "github.com/threatwinds/go-sdk/plugins" "github.com/threatwinds/go-sdk/utils" - "github.com/tidwall/gjson" "google.golang.org/protobuf/types/known/emptypb" ) @@ -132,8 +131,8 @@ func isDuplicate(alert *plugins.Alert) bool { for _, d := range alert.DeduplicateBy { d = strings.TrimSuffix(d, ".keyword") - value := gjson.Get(*alertString, d) - if value.Type == gjson.Null { + value, ok := scalarGroupingValue(alertGroupingValue(*alertString, d)) + if !ok { continue } @@ -147,13 +146,7 @@ func isDuplicate(alert *plugins.Alert) bool { return "" }) - if value.Type == gjson.String { - bb.FilterTerm(searchField, value.String()) - } else if value.Type == gjson.Number { - bb.FilterTerm(searchField, value.Float()) - } else if value.IsBool() { - bb.FilterTerm(searchField, value.Bool()) - } + bb.FilterTerm(searchField, value) } if !execute { @@ -228,8 +221,8 @@ func getPreviousAlertId(alert *plugins.Alert) *string { for _, d := range alert.GroupBy { d = strings.TrimSuffix(d, ".keyword") - value := gjson.Get(*alertString, d) - if value.Type == gjson.Null { + value, ok := scalarGroupingValue(alertGroupingValue(*alertString, d)) + if !ok { continue } @@ -243,13 +236,7 @@ func getPreviousAlertId(alert *plugins.Alert) *string { return "" }) - if value.Type == gjson.String { - bb.FilterTerm(searchField, value.String()) - } else if value.Type == gjson.Number { - bb.FilterTerm(searchField, value.Float()) - } else if value.IsBool() { - bb.FilterTerm(searchField, value.Bool()) - } + bb.FilterTerm(searchField, value) } if !execute { diff --git a/plugins/alerts/testdata/filter-normalization.json b/plugins/alerts/testdata/filter-normalization.json new file mode 100644 index 000000000..261b90718 --- /dev/null +++ b/plugins/alerts/testdata/filter-normalization.json @@ -0,0 +1,1964 @@ +[ + { + "name": "cisco/asa.yml invalid IPsec 402114", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 402114 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/asa.yml invalid IPsec 402115", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 402115 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/asa.yml invalid IPsec 402116", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 402116 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/asa.yml invalid IPsec 402117", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 402117 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/asa.yml invalid IPsec 402118", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 402118 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/asa.yml invalid IPsec 402119", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 402119 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/asa.yml invalid IPsec 402120", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 402120 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/asa.yml scan outcome unknown", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 733101 + } + }, + "expected": {}, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "cisco/asa.yml explicit shun", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 733102 + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/asa.yml ACL Permitted", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 106102, + "ciscoResult": "Permitted" + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/asa.yml ACL Denied", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 106102, + "ciscoResult": "Denied" + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/asa.yml ACL unknown", + "filter": "cisco/asa.yml", + "input": { + "log": { + "messageId": 106102, + "ciscoResult": "unknown" + } + }, + "expected": {}, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "cisco/firepower.yml invalid IPsec 402114", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 402114 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/firepower.yml invalid IPsec 402115", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 402115 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/firepower.yml invalid IPsec 402116", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 402116 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/firepower.yml invalid IPsec 402117", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 402117 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/firepower.yml invalid IPsec 402118", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 402118 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/firepower.yml invalid IPsec 402119", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 402119 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/firepower.yml invalid IPsec 402120", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 402120 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/firepower.yml scan outcome unknown", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 733101 + } + }, + "expected": {}, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "cisco/firepower.yml explicit shun", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 733102 + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/firepower.yml ACL Permitted", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 106102, + "ciscoResult": "Permitted" + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/firepower.yml ACL Denied", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 106102, + "ciscoResult": "Denied" + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "cisco/firepower.yml ACL unknown", + "filter": "cisco/firepower.yml", + "input": { + "log": { + "messageId": 106102, + "ciscoResult": "unknown" + } + }, + "expected": {}, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "FortiGate \"accept\"", + "filter": "fortinet/fortinet.yml", + "input": { + "log": { + "action": "\"accept\"" + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "FortiGate \"deny\"", + "filter": "fortinet/fortinet.yml", + "input": { + "log": { + "action": "\"deny\"" + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "FortiGate ip-conn", + "filter": "fortinet/fortinet.yml", + "input": { + "log": { + "action": "ip-conn" + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "FortiGate client-rst", + "filter": "fortinet/fortinet.yml", + "input": { + "log": { + "action": "client-rst" + } + }, + "expected": {}, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "FortiGate timeout", + "filter": "fortinet/fortinet.yml", + "input": { + "log": { + "action": "timeout" + } + }, + "expected": {}, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "FortiGate profile deny overrides policy accept", + "filter": "fortinet/fortinet.yml", + "input": { + "log": { + "action": "\"accept\"", + "utmaction": "block" + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "pfSense pass", + "filter": "pfsense/pfsense_fw.yml", + "input": { + "log": { + "action": "pass" + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "pfSense block", + "filter": "pfsense/pfsense_fw.yml", + "input": { + "log": { + "action": "block" + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "Sophos HTTP 200", + "filter": "sophos/sophos_xg_firewall.yml", + "input": { + "log": { + "statuscode": 200 + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "Sophos HTTP 302", + "filter": "sophos/sophos_xg_firewall.yml", + "input": { + "log": { + "statuscode": 302 + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "Sophos HTTP 403", + "filter": "sophos/sophos_xg_firewall.yml", + "input": { + "log": { + "statuscode": 403 + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "Sophos HTTP 500", + "filter": "sophos/sophos_xg_firewall.yml", + "input": { + "log": { + "statuscode": 500 + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "Sophos denied block-page HTTP 200", + "filter": "sophos/sophos_xg_firewall.yml", + "input": { + "log": { + "statuscode": 200, + "subType": "Denied" + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "Palo Alto explicit deny overrides status success", + "filter": "paloalto/pa_firewall.yml", + "input": { + "log": { + "act": "deny", + "status": "success" + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "Palo Alto allow", + "filter": "paloalto/pa_firewall.yml", + "input": { + "log": { + "act": "allow" + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "Palo Alto pending administrative operation", + "filter": "paloalto/pa_firewall.yml", + "input": { + "log": { + "result": "Submitted" + } + }, + "expected": {}, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "Palo Alto source byte and packet counters", + "filter": "paloalto/pa_firewall.yml", + "input": { + "log": { + "bytessent": "1024", + "bytesreceived": "2048", + "pktssent": "3", + "pktsreceived": "4" + } + }, + "expected": { + "origin.bytesSent": 1024, + "origin.bytesReceived": 2048, + "origin.packagesSent": "3", + "origin.packagesReceived": "4" + }, + "absent": [ + "target.bytesSent" + ], + "rules": {} + }, + { + "name": "Suricata priority 1", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "alert": { + "severity": 1 + } + } + }, + "expected": { + "severity": "critical" + }, + "absent": [], + "rules": {} + }, + { + "name": "Suricata priority 2", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "alert": { + "severity": 2 + } + } + }, + "expected": { + "severity": "warning" + }, + "absent": [], + "rules": {} + }, + { + "name": "Suricata priority 3", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "alert": { + "severity": 3 + } + } + }, + "expected": { + "severity": "info" + }, + "absent": [], + "rules": {} + }, + { + "name": "Suricata final verdict wins", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "alert": { + "action": "allowed" + }, + "verdict": { + "action": "drop" + } + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "Suricata IDS allowed is not connection success", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "alert": { + "action": "allowed" + } + } + }, + "expected": {}, + "absent": [ + "actionResult", + "action" + ], + "rules": {} + }, + { + "name": "Suricata explicit IPS pass", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "verdict": { + "action": "pass" + } + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "Suricata confirmed established flow", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "eventtype": "flow", + "flow": { + "state": "established", + "bytestoserver": 400, + "bytestoclient": 500 + } + } + }, + "expected": { + "actionResult": "success", + "connectionStatus": "established" + }, + "absent": [], + "rules": {} + }, + { + "name": "Suricata denied established flow", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "verdict": { + "action": "drop" + }, + "flow": { + "state": "established", + "bytestoserver": 400, + "bytestoclient": 500 + } + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [ + "connectionStatus" + ], + "rules": {} + }, + { + "name": "ESET event name is not an outcome", + "filter": "antivirus/esmc-eset.yml", + "input": { + "log": { + "event": "HIPS_Event" + } + }, + "expected": { + "log.event": "HIPS_Event" + }, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "ESET actual blocked result", + "filter": "antivirus/esmc-eset.yml", + "input": { + "log": { + "event": "HIPS_Event", + "result": "Blocked" + } + }, + "expected": { + "actionResult": "denied", + "log.event": "HIPS_Event" + }, + "absent": [], + "rules": {} + }, + { + "name": "Linux negative syscall return preserved", + "filter": "linux/linux.yml", + "input": { + "log": { + "type": "auditd", + "exit": -13, + "exe": "/usr/bin/cat", + "comm": "cat", + "cwd": "/home/user" + } + }, + "expected": { + "log.exitCode": -13, + "actionResult": "failure", + "origin.path": "/usr/bin/cat", + "origin.process": "cat", + "log.workingDirectory": "/home/user" + }, + "absent": [ + "statusCode" + ], + "rules": {} + }, + { + "name": "Linux textual job failure", + "filter": "linux/linux.yml", + "input": { + "log": { + "JOBRESULT": "failed" + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "Linux unknown job result", + "filter": "linux/linux.yml", + "input": { + "log": { + "JOBRESULT": "dependency" + } + }, + "expected": { + "log.vendorActionResult": "dependency" + }, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "Windows account operation 4725", + "filter": "windows/windows-events.yml", + "input": { + "log": { + "eventCode": 4725 + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "Windows account operation 4726", + "filter": "windows/windows-events.yml", + "input": { + "log": { + "eventCode": 4726 + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "Windows account operation 4740", + "filter": "windows/windows-events.yml", + "input": { + "log": { + "eventCode": 4740 + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "Windows NTLM status 0", + "filter": "windows/windows-events.yml", + "input": { + "log": { + "eventCode": 4776, + "data": { + "Status": "0" + } + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "Windows NTLM status 0x0", + "filter": "windows/windows-events.yml", + "input": { + "log": { + "eventCode": 4776, + "data": { + "Status": "0x0" + } + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "Windows NTLM status 0x00000000", + "filter": "windows/windows-events.yml", + "input": { + "log": { + "eventCode": 4776, + "data": { + "Status": "0x00000000" + } + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "Windows NTLM status 0xC000006A", + "filter": "windows/windows-events.yml", + "input": { + "log": { + "eventCode": 4776, + "data": { + "Status": "0xC000006A" + } + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "Windows missing IP placeholder", + "filter": "windows/windows-events.yml", + "input": { + "log": { + "data": { + "IpAddress": "-", + "TargetUserName": "-" + } + } + }, + "expected": {}, + "absent": [ + "origin.ip", + "target.user", + "origin.user" + ], + "rules": {} + }, + { + "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": "CrowdStrike remote/local split", + "filter": "crowdstrike/crowdstrike.yml", + "input": { + "log": { + "event": { + "SourceIp": "198.51.100.10", + "LocalIP": "10.0.0.8" + } + } + }, + "expected": { + "origin.ip": "198.51.100.10", + "log.eventLocalIP": "10.0.0.8" + }, + "absent": [ + "target.ip" + ], + "rules": {} + }, + { + "name": "O365 result Failed", + "filter": "office365/o365.yml", + "input": { + "log": { + "ResultStatus": "Failed" + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "O365 result Blocked", + "filter": "office365/o365.yml", + "input": { + "log": { + "ResultStatus": "Blocked" + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "O365 result Succeeded", + "filter": "office365/o365.yml", + "input": { + "log": { + "ResultStatus": "Succeeded" + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "O365 result PartiallySucceeded", + "filter": "office365/o365.yml", + "input": { + "log": { + "ResultStatus": "PartiallySucceeded" + } + }, + "expected": {}, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "O365 failed operation wins over audit Success", + "filter": "office365/o365.yml", + "input": { + "log": { + "ResultStatus": "Success", + "Operation": "UserLoginFailed", + "Workload": "AzureActiveDirectory", + "ClientIP": "198.51.100.10" + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": { + "rules/office365/credential_access_microsoft_365_potential_password_spraying_attack.yml": true + } + }, + { + "name": "Azure actual result Denied", + "filter": "azure/azure-eventhub.yml", + "input": { + "log": { + "properties": { + "result": "Denied" + } + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": {} + }, + { + "name": "Azure actual result Failed", + "filter": "azure/azure-eventhub.yml", + "input": { + "log": { + "properties": { + "result": "Failed" + } + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": {} + }, + { + "name": "Azure actual result Succeeded", + "filter": "azure/azure-eventhub.yml", + "input": { + "log": { + "properties": { + "result": "Succeeded" + } + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [], + "rules": {} + }, + { + "name": "Azure actual result Running", + "filter": "azure/azure-eventhub.yml", + "input": { + "log": { + "properties": { + "result": "Running" + } + } + }, + "expected": {}, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "Azure storage metadata is not host geography", + "filter": "azure/azure-eventhub.yml", + "input": { + "log": { + "AccountName": "storageaccount", + "Location": "eastus", + "StatusText": "Success", + "ResponseBodySize": 100, + "ResponseHeaderSize": 20 + } + }, + "expected": { + "origin.bytesReceived": 100, + "log.accountName": "storageaccount", + "log.location": "eastus" + }, + "absent": [ + "origin.host", + "origin.geolocation", + "connectionStatus", + "origin.bytesSent" + ], + "rules": {} + }, + { + "name": "GCP numeric protocol", + "filter": "google/gcp.yml", + "input": { + "log": { + "jsonPayload": { + "protocol": 6 + } + } + }, + "expected": { + "protocol": "TCP" + }, + "absent": [], + "rules": {} + }, + { + "name": "GCP unknown numeric protocol preserved", + "filter": "google/gcp.yml", + "input": { + "log": { + "jsonPayload": { + "protocol": 253 + } + } + }, + "expected": { + "log.ipProtocolNumber": 253 + }, + "absent": [ + "protocol" + ], + "rules": {} + }, + { + "name": "AWS principal/source/time promoted", + "filter": "aws/aws.yml", + "input": { + "log": { + "sourceIPAddress": "198.51.100.10", + "eventTime": "2026-09-16T12:00:00Z", + "userIdentity": { + "arn": "arn:aws:iam::123456789012:user/alice" + } + } + }, + "expected": { + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/alice", + "deviceTime": "2026-09-16T12:00:00Z", + "log.userIdentityArn": "arn:aws:iam::123456789012:user/alice" + }, + "absent": [], + "rules": {} + }, + { + "name": "AWS service name is not IP", + "filter": "aws/aws.yml", + "input": { + "log": { + "sourceIPAddress": "cloudtrail.amazonaws.com" + } + }, + "expected": {}, + "absent": [ + "origin.ip" + ], + "rules": {} + }, + { + "name": "Bitdefender phishing aph_blocked", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "actFull": "aph_blocked", + "BitdefenderGZModule": "aph" + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml": false + } + }, + { + "name": "Bitdefender phishing reportOnly", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "actFull": "reportOnly", + "BitdefenderGZModule": "aph" + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml": true + } + }, + { + "name": "Bitdefender attacker priority", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "BitdefenderGZDetectionAttackerIp": "198.51.100.10", + "BitdefenderGZEventSourceIP": "10.0.0.2" + } + }, + "expected": { + "origin.ip": "198.51.100.10" + }, + "absent": [], + "rules": {} + }, + { + "name": "GCP severity EMERGENCY", + "filter": "google/gcp.yml", + "input": { + "log": { + "severity": "EMERGENCY" + } + }, + "expected": { + "severity": "critical" + }, + "absent": [] + }, + { + "name": "GCP severity ALERT", + "filter": "google/gcp.yml", + "input": { + "log": { + "severity": "ALERT" + } + }, + "expected": { + "severity": "critical" + }, + "absent": [] + }, + { + "name": "GCP severity CRITICAL", + "filter": "google/gcp.yml", + "input": { + "log": { + "severity": "CRITICAL" + } + }, + "expected": { + "severity": "critical" + }, + "absent": [] + }, + { + "name": "GCP severity ERROR", + "filter": "google/gcp.yml", + "input": { + "log": { + "severity": "ERROR" + } + }, + "expected": { + "severity": "error" + }, + "absent": [] + }, + { + "name": "GCP severity WARNING", + "filter": "google/gcp.yml", + "input": { + "log": { + "severity": "WARNING" + } + }, + "expected": { + "severity": "warning" + }, + "absent": [] + }, + { + "name": "GCP severity NOTICE", + "filter": "google/gcp.yml", + "input": { + "log": { + "severity": "NOTICE" + } + }, + "expected": { + "severity": "info" + }, + "absent": [] + }, + { + "name": "GCP severity INFO", + "filter": "google/gcp.yml", + "input": { + "log": { + "severity": "INFO" + } + }, + "expected": { + "severity": "info" + }, + "absent": [] + }, + { + "name": "GCP severity DEBUG", + "filter": "google/gcp.yml", + "input": { + "log": { + "severity": "DEBUG" + } + }, + "expected": { + "severity": "debug" + }, + "absent": [] + }, + { + "name": "O365 IPv6 [2001:db8::10]:443", + "filter": "office365/o365.yml", + "input": { + "log": { + "ClientIP": "[2001:db8::10]:443" + } + }, + "expected": { + "origin.ip": "2001:db8::10", + "origin.port": 443 + }, + "absent": [] + }, + { + "name": "O365 IPv6 2001:db8::10", + "filter": "office365/o365.yml", + "input": { + "log": { + "ClientIP": "2001:db8::10" + } + }, + "expected": { + "origin.ip": "2001:db8::10" + }, + "absent": [ + "origin.port" + ] + }, + { + "name": "O365 unknown endpoint retained", + "filter": "office365/o365.yml", + "input": { + "log": { + "ClientIP": "not-an-ip" + } + }, + "expected": { + "log.unparsedOriginIp": "not-an-ip" + }, + "absent": [ + "origin.ip" + ] + }, + { + "name": "O365 port range checked", + "filter": "office365/o365.yml", + "input": { + "log": { + "ClientIP": "203.0.113.10:99999" + } + }, + "expected": { + "origin.ip": "203.0.113.10" + }, + "absent": [ + "origin.port" + ] + }, + { + "name": "Crowdstrike endpoint source fallback", + "filter": "crowdstrike/crowdstrike.yml", + "input": { + "log": { + "event": { + "LocalIP": "10.0.0.10" + } + } + }, + "expected": { + "origin.ip": "10.0.0.10" + }, + "absent": [ + "target.ip" + ] + }, + { + "name": "GCP audit status 0", + "filter": "google/gcp.yml", + "input": { + "log": { + "protoPayload": { + "methodName": "DeleteBucket", + "status": { + "code": 0 + } + } + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [] + }, + { + "name": "GCP audit status 5", + "filter": "google/gcp.yml", + "input": { + "log": { + "protoPayload": { + "methodName": "DeleteBucket", + "status": { + "code": 5 + } + } + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [] + }, + { + "name": "GCP audit status 7", + "filter": "google/gcp.yml", + "input": { + "log": { + "protoPayload": { + "methodName": "DeleteBucket", + "status": { + "code": 7 + } + } + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [] + }, + { + "name": "Azure result Failed", + "filter": "azure/azure-eventhub.yml", + "input": { + "log": { + "resultType": "Failed" + } + }, + "expected": { + "actionResult": "failure" + }, + "absent": [] + }, + { + "name": "Azure result Succeeded", + "filter": "azure/azure-eventhub.yml", + "input": { + "log": { + "resultType": "Succeeded" + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [] + }, + { + "name": "Azure result Denied", + "filter": "azure/azure-eventhub.yml", + "input": { + "log": { + "resultType": "Denied" + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [] + }, + { + "name": "Azure result Started", + "filter": "azure/azure-eventhub.yml", + "input": { + "log": { + "resultType": "Started" + } + }, + "expected": {}, + "absent": [ + "actionResult" + ] + }, + { + "name": "O365 anti-phish policy Success", + "filter": "office365/o365.yml", + "input": { + "log": { + "Operation": "Set-AntiPhishPolicy", + "ResultStatus": "Success" + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/office365/anti_phishing_policy_bypasses.yml": true + } + }, + { + "name": "O365 anti-phish policy Failed", + "filter": "office365/o365.yml", + "input": { + "log": { + "Operation": "Set-AntiPhishPolicy", + "ResultStatus": "Failed" + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/office365/anti_phishing_policy_bypasses.yml": false + } + }, + { + "name": "Suricata priority rule 1", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "alert": { + "severity": 1 + }, + "eventtype": "alert" + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/suricata/high_severity_suricata_alerts_were_detected.yml": true, + "rules/suricata/medium_severity_suricata_alerts_were_detected.yml": false + } + }, + { + "name": "Suricata priority rule 2", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "alert": { + "severity": 2 + }, + "eventtype": "alert" + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/suricata/high_severity_suricata_alerts_were_detected.yml": false, + "rules/suricata/medium_severity_suricata_alerts_were_detected.yml": true + } + }, + { + "name": "Suricata priority rule 3", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "alert": { + "severity": 3 + }, + "eventtype": "alert" + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/suricata/high_severity_suricata_alerts_were_detected.yml": false, + "rules/suricata/medium_severity_suricata_alerts_were_detected.yml": false + } + }, + { + "name": "Suricata NTP ratio 11", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "destport": 123, + "flow": { + "bytestoclient": 1100, + "bytestoserver": 100 + } + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/nids/suricata/ddos_attack_patterns.yml": true + } + }, + { + "name": "Suricata NTP ratio 9", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "destport": 123, + "flow": { + "bytestoclient": 900, + "bytestoserver": 100 + } + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/nids/suricata/ddos_attack_patterns.yml": false + } + }, + { + "name": "Bitdefender CEF priority 0", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "severity": "0", + "BitdefenderGZModule": "network-monitor" + } + }, + "expected": { + "severity": "info", + "log.cefSeverity": "0" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/network_threat_detection.yml": false + } + }, + { + "name": "Bitdefender CEF priority 3", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "severity": "3", + "BitdefenderGZModule": "network-monitor" + } + }, + "expected": { + "severity": "info", + "log.cefSeverity": "3" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/network_threat_detection.yml": false + } + }, + { + "name": "Bitdefender CEF priority 6", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "severity": "6", + "BitdefenderGZModule": "network-monitor" + } + }, + "expected": { + "severity": "warning", + "log.cefSeverity": "6" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/network_threat_detection.yml": false + } + }, + { + "name": "Bitdefender CEF priority 8", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "severity": "8", + "BitdefenderGZModule": "network-monitor" + } + }, + "expected": { + "severity": "error", + "log.cefSeverity": "8" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/network_threat_detection.yml": true + } + }, + { + "name": "Bitdefender CEF priority 10", + "filter": "antivirus/bitdefender_gz.yml", + "input": { + "log": { + "severity": "10", + "BitdefenderGZModule": "network-monitor" + } + }, + "expected": { + "severity": "critical", + "log.cefSeverity": "10" + }, + "absent": [], + "rules": { + "rules/antivirus/bitdefender_gz/network_threat_detection.yml": true + } + }, + { + "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" + ] + }, + { + "name": "Suricata DDoS flow age 4", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "proto": "UDP", + "flow": { + "pktstoserver": 2000, + "age": 4 + } + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/nids/suricata/ddos_attack_patterns.yml": true + } + }, + { + "name": "Suricata DDoS flow age 10", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "proto": "UDP", + "flow": { + "pktstoserver": 2000, + "age": 10 + } + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/nids/suricata/ddos_attack_patterns.yml": false + } + }, + { + "name": "Suricata DDoS missing counters", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "destport": 123 + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/nids/suricata/ddos_attack_patterns.yml": false + } + }, + { + "name": "Suricata flow drop overrides established state", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "flow": { + "action": "drop", + "state": "established", + "bytestoserver": 500, + "bytestoclient": 200 + } + } + }, + "expected": { + "actionResult": "denied" + }, + "absent": [ + "connectionStatus" + ] + }, + { + "name": "Suricata flow explicit pass", + "filter": "suricata/suricata.yml", + "input": { + "log": { + "flow": { + "action": "pass" + } + } + }, + "expected": { + "actionResult": "success" + }, + "absent": [ + "connectionStatus" + ] + }, + { + "name": "O365 successful guessing trigger UserLoggedIn Success", + "filter": "office365/o365.yml", + "input": { + "log": { + "Operation": "UserLoggedIn", + "ResultStatus": "Success", + "Workload": "AzureActiveDirectory", + "UserId": "alice@example.test", + "ClientIP": "203.0.113.10" + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/office365/possible_succesfull_password_guessing_o365.yml": true + } + }, + { + "name": "O365 successful guessing trigger UserLoginFailed Failed", + "filter": "office365/o365.yml", + "input": { + "log": { + "Operation": "UserLoginFailed", + "ResultStatus": "Failed", + "Workload": "AzureActiveDirectory", + "UserId": "alice@example.test", + "ClientIP": "203.0.113.10" + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/office365/possible_succesfull_password_guessing_o365.yml": false + } + }, + { + "name": "O365 successful guessing trigger UserLoggedIn Failed", + "filter": "office365/o365.yml", + "input": { + "log": { + "Operation": "UserLoggedIn", + "ResultStatus": "Failed", + "Workload": "AzureActiveDirectory", + "UserId": "alice@example.test", + "ClientIP": "203.0.113.10" + } + }, + "expected": {}, + "absent": [], + "rules": { + "rules/office365/possible_succesfull_password_guessing_o365.yml": false + } + }, + { + "name": "Azure descriptive denial", + "filter": "azure/azure-eventhub.yml", + "input": { + "log": { + "properties": { + "result": "Access denied" + } + } + }, + "expected": { + "actionResult": "denied" + } + }, + { + "name": "Azure explicit failure overrides success event suffix", + "filter": "azure/azure-eventhub.yml", + "input": { + "log": { + "resultType": "Failed", + "eventType": "Microsoft.ResourceWriteSuccess" + } + }, + "expected": { + "actionResult": "failure" + } + } +] diff --git a/rules/antivirus/bitdefender_gz/apt_detection.yml b/rules/antivirus/bitdefender_gz/apt_detection.yml index 8d91eab8b..5f30c2629 100644 --- a/rules/antivirus/bitdefender_gz/apt_detection.yml +++ b/rules/antivirus/bitdefender_gz/apt_detection.yml @@ -40,7 +40,7 @@ description: | 5. Collect forensic artifacts before remediating - memory image and endpoint logs - since a targeted intrusion warrants attribution work 6. Isolate the endpoint if the detection action shows the threat was not blocked, then hunt for what ran while it was active where: | - greaterOrEqual("severity", 8) && + (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) && ( (equals("log.BitdefenderGZModule", "hd") && regexMatch("log.BitdefenderGZAttackTypes", "(?i)targeted attack")) || diff --git a/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml b/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml index d513c6d45..524628e6b 100644 --- a/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml +++ b/rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml @@ -38,7 +38,7 @@ description: | - Check that signatures were current at deviceTime, using log.BitdefenderGZSignaturesNumber where: | oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - greaterOrEqual("severity", 8) + (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) groupBy: - target.host - target.malware diff --git a/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml b/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml index 0327d9b5d..e26dcc8ce 100644 --- a/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml +++ b/rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml @@ -33,7 +33,7 @@ description: | 7. Keep the incident open until a full day passes with no new host reporting the same malware where: | oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - greaterOrEqual("severity", 8) && + (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) && exists("target.malware") correlation: - indexPattern: v11-log-antivirus-bitdefender-gz-* diff --git a/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml b/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml index f045cae50..4bb04a2a7 100644 --- a/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml +++ b/rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml @@ -35,7 +35,7 @@ description: | 7. Reimage if the same host keeps reappearing in this rule across days where: | oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) && - greaterOrEqual("severity", 8) + (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) correlation: - indexPattern: v11-log-antivirus-bitdefender-gz-* within: 1h diff --git a/rules/antivirus/bitdefender_gz/network_threat_detection.yml b/rules/antivirus/bitdefender_gz/network_threat_detection.yml index b452ab552..7153b51ff 100644 --- a/rules/antivirus/bitdefender_gz/network_threat_detection.yml +++ b/rules/antivirus/bitdefender_gz/network_threat_detection.yml @@ -37,7 +37,7 @@ description: | 6. Block the source at the perimeter, and only then close the alert. A blocked attempt means this attack failed, not that the attacker stopped where: | oneOf("log.BitdefenderGZModule", ["network-monitor", "fw"]) && - greaterOrEqual("severity", 8) + (greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) correlation: - indexPattern: v11-log-antivirus-bitdefender-gz-* within: 2h diff --git a/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml b/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml index 19fe39758..3fa3b1d69 100644 --- a/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml +++ b/rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml @@ -37,7 +37,7 @@ description: | 7. Submit the URL for blocking at the perimeter so the rest of the estate is covered where: | equals("log.BitdefenderGZModule", "aph") && - equals("actionResult", "success") + equals("action", "reportOnly") groupBy: - target.user - adversary.url diff --git a/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml b/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml index d83430101..f5cb7762d 100644 --- a/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml +++ b/rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml @@ -42,7 +42,7 @@ description: | where: | equals("log.eventType", "AntiMalware") && ( - equals("actionResult", "failed") || + oneOf("actionResult", ["failure", "failed"]) || (greaterOrEqual("log.BitdefenderGZPresentMalwareCnt", 1) && equals("log.BitdefenderGZQuarantinedMalwareCnt", 0) && equals("log.BitdefenderGZCleanedMalwareCnt", 0)) diff --git a/rules/antivirus/esmc-eset/exploit_detection_events.yml b/rules/antivirus/esmc-eset/exploit_detection_events.yml index 035889c31..f2e1f9969 100644 --- a/rules/antivirus/esmc-eset/exploit_detection_events.yml +++ b/rules/antivirus/esmc-eset/exploit_detection_events.yml @@ -26,7 +26,7 @@ description: | where: | (contains("log.jsonMessage", "exploit") || oneOf("log.msgType", ["Exploit_Blocked", "Exploit"])) && - equals("actionResult", "blocked") && + oneOf("actionResult", ["denied", "blocked"]) && oneOf("log.severity", ["medium", "high"]) groupBy: - lastEvent.log.jsonMessage 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 diff --git a/rules/cisco/meraki/advanced_malware_protection_alerts.yml b/rules/cisco/meraki/advanced_malware_protection_alerts.yml index d134d9c46..4e5d8e1f9 100644 --- a/rules/cisco/meraki/advanced_malware_protection_alerts.yml +++ b/rules/cisco/meraki/advanced_malware_protection_alerts.yml @@ -34,5 +34,5 @@ where: | contains("log.eventName", "Advanced Malware Protection")) && exists("origin.ip") groupBy: - - adversary.hostname + - adversary.host - adversary.ip diff --git a/rules/cloud/google/gcp_iam_policy_changed.yml b/rules/cloud/google/gcp_iam_policy_changed.yml index 4916be7de..172b5e091 100644 --- a/rules/cloud/google/gcp_iam_policy_changed.yml +++ b/rules/cloud/google/gcp_iam_policy_changed.yml @@ -25,6 +25,6 @@ description: | 5. Review the actor's session for other privilege escalation attempts 6. Check if the service account's workload identity was compromised where: | - oneof("log.protoPayloadServiceName", ["cloudresourcemanager.googleapis.com", "pubsub.googleapis.com"]) && - oneof("log.protoPayloadMethodName", ["SetIamPolicy", "google.iam.v1.IAMPolicy.SetIamPolicy"]) && + oneOf("log.protoPayloadServiceName", ["cloudresourcemanager.googleapis.com", "pubsub.googleapis.com"]) && + oneOf("log.protoPayloadMethodName", ["SetIamPolicy", "google.iam.v1.IAMPolicy.SetIamPolicy"]) && exists("log.protoPayload.request.policy.bindings") && contains("log.logName", "activity") && exists("origin.user") diff --git a/rules/cloud/google/gcp_logging_sink_modified.yml b/rules/cloud/google/gcp_logging_sink_modified.yml index 5c797bd1b..cdcbbb1cc 100644 --- a/rules/cloud/google/gcp_logging_sink_modified.yml +++ b/rules/cloud/google/gcp_logging_sink_modified.yml @@ -26,5 +26,5 @@ description: | 6. Review Cloud Audit logs for other logging configuration changes where: | equals("log.protoPayloadServiceName", "logging.googleapis.com") && - oneof("log.protoPayloadMethodName", ["google.logging.v2.ConfigServiceV2.CreateSink", "google.logging.v2.ConfigServiceV2.DeleteSink", "google.logging.v2.ConfigServiceV2.UpdateSink"]) && + oneOf("log.protoPayloadMethodName", ["google.logging.v2.ConfigServiceV2.CreateSink", "google.logging.v2.ConfigServiceV2.DeleteSink", "google.logging.v2.ConfigServiceV2.UpdateSink"]) && exists("origin.user") diff --git a/rules/crowdstrike/inhibit_system_recovery.yml b/rules/crowdstrike/inhibit_system_recovery.yml index 2031b0c39..b4efb374c 100644 --- a/rules/crowdstrike/inhibit_system_recovery.yml +++ b/rules/crowdstrike/inhibit_system_recovery.yml @@ -16,5 +16,5 @@ where: > exists("log.eventCommandLine") && regexMatch("log.eventCommandLine", "(?i).*(vssadmin.*delete shadows|wmic.*shadowcopy.*delete|bcdedit.*recoveryenabled.*no).*") groupBy: - - origin.host - - origin.user \ No newline at end of file + - adversary.host + - adversary.user \ No newline at end of file diff --git a/rules/crowdstrike/multiple_authentication_failures_(possible_brute_force_attack).yml b/rules/crowdstrike/multiple_authentication_failures_(possible_brute_force_attack).yml index 1c7ab31cf..287b98cca 100644 --- a/rules/crowdstrike/multiple_authentication_failures_(possible_brute_force_attack).yml +++ b/rules/crowdstrike/multiple_authentication_failures_(possible_brute_force_attack).yml @@ -27,4 +27,4 @@ afterEvents: operator: filter_term value: 'false' deduplicateBy: - - origin.ip \ No newline at end of file + - adversary.ip \ No newline at end of file diff --git a/rules/crowdstrike/os_credential_dumping_activity.yml b/rules/crowdstrike/os_credential_dumping_activity.yml index 2a9d35238..77a2bd787 100644 --- a/rules/crowdstrike/os_credential_dumping_activity.yml +++ b/rules/crowdstrike/os_credential_dumping_activity.yml @@ -16,5 +16,5 @@ where: > exists("log.eventCommandLine") && regexMatch("log.eventCommandLine", "(?i).*(procdump.*lsass|mimikatz|sekurlsa|lsass\\.dmp).*") groupBy: - - origin.host - - origin.user \ No newline at end of file + - adversary.host + - adversary.user \ No newline at end of file diff --git a/rules/crowdstrike/security_defenses_impaired_or_policy_disabled.yml b/rules/crowdstrike/security_defenses_impaired_or_policy_disabled.yml index fb727dae2..031d0cef3 100644 --- a/rules/crowdstrike/security_defenses_impaired_or_policy_disabled.yml +++ b/rules/crowdstrike/security_defenses_impaired_or_policy_disabled.yml @@ -15,5 +15,5 @@ where: > equals("log.eventPatternDispositionFlagsPolicyDisabled", true) || oneOf("log.eventPatternDispositionValue", [8192, 8208, 8320, 8704, 9216, 10240, 12304, 73728, 73744]) groupBy: - - origin.host + - adversary.host - lastEvent.log.eventPatternDispositionDescription \ No newline at end of file diff --git a/rules/crowdstrike/suspicious_downloader_execution_linux_macos.yml b/rules/crowdstrike/suspicious_downloader_execution_linux_macos.yml index d3ae34193..7fa3fc904 100644 --- a/rules/crowdstrike/suspicious_downloader_execution_linux_macos.yml +++ b/rules/crowdstrike/suspicious_downloader_execution_linux_macos.yml @@ -17,5 +17,5 @@ where: > exists("log.eventCommandLine") && regexMatch("log.eventCommandLine", "(?i).*(curl|wget).*http.*") groupBy: - - origin.host + - adversary.host - lastEvent.log.eventCommandLine \ No newline at end of file diff --git a/rules/crowdstrike/suspicious_encoded_powershell_execution.yml b/rules/crowdstrike/suspicious_encoded_powershell_execution.yml index b7e043dee..881f657cc 100644 --- a/rules/crowdstrike/suspicious_encoded_powershell_execution.yml +++ b/rules/crowdstrike/suspicious_encoded_powershell_execution.yml @@ -16,5 +16,5 @@ where: > exists("log.eventCommandLine") && regexMatch("log.eventCommandLine", "(?i).*(powershell|pwsh).*-(e|en|enc|encodedcommand|ec)\\s+.*") groupBy: - - origin.host + - adversary.host - lastEvent.log.eventCommandLine \ No newline at end of file diff --git a/rules/crowdstrike/suspicious_native_downloaders.yml b/rules/crowdstrike/suspicious_native_downloaders.yml index 6b05dd819..0b103089e 100644 --- a/rules/crowdstrike/suspicious_native_downloaders.yml +++ b/rules/crowdstrike/suspicious_native_downloaders.yml @@ -16,5 +16,5 @@ where: > exists("log.eventCommandLine") && regexMatch("log.eventCommandLine", "(?i).*(certutil.*-urlcache|bitsadmin.*-transfer|curl.*http|wget.*http).*") groupBy: - - origin.host + - adversary.host - lastEvent.log.eventCommandLine \ No newline at end of file diff --git a/rules/crowdstrike/windows_event_log_clearing.yml b/rules/crowdstrike/windows_event_log_clearing.yml index c5f50d002..b2e479729 100644 --- a/rules/crowdstrike/windows_event_log_clearing.yml +++ b/rules/crowdstrike/windows_event_log_clearing.yml @@ -16,5 +16,5 @@ where: > exists("log.eventCommandLine") && regexMatch("log.eventCommandLine", "(?i).*(wevtutil\\s+cl.*|Clear-EventLog.*|Remove-EventLog.*).*") groupBy: - - origin.host - - origin.user \ No newline at end of file + - adversary.host + - adversary.user \ No newline at end of file diff --git a/rules/fortinet/fortiweb/fortiweb_webshell_upload.yml b/rules/fortinet/fortiweb/fortiweb_webshell_upload.yml index 9313b051d..e8e1a9bcc 100644 --- a/rules/fortinet/fortiweb/fortiweb_webshell_upload.yml +++ b/rules/fortinet/fortiweb/fortiweb_webshell_upload.yml @@ -25,7 +25,7 @@ description: | 6. If a web shell was successfully uploaded, immediately isolate the server where: | oneOf("action", ["deny", "block", "alert_deny", "alert"]) && - (regexMatch("log.msg", "(?i)(web.*shell|backdoor.*upload|cmd.*shell|reverse.*shell|\.php.*upload|\.asp.*upload|\.jsp.*upload|c99|r57|china.*chopper|weevely|b374k)") || + (regexMatch("log.msg", "(?i)(web.*shell|backdoor.*upload|cmd.*shell|reverse.*shell|\\.php.*upload|\\.asp.*upload|\\.jsp.*upload|c99|r57|china.*chopper|weevely|b374k)") || (contains("log.attack_type", "file_upload") && contains("log.msg", ["shell", "backdoor", "malicious"])) || regexMatch("log.msg", "(?i)(file.*upload.*violat|upload.*restrict|dangerous.*file.*type)")) groupBy: diff --git a/rules/json/json-input/json_injection_attempts.yml b/rules/json/json-input/json_injection_attempts.yml index e85aa80eb..5d0ef499d 100644 --- a/rules/json/json-input/json_injection_attempts.yml +++ b/rules/json/json-input/json_injection_attempts.yml @@ -42,4 +42,4 @@ afterEvents: count: 3 groupBy: - adversary.ip - - target.hostname + - target.host diff --git a/rules/linux/attempt_to_disable_syslog_service.yml b/rules/linux/attempt_to_disable_syslog_service.yml index 1d0f00dd2..bda8d50b9 100644 --- a/rules/linux/attempt_to_disable_syslog_service.yml +++ b/rules/linux/attempt_to_disable_syslog_service.yml @@ -17,5 +17,5 @@ references: - "https://attack.mitre.org/techniques/T1562/001/" where: regexMatch("log.message", "((service|chkconfig) (syslog|rsyslog|syslog-ng) (stop|disable|off|kill))|((systemctl) (stop|disable|off|kill) (syslog|rsyslog|syslog-ng))") groupBy: - - origin.ip - - origin.user + - adversary.ip + - adversary.user diff --git a/rules/linux/chattr_immutable_file.yml b/rules/linux/chattr_immutable_file.yml index 3761c72c5..9263bfe4b 100644 --- a/rules/linux/chattr_immutable_file.yml +++ b/rules/linux/chattr_immutable_file.yml @@ -20,5 +20,5 @@ references: - "https://attack.mitre.org/techniques/T1222/" where: regexMatch("log.message", "(chattr (\\+|-)i )") groupBy: - - origin.ip - - origin.user + - adversary.ip + - adversary.user diff --git a/rules/linux/debian_family/auditd_syslog_disabling.yml b/rules/linux/debian_family/auditd_syslog_disabling.yml index b1dc69f1d..c10186901 100644 --- a/rules/linux/debian_family/auditd_syslog_disabling.yml +++ b/rules/linux/debian_family/auditd_syslog_disabling.yml @@ -31,5 +31,5 @@ where: | contains("log.message", "syslog")) && !(contains("log.message", "restart") || contains("log.message", "reload")) groupBy: - - origin.host - - origin.user + - adversary.host + - adversary.user diff --git a/rules/linux/debian_family/container_escape_techniques.yml b/rules/linux/debian_family/container_escape_techniques.yml index 9f775b66d..86c6649c7 100644 --- a/rules/linux/debian_family/container_escape_techniques.yml +++ b/rules/linux/debian_family/container_escape_techniques.yml @@ -34,5 +34,5 @@ where: | (contains("log.message", "mount") && contains("log.message", "/dev/") && (contains("log.message", "container") || contains("log.message", "docker"))) groupBy: - - origin.host - - origin.user + - adversary.host + - adversary.user diff --git a/rules/linux/debian_family/debian_kernel_exploits.yml b/rules/linux/debian_family/debian_kernel_exploits.yml index 4d79af1ea..19522d6f8 100644 --- a/rules/linux/debian_family/debian_kernel_exploits.yml +++ b/rules/linux/debian_family/debian_kernel_exploits.yml @@ -45,5 +45,5 @@ where: | contains("log.message", "Return-oriented programming") || contains("log.message", "ROP chain")) groupBy: - - origin.host - - origin.user + - adversary.host + - adversary.user diff --git a/rules/linux/debian_family/debian_specific_rootkits.yml b/rules/linux/debian_family/debian_specific_rootkits.yml index bf79c5d41..a58a77291 100644 --- a/rules/linux/debian_family/debian_specific_rootkits.yml +++ b/rules/linux/debian_family/debian_specific_rootkits.yml @@ -27,6 +27,6 @@ description: | 7. Analyze network connections for command and control communications 8. Update security tools and perform full system scan where: | - regexMatch("origin.process", "/(reptile|bdvl|azazel|jynx|xorddos)") || (contains("origin.command", "insmod") && regexMatch("origin.command", "(rootkit|hide|backdoor)")) || (contains("origin.command", "ld.so.preload") && regexMatch("origin.command", "(>>|tee|echo)")) + (regexMatch("origin.path", "/(reptile|bdvl|azazel|jynx|xorddos)") || regexMatch("origin.process", "/(reptile|bdvl|azazel|jynx|xorddos)")) || (contains("origin.command", "insmod") && regexMatch("origin.command", "(rootkit|hide|backdoor)")) || (contains("origin.command", "ld.so.preload") && regexMatch("origin.command", "(>>|tee|echo)")) deduplicateBy: - dataSource diff --git a/rules/linux/debian_family/etc_shadow_access.yml b/rules/linux/debian_family/etc_shadow_access.yml index 8edfd744c..841e7fdc5 100644 --- a/rules/linux/debian_family/etc_shadow_access.yml +++ b/rules/linux/debian_family/etc_shadow_access.yml @@ -32,5 +32,5 @@ where: | contains("log.process", "usermod") || contains("log.process", "groupadd") || contains("log.process", "chpasswd") || contains("log.process", "login")) groupBy: - - origin.host - - origin.user + - adversary.host + - adversary.user diff --git a/rules/linux/debian_family/kernel_exploit_indicators.yml b/rules/linux/debian_family/kernel_exploit_indicators.yml index d56aec149..2501952c2 100644 --- a/rules/linux/debian_family/kernel_exploit_indicators.yml +++ b/rules/linux/debian_family/kernel_exploit_indicators.yml @@ -32,5 +32,5 @@ where: | contains("log.message", "copy_from_user"))) || (contains("log.message", "SPLICE_F_MOVE") && contains("log.message", "pipe_buf_operations")) groupBy: - - origin.host - - origin.user + - adversary.host + - adversary.user diff --git a/rules/linux/debian_family/suid_sgid_binary_creation.yml b/rules/linux/debian_family/suid_sgid_binary_creation.yml index 89058a645..c5fd1921d 100644 --- a/rules/linux/debian_family/suid_sgid_binary_creation.yml +++ b/rules/linux/debian_family/suid_sgid_binary_creation.yml @@ -29,5 +29,5 @@ where: | contains("log.message", "2755") || contains("log.message", "6755") || contains("log.message", "u+s") || contains("log.message", "g+s"))) groupBy: - - origin.host - - origin.user + - adversary.host + - adversary.user diff --git a/rules/linux/debian_family/suspicious_binary_in_tmp.yml b/rules/linux/debian_family/suspicious_binary_in_tmp.yml index f7a2cf478..50f2e6468 100644 --- a/rules/linux/debian_family/suspicious_binary_in_tmp.yml +++ b/rules/linux/debian_family/suspicious_binary_in_tmp.yml @@ -24,6 +24,6 @@ description: | 6. Remove the binary and investigate the initial access vector 7. Scan the system for additional indicators of compromise where: | - regexMatch("origin.process", "^/(tmp|dev/shm|var/tmp)/") && !regexMatch("origin.process", "(apt|dpkg|yum|dnf|pip)") + (regexMatch("origin.path", "^/(tmp|dev/shm|var/tmp)/") || regexMatch("origin.process", "^/(tmp|dev/shm|var/tmp)/")) && !regexMatch("origin.process", "(apt|dpkg|yum|dnf|pip)") deduplicateBy: - dataSource diff --git a/rules/linux/disable_selinux_attempt.yml b/rules/linux/disable_selinux_attempt.yml index bd78dd7fb..8f199e4e6 100644 --- a/rules/linux/disable_selinux_attempt.yml +++ b/rules/linux/disable_selinux_attempt.yml @@ -17,5 +17,5 @@ references: - "https://attack.mitre.org/techniques/T1562/001/" where: contains("log.message", "setenforce 0") groupBy: - - origin.ip - - origin.user + - adversary.ip + - adversary.user diff --git a/rules/linux/insmod_kernel_module_load.yml b/rules/linux/insmod_kernel_module_load.yml index d44bfd05c..0fc7a9a17 100644 --- a/rules/linux/insmod_kernel_module_load.yml +++ b/rules/linux/insmod_kernel_module_load.yml @@ -18,5 +18,5 @@ references: - "https://attack.mitre.org/techniques/T1547/006/" where: regexMatch("log.message", "(insmod (.+).ko)") groupBy: - - origin.ip - - origin.user + - adversary.ip + - adversary.user diff --git a/rules/linux/linux_hping_activity.yml b/rules/linux/linux_hping_activity.yml index 7f41b429b..c38a8e7a9 100644 --- a/rules/linux/linux_hping_activity.yml +++ b/rules/linux/linux_hping_activity.yml @@ -17,5 +17,5 @@ references: - "https://attack.mitre.org/techniques/T1082/" where: contains("log.message", "hping") deduplicateBy: - - origin.ip - - origin.user + - adversary.ip + - adversary.user diff --git a/rules/linux/linux_nping_activity.yml b/rules/linux/linux_nping_activity.yml index bb34dfe31..5c61bceb1 100644 --- a/rules/linux/linux_nping_activity.yml +++ b/rules/linux/linux_nping_activity.yml @@ -17,5 +17,5 @@ references: - "https://attack.mitre.org/techniques/T1046/" where: contains("log.message", "nping") deduplicateBy: - - origin.ip - - origin.user + - adversary.ip + - adversary.user diff --git a/rules/linux/log_files_deleted.yml b/rules/linux/log_files_deleted.yml index 180f64546..1df0c1333 100644 --- a/rules/linux/log_files_deleted.yml +++ b/rules/linux/log_files_deleted.yml @@ -17,5 +17,5 @@ references: - "https://attack.mitre.org/techniques/T1070/002/" where: regexMatch("log.message", "(/var/run/utmp|/var/log/wtmp|/var/log/btmp|/var/log/lastlog|/var/log/faillog|/var/log/syslog|/var/log/messages|/var/log/secure|/var/log/auth.log|/var/log/boot.log|/var/log/kern.log)") && !contains("log.message", "gzip") && regexMatch("log.message", "(rm |shred -u)") groupBy: - - origin.ip - - origin.user + - adversary.ip + - adversary.user diff --git a/rules/linux/rhel_family/openshift_security_violations.yml b/rules/linux/rhel_family/openshift_security_violations.yml index b07f9493c..091cdc789 100644 --- a/rules/linux/rhel_family/openshift_security_violations.yml +++ b/rules/linux/rhel_family/openshift_security_violations.yml @@ -41,5 +41,5 @@ where: | (oneOf("log.resource", ["secrets", "configmaps", "serviceaccounts"]) && equals("log.verb", "get") && equals("log.response_code", 403)) ) groupBy: - - origin.host - - origin.user + - adversary.host + - adversary.user diff --git a/rules/linux/rhel_family/rhel_kernel_exploits.yml b/rules/linux/rhel_family/rhel_kernel_exploits.yml index fbf2b23b5..8f2dc4767 100644 --- a/rules/linux/rhel_family/rhel_kernel_exploits.yml +++ b/rules/linux/rhel_family/rhel_kernel_exploits.yml @@ -36,5 +36,5 @@ afterEvents: within: 5m count: 3 groupBy: - - origin.host - - origin.ip + - adversary.host + - adversary.ip diff --git a/rules/linux/rhel_family/rhel_specific_malware.yml b/rules/linux/rhel_family/rhel_specific_malware.yml index df43abc87..61d8a2b63 100644 --- a/rules/linux/rhel_family/rhel_specific_malware.yml +++ b/rules/linux/rhel_family/rhel_specific_malware.yml @@ -28,6 +28,6 @@ description: | 9. Review access logs to determine initial compromise method 10. Implement additional monitoring for similar attack patterns where: | - regexMatch("origin.process", "/(reptile|bdvl|kovid|suterusu|rooty|vlany|azazel|jynx|xorddos)") + (regexMatch("origin.path", "/(reptile|bdvl|kovid|suterusu|rooty|vlany|azazel|jynx|xorddos)") || regexMatch("origin.process", "/(reptile|bdvl|kovid|suterusu|rooty|vlany|azazel|jynx|xorddos)")) deduplicateBy: - dataSource diff --git a/rules/linux/rhel_family/rpm_database_tampering.yml b/rules/linux/rhel_family/rpm_database_tampering.yml index ad164a4d9..820f796fe 100644 --- a/rules/linux/rhel_family/rpm_database_tampering.yml +++ b/rules/linux/rhel_family/rpm_database_tampering.yml @@ -53,5 +53,5 @@ where: | ) ) groupBy: - - origin.host + - adversary.host - target.file diff --git a/rules/linux/rhel_family/secure_boot_violations.yml b/rules/linux/rhel_family/secure_boot_violations.yml index cd2cdab5a..914be6ab2 100644 --- a/rules/linux/rhel_family/secure_boot_violations.yml +++ b/rules/linux/rhel_family/secure_boot_violations.yml @@ -35,5 +35,5 @@ where: | (oneOf("log.efi_variable", ["SecureBoot", "SetupMode", "PK", "KEK", "db", "dbx"]) && oneOf("log.action", ["modify", "delete"])) || (contains("log.shim_message", "verification failed") || contains("log.shim_message", "signature invalid")) groupBy: - - origin.host - - origin.user + - adversary.host + - adversary.user diff --git a/rules/linux/rhel_family/selinux_policy_violations.yml b/rules/linux/rhel_family/selinux_policy_violations.yml index c1a566385..7d4e5e3a8 100644 --- a/rules/linux/rhel_family/selinux_policy_violations.yml +++ b/rules/linux/rhel_family/selinux_policy_violations.yml @@ -55,4 +55,4 @@ where: | groupBy: - lastEvent.log.scontext - lastEvent.log.tcontext - - origin.host + - adversary.host diff --git a/rules/linux/rhel_family/yum_dnf_repository_attacks.yml b/rules/linux/rhel_family/yum_dnf_repository_attacks.yml index 1608ce966..5bb65f5de 100644 --- a/rules/linux/rhel_family/yum_dnf_repository_attacks.yml +++ b/rules/linux/rhel_family/yum_dnf_repository_attacks.yml @@ -49,4 +49,4 @@ where: | ) groupBy: - lastEvent.log.comm - - origin.host + - adversary.host diff --git a/rules/linux/tc_bpf_filter.yml b/rules/linux/tc_bpf_filter.yml index f10b8b5a7..30e734243 100644 --- a/rules/linux/tc_bpf_filter.yml +++ b/rules/linux/tc_bpf_filter.yml @@ -19,5 +19,5 @@ references: - "https://attack.mitre.org/techniques/T1059/004/" where: containsAll("log.message", ["tc", "filter", "bpf"]) groupBy: - - origin.ip - - origin.user + - adversary.ip + - adversary.user diff --git a/rules/macos/endpoint_security_bypass.yml b/rules/macos/endpoint_security_bypass.yml index 29203fcfd..6c43a37ce 100644 --- a/rules/macos/endpoint_security_bypass.yml +++ b/rules/macos/endpoint_security_bypass.yml @@ -38,11 +38,11 @@ where: | afterEvents: - indexPattern: v11-log-macos-* with: - - field: system.hostname + - field: origin.host operator: filter_term - value: '{{.system.hostname}}' + value: '{{.origin.host}}' within: 15m count: 2 groupBy: - lastEvent.log.process - - lastEvent.system.hostname + - adversary.host diff --git a/rules/mikrotik/mikrotik_fw/ssh_brute_force_attempts.yml b/rules/mikrotik/mikrotik_fw/ssh_brute_force_attempts.yml index 28eb4ac9a..43eca7e4d 100644 --- a/rules/mikrotik/mikrotik_fw/ssh_brute_force_attempts.yml +++ b/rules/mikrotik/mikrotik_fw/ssh_brute_force_attempts.yml @@ -24,9 +24,9 @@ description: | 5. Ensure SSH access is restricted to authorized management networks only 6. Review MikroTik firewall rules and consider disabling SSH access from untrusted networks where: | - equals("target.port", 22) && equals("protocol", "tcp") && exists("origin.ip") && + equals("target.port", 22) && equalsIgnoreCase("protocol", "tcp") && exists("origin.ip") && (contains("log.chain", "input") || contains("log.action", "drop") - || (contains("log.topics", "system") && contains("log.kvMessage", "ssh"))) + || (contains("log.topics", "system") && contains("raw", "ssh"))) afterEvents: - indexPattern: v11-log-firewall-mikrotik-* with: @@ -38,7 +38,7 @@ afterEvents: value: '22' - field: protocol operator: filter_term - value: 'tcp' + value: '{{.protocol}}' within: 15m count: 10 groupBy: diff --git a/rules/netflow/tor_usage_detection.yml b/rules/netflow/tor_usage_detection.yml index 1c084ad92..41db93b36 100644 --- a/rules/netflow/tor_usage_detection.yml +++ b/rules/netflow/tor_usage_detection.yml @@ -27,7 +27,7 @@ description: | where: | exists("origin.ip") && exists("target.ip") && oneOf("target.port", [9001, 9030, 9050, 9051]) && - equals("protocol", "6") && greaterThan("log.bytes", 512) + oneOf("protocol", ["TCP", "tcp", "6"]) && greaterThan("log.bytes", 512) afterEvents: - indexPattern: v11-log-netflow-* with: diff --git a/rules/nids/suricata/ddos_attack_patterns.yml b/rules/nids/suricata/ddos_attack_patterns.yml index 05612ee99..7d478446e 100644 --- a/rules/nids/suricata/ddos_attack_patterns.yml +++ b/rules/nids/suricata/ddos_attack_patterns.yml @@ -32,17 +32,17 @@ where: | contains("log.alert.signature", "DDoS") || contains("log.alert.signature", "amplification"))) || (equals("protocol", "TCP") && - equals("log.tcp.flags", "S") && - greaterThan("log.flow.pkts_toserver", 100) && - lessThan("log.flow.duration", 5)) || + (equals("log.tcp.flags", "S") || (equals("log.tcp.syn", true) && !equals("log.tcp.ack", true))) && + (greaterThan("log.flow.pktstoserver", 100) || greaterThan("log.flow.pkts_toserver", 100)) && + (lessThan("log.flow.age", 5) || (!exists("log.flow.age") && lessThan("log.flow.duration", 5)))) || (equals("protocol", "UDP") && - greaterThan("log.flow.pkts_toserver", 1000) && - lessThan("log.flow.duration", 10)) || + (greaterThan("log.flow.pktstoserver", 1000) || greaterThan("log.flow.pkts_toserver", 1000)) && + (lessThan("log.flow.age", 10) || (!exists("log.flow.age") && lessThan("log.flow.duration", 10)))) || (equals("protocol", "ICMP") && - greaterThan("log.flow.pkts_toserver", 500) && - lessThan("log.flow.duration", 5)) || + (greaterThan("log.flow.pktstoserver", 500) || greaterThan("log.flow.pkts_toserver", 500)) && + (lessThan("log.flow.age", 5) || (!exists("log.flow.age") && lessThan("log.flow.duration", 5)))) || (equals("target.port", 123) && - safe("log.flow.bytes_toclient", 0) > safe("log.flow.bytes_toserver", 0) * 10) + safe("log.flow.bytestoclient", safe("log.flow.bytes_toclient", 0.0)) > safe("log.flow.bytestoserver", safe("log.flow.bytes_toserver", 0.0)) * 10.0) ) afterEvents: - indexPattern: v11-log-suricata-* diff --git a/rules/office365/anti_phishing_policy_bypasses.yml b/rules/office365/anti_phishing_policy_bypasses.yml index f0d808347..d3fc21106 100644 --- a/rules/office365/anti_phishing_policy_bypasses.yml +++ b/rules/office365/anti_phishing_policy_bypasses.yml @@ -25,19 +25,19 @@ description: | 6. Consider rolling back unauthorized changes and implementing additional approval workflows 7. Monitor for any unusual email activity following the policy modifications where: | - oneOf("log.Operation", ["Set-AntiPhishPolicy", "Remove-AntiPhishPolicy", "New-AntiPhishPolicy", "Disable-AntiPhishRule"]) && - equals("log.ResultStatus", "Success") + oneOf("action", ["Set-AntiPhishPolicy", "Remove-AntiPhishPolicy", "New-AntiPhishPolicy", "Disable-AntiPhishRule"]) && + equals("actionResult", "success") afterEvents: - indexPattern: v11-log-o365-* with: - field: origin.user operator: filter_term value: '{{.origin.user}}' - - field: log.Operation + - field: action operator: filter_match value: 'AntiPhish' within: 4h count: 2 groupBy: - - lastEvent.log.Operation + - lastEvent.action - adversary.user diff --git a/rules/office365/credential_access_microsoft_365_potential_password_spraying_attack.yml b/rules/office365/credential_access_microsoft_365_potential_password_spraying_attack.yml index db5985922..907877deb 100644 --- a/rules/office365/credential_access_microsoft_365_potential_password_spraying_attack.yml +++ b/rules/office365/credential_access_microsoft_365_potential_password_spraying_attack.yml @@ -16,13 +16,19 @@ references: description: "Credential Access consists of techniques for stealing credentials like account names and passwords. Techniques used to get credentials include keylogging or credential dumping. Using legitimate credentials can give adversaries access to systems, make them harder to detect, and provide the opportunity to create more accounts to help achieve their goals.
Identifies a high number (25) of failed Microsoft 365 user authentication attempts from a single IP address within 30 minutes, which could be indicative of a password spraying attack. An adversary may attempt a password spraying attack to obtain unauthorized access to user accounts." where: | - oneOf("log.Workload", ["Exchange", "AzureActiveDirectory"]) && oneOf("action", ["UserLoginFailed", "PasswordLogonInitialAuthUsingPassword"]) && equals("actionResult", "failed") && exists("origin.ip") + oneOf("log.Workload", ["Exchange", "AzureActiveDirectory"]) && oneOf("action", ["UserLoginFailed", "PasswordLogonInitialAuthUsingPassword"]) && oneOf("actionResult", ["failure", "failed"]) && exists("origin.ip") afterEvents: - indexPattern: v11-log-o365-* with: - field: origin.ip operator: filter_term value: '{{.origin.ip}}' + - field: action + operator: filter_term + value: '{{.action}}' + - field: actionResult + operator: filter_term + value: '{{.actionResult}}' within: 60s count: 5 groupBy: diff --git a/rules/office365/dlp_policy_violations.yml b/rules/office365/dlp_policy_violations.yml index c6ec30033..e506dc8a3 100644 --- a/rules/office365/dlp_policy_violations.yml +++ b/rules/office365/dlp_policy_violations.yml @@ -41,7 +41,7 @@ where: | equals("log.Workload", "Teams") || equals("log.Workload", "SecurityComplianceCenter") ) && - !equals("actionResult", "failed") + !oneOf("actionResult", ["failure", "failed"]) groupBy: - lastEvent.log.PolicyId - lastEvent.log.SensitiveInfoTypeData diff --git a/rules/office365/information_barriers_violations.yml b/rules/office365/information_barriers_violations.yml index ef3b66437..b49e7c080 100644 --- a/rules/office365/information_barriers_violations.yml +++ b/rules/office365/information_barriers_violations.yml @@ -24,7 +24,7 @@ description: | 5. Provide additional training to users if violations appear to be due to lack of awareness 6. Consider implementing additional technical controls to prevent future violations where: | - equals("action", "InformationBarrierPolicyViolation") || (equals("log.PolicyType", "InformationBarrier") && equals("actionResult", "blocked")) || (equals("log.ViolationType", "InformationBarrier") && equals("action", "CommunicationBlocked")) + equals("action", "InformationBarrierPolicyViolation") || (equals("log.PolicyType", "InformationBarrier") && oneOf("actionResult", ["denied", "blocked"])) || (equals("log.ViolationType", "InformationBarrier") && equals("action", "CommunicationBlocked")) afterEvents: - indexPattern: v11-log-o365-* with: diff --git a/rules/office365/possible_succesfull_password_guessing_o365.yml b/rules/office365/possible_succesfull_password_guessing_o365.yml index bc9bd6fd8..af391169b 100644 --- a/rules/office365/possible_succesfull_password_guessing_o365.yml +++ b/rules/office365/possible_succesfull_password_guessing_o365.yml @@ -21,7 +21,7 @@ references: - "https://attack.mitre.org/tactics/TA0006" - "https://attack.mitre.org/techniques/T1110/001/" where: | - oneOf("log.Workload", ["Exchange","AzureActiveDirectory"]) && equals("action", "UserLoginFailed") && equals("actionResult", "failed") && exists("origin.user") && exists("log.clientIP") + oneOf("log.Workload", ["Exchange","AzureActiveDirectory"]) && equals("action", "UserLoggedIn") && equals("actionResult", "success") && exists("origin.user") && exists("origin.ip") afterEvents: - indexPattern: v11-log-o365-* with: @@ -31,9 +31,9 @@ afterEvents: - field: origin.user operator: filter_term value: "{{.origin.user}}" - - field: log.clientIP + - field: origin.ip operator: filter_term - value: "{{.log.clientIP}}" + value: "{{.origin.ip}}" within: 1m count: 10 groupBy: diff --git a/rules/office365/safe_links_click_patterns.yml b/rules/office365/safe_links_click_patterns.yml index 5ee89a314..a7adc641f 100644 --- a/rules/office365/safe_links_click_patterns.yml +++ b/rules/office365/safe_links_click_patterns.yml @@ -24,7 +24,7 @@ description: | 5. Implement additional security awareness training for the affected user 6. Consider blocking the malicious domains at the network level where: | - equals("action", "ClickedSafeLink") && equals("actionResult", "blocked") && exists("origin.user") + equals("action", "ClickedSafeLink") && oneOf("actionResult", ["denied", "blocked"]) && exists("origin.user") afterEvents: - indexPattern: v11-log-o365-* with: diff --git a/rules/suricata/high_severity_suricata_alerts_were_detected.yml b/rules/suricata/high_severity_suricata_alerts_were_detected.yml index 66cdfc139..ee0dc25b1 100644 --- a/rules/suricata/high_severity_suricata_alerts_were_detected.yml +++ b/rules/suricata/high_severity_suricata_alerts_were_detected.yml @@ -13,7 +13,7 @@ adversary: origin references: - "https://suricata.readthedocs.io/en/latest/" description: "Suricata has detected a high severity alert. This indicates potential malicious activity targeting the network infrastructure, such as exploitation attempts, malware communication, or suspicious network behavior." -where: equals("log.eventType", "alert") && equals("severity", "high") +where: equals("log.eventType", "alert") && equals("log.alert.severity", 1) afterEvents: - indexPattern: v11-log-suricata-* with: diff --git a/rules/suricata/medium_severity_suricata_alerts_were_detected.yml b/rules/suricata/medium_severity_suricata_alerts_were_detected.yml index 277e9c8cf..c87d3ce58 100644 --- a/rules/suricata/medium_severity_suricata_alerts_were_detected.yml +++ b/rules/suricata/medium_severity_suricata_alerts_were_detected.yml @@ -13,7 +13,7 @@ adversary: target description: "Suricata has detected a medium severity alert that may indicate reconnaissance or scanning attempts. This could include port scanning, protocol anomalies, or suspicious network patterns that warrant investigation but may not represent an immediate threat." references: - "https://suricata.readthedocs.io/en/latest/" -where: equals("log.eventType", "alert") && equals("severity", "medium") +where: equals("log.eventType", "alert") && equals("log.alert.severity", 2) afterEvents: - indexPattern: v11-log-suricata-* with: diff --git a/rules/vmware/vmware-esxi/esxi_account_manipulation.yml b/rules/vmware/vmware-esxi/esxi_account_manipulation.yml index 15548cd36..fada1bb8f 100644 --- a/rules/vmware/vmware-esxi/esxi_account_manipulation.yml +++ b/rules/vmware/vmware-esxi/esxi_account_manipulation.yml @@ -40,4 +40,4 @@ where: | )) ) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/esxi_disk_theft.yml b/rules/vmware/vmware-esxi/esxi_disk_theft.yml index 59b3b0ebb..13ba6b8fd 100644 --- a/rules/vmware/vmware-esxi/esxi_disk_theft.yml +++ b/rules/vmware/vmware-esxi/esxi_disk_theft.yml @@ -40,5 +40,5 @@ where: | (contains("log.message", "Datastore") && contains("log.message", "browse") && contains("log.message", ".vmdk")) ) groupBy: - - adversary.hostname + - adversary.host - adversary.ip diff --git a/rules/vmware/vmware-esxi/esxi_firewall_modification.yml b/rules/vmware/vmware-esxi/esxi_firewall_modification.yml index 8e924db50..03685de0d 100644 --- a/rules/vmware/vmware-esxi/esxi_firewall_modification.yml +++ b/rules/vmware/vmware-esxi/esxi_firewall_modification.yml @@ -34,7 +34,7 @@ where: | contains("log.message", "set --allowed-all true") || contains("log.message", "set --enabled") )) || - (contains("log.message", "iptables") && exists("origin.hostname")) + (contains("log.message", "iptables") && exists("origin.host")) ) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/esxi_host_compromise.yml b/rules/vmware/vmware-esxi/esxi_host_compromise.yml index 3216988d9..82a6e4028 100644 --- a/rules/vmware/vmware-esxi/esxi_host_compromise.yml +++ b/rules/vmware/vmware-esxi/esxi_host_compromise.yml @@ -42,6 +42,6 @@ where: | contains("log.message", "log events removed") || (equals("log.process", "vmkernel") && contains("log.message", "SCSI sense")) || (contains("log.eventInfo", "ransom") || contains("log.eventInfo", "encrypt"))) && - exists("origin.hostname") + exists("origin.host") groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/esxi_ransomware_detection.yml b/rules/vmware/vmware-esxi/esxi_ransomware_detection.yml index c3e00ebff..3c50f57a9 100644 --- a/rules/vmware/vmware-esxi/esxi_ransomware_detection.yml +++ b/rules/vmware/vmware-esxi/esxi_ransomware_detection.yml @@ -37,4 +37,4 @@ where: | (contains("log.message", "chmod") && contains("log.message", "+x") && contains("log.message", ".sh")) ) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/esxi_ssh_access.yml b/rules/vmware/vmware-esxi/esxi_ssh_access.yml index 9054812d6..8df96e6ba 100644 --- a/rules/vmware/vmware-esxi/esxi_ssh_access.yml +++ b/rules/vmware/vmware-esxi/esxi_ssh_access.yml @@ -33,5 +33,5 @@ where: | (contains("log.message", "ssh") && contains("log.message", "connection from")) ) groupBy: - - adversary.hostname + - adversary.host - adversary.ip diff --git a/rules/vmware/vmware-esxi/esxi_syslog_disruption.yml b/rules/vmware/vmware-esxi/esxi_syslog_disruption.yml index a112529cd..09e637d95 100644 --- a/rules/vmware/vmware-esxi/esxi_syslog_disruption.yml +++ b/rules/vmware/vmware-esxi/esxi_syslog_disruption.yml @@ -37,4 +37,4 @@ where: | (contains("log.message", "Syslog.global.logHost") && contains("log.message", "changed")) ) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/esxi_vib_sideloading.yml b/rules/vmware/vmware-esxi/esxi_vib_sideloading.yml index bf89be95d..aedb53971 100644 --- a/rules/vmware/vmware-esxi/esxi_vib_sideloading.yml +++ b/rules/vmware/vmware-esxi/esxi_vib_sideloading.yml @@ -33,4 +33,4 @@ where: | (contains("log.message", "esxcli software acceptance") && contains("log.message", "set")) ) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/hypervisor_escape_attempts.yml b/rules/vmware/vmware-esxi/hypervisor_escape_attempts.yml index 179e09f36..9bf3339a1 100644 --- a/rules/vmware/vmware-esxi/hypervisor_escape_attempts.yml +++ b/rules/vmware/vmware-esxi/hypervisor_escape_attempts.yml @@ -33,4 +33,4 @@ where: | (regexMatch("log.message", "(?i)CVE-2024-37085")) || (regexMatch("log.message", "(?i)VMCI") && regexMatch("log.message", "(?i)(backdoor|socket|unauthorized)")) groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/powercli_script_execution.yml b/rules/vmware/vmware-esxi/powercli_script_execution.yml index 7e5c6b222..249c3d665 100644 --- a/rules/vmware/vmware-esxi/powercli_script_execution.yml +++ b/rules/vmware/vmware-esxi/powercli_script_execution.yml @@ -30,4 +30,4 @@ where: | (regexMatch("log.message", "(?i)Syslog\\.global\\.log") && regexMatch("log.message", "(?i)Set-VMHost")) || regexMatch("log.message", "(?i)Get-VMHostSysLogServer") groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/vcenter_server_attacks.yml b/rules/vmware/vmware-esxi/vcenter_server_attacks.yml index c57f7437e..504be103b 100644 --- a/rules/vmware/vmware-esxi/vcenter_server_attacks.yml +++ b/rules/vmware/vmware-esxi/vcenter_server_attacks.yml @@ -38,13 +38,13 @@ where: | contains("log.message", "VIB install") || contains("log.message", "authentication failed") || (equals("log.process", "vpxd") && equals("severity", "error"))) && - exists("origin.hostname") + exists("origin.host") afterEvents: - indexPattern: v11-log-vmware-esxi-* with: - - field: origin.hostname + - field: origin.host operator: filter_term - value: '{{.origin.hostname}}' + value: '{{.origin.host}}' within: 30m count: 5 groupBy: diff --git a/rules/vmware/vmware-esxi/vm_escape_detection.yml b/rules/vmware/vmware-esxi/vm_escape_detection.yml index fc90cbdde..f97254e7e 100644 --- a/rules/vmware/vmware-esxi/vm_escape_detection.yml +++ b/rules/vmware/vmware-esxi/vm_escape_detection.yml @@ -34,4 +34,4 @@ where: | (regexMatch("log.eventInfo", "(?i)(vm.*escape|breakout|containment.*breach)")) groupBy: - lastEvent.log.process - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/vmware_tools_vulnerabilities.yml b/rules/vmware/vmware-esxi/vmware_tools_vulnerabilities.yml index 11428f501..cea3b485e 100644 --- a/rules/vmware/vmware-esxi/vmware_tools_vulnerabilities.yml +++ b/rules/vmware/vmware-esxi/vmware_tools_vulnerabilities.yml @@ -35,10 +35,10 @@ where: | afterEvents: - indexPattern: v11-log-vmware-esxi-* with: - - field: origin.hostname + - field: origin.host operator: filter_term - value: '{{.origin.hostname}}' + value: '{{.origin.host}}' within: 15m count: 5 groupBy: - - adversary.hostname + - adversary.host diff --git a/rules/vmware/vmware-esxi/vsphere_api_abuse.yml b/rules/vmware/vmware-esxi/vsphere_api_abuse.yml index cb25a9c1d..eee01dfb9 100644 --- a/rules/vmware/vmware-esxi/vsphere_api_abuse.yml +++ b/rules/vmware/vmware-esxi/vsphere_api_abuse.yml @@ -43,11 +43,11 @@ where: | afterEvents: - indexPattern: v11-log-vmware-esxi-* with: - - field: origin.hostname + - field: origin.host operator: filter_term - value: '{{.origin.hostname}}' + value: '{{.origin.host}}' within: 5m count: 10 groupBy: - lastEvent.log.eventInfo - - adversary.hostname + - adversary.host diff --git a/rules/windows/adfs_authentication_anomalies.yml b/rules/windows/adfs_authentication_anomalies.yml index e0c052b9d..30daa9103 100644 --- a/rules/windows/adfs_authentication_anomalies.yml +++ b/rules/windows/adfs_authentication_anomalies.yml @@ -34,5 +34,5 @@ afterEvents: within: 10m count: 10 groupBy: - - origin.ip + - adversary.ip - target.user diff --git a/rules/windows/asrep_roasting_detection.yml b/rules/windows/asrep_roasting_detection.yml index 09eecd197..740fbd1b0 100644 --- a/rules/windows/asrep_roasting_detection.yml +++ b/rules/windows/asrep_roasting_detection.yml @@ -43,5 +43,5 @@ afterEvents: within: 15m count: 3 groupBy: - - origin.ip - - origin.host + - adversary.ip + - adversary.host diff --git a/rules/windows/audit_log_was_cleared.yml b/rules/windows/audit_log_was_cleared.yml index b9d2c7a5b..2e6c084e9 100644 --- a/rules/windows/audit_log_was_cleared.yml +++ b/rules/windows/audit_log_was_cleared.yml @@ -14,5 +14,5 @@ references: - "https://attack.mitre.org/techniques/T1070/001/" where: equals("log.eventCode", 1102) groupBy: - - origin.ip + - adversary.ip - target.user diff --git a/rules/windows/bruteforce_attack.yml b/rules/windows/bruteforce_attack.yml index eb664c56c..c38cf3b26 100644 --- a/rules/windows/bruteforce_attack.yml +++ b/rules/windows/bruteforce_attack.yml @@ -32,6 +32,6 @@ afterEvents: within: 5m count: 10 deduplicateBy: - - origin.host + - adversary.host - target.user - - origin.ip + - adversary.ip diff --git a/rules/windows/bruteforce_multiple_logon_failure_followed_by_success.yml b/rules/windows/bruteforce_multiple_logon_failure_followed_by_success.yml index 5bccb7233..ac2bf001a 100644 --- a/rules/windows/bruteforce_multiple_logon_failure_followed_by_success.yml +++ b/rules/windows/bruteforce_multiple_logon_failure_followed_by_success.yml @@ -32,6 +32,6 @@ afterEvents: within: 5m count: 10 deduplicateBy: - - origin.ip + - adversary.ip - target.user - - origin.host + - adversary.host diff --git a/rules/windows/certificate_services_abuse.yml b/rules/windows/certificate_services_abuse.yml index b5e1f4ae3..317a15782 100644 --- a/rules/windows/certificate_services_abuse.yml +++ b/rules/windows/certificate_services_abuse.yml @@ -27,4 +27,4 @@ description: | where: (equals("log.eventCode", "4886") || equals("log.eventCode", "4887")) && equals("log.providerName", "Microsoft-Windows-Security-Auditing") && (contains("log.eventDataSubjectUserName", "$") || equals("log.eventDataSubjectUserName", "ANONYMOUS LOGON")) groupBy: - lastEvent.log.eventDataSubjectUserName - - origin.host + - adversary.host diff --git a/rules/windows/golden_ticket_detection.yml b/rules/windows/golden_ticket_detection.yml index 9df408ff3..60fc7a7df 100644 --- a/rules/windows/golden_ticket_detection.yml +++ b/rules/windows/golden_ticket_detection.yml @@ -60,5 +60,5 @@ afterEvents: within: 30m count: 3 groupBy: - - origin.host + - adversary.host - target.user diff --git a/rules/windows/kerberoasting_detection.yml b/rules/windows/kerberoasting_detection.yml index eb2a0c2ab..db49e85cb 100644 --- a/rules/windows/kerberoasting_detection.yml +++ b/rules/windows/kerberoasting_detection.yml @@ -47,5 +47,5 @@ afterEvents: within: 15m count: 3 groupBy: - - origin.ip + - adversary.ip - target.user diff --git a/rules/windows/lsass_memdump_handle_access.yml b/rules/windows/lsass_memdump_handle_access.yml index e0fca6615..e01d84ef6 100644 --- a/rules/windows/lsass_memdump_handle_access.yml +++ b/rules/windows/lsass_memdump_handle_access.yml @@ -21,5 +21,5 @@ references: where: equals("log.eventCode", 4656) && regexMatch("log.eventDataObjectName", "(:\\Windows\\System32\\lsass.exe|\\Device\\HarddiskVolume[A-Za-z?:\\]([A-Za-z?])?\\Windows\\System32\\lsass.exe)") && !regexMatch("log.eventDataProcessName", "(:\\Program Files\\(.+).exe|:\\Program Files (x86)\\(.+).exe|:\\Windows\\system32\\wbem\\WmiPrvSE.exe|:\\Windows\\System32\\dllhost.exe|:\\Windows\\System32\\svchost.exe|:\\Windows\\System32\\msiexec.exe|:\\ProgramData\\Microsoft\\Windows Defender\\(.+).exe|:\\Windows\\explorer.exe)") && oneOf("log.eventDataAccessMask", ["2097151", "4112", "1040", "1180185", "2031615"]) groupBy: - - origin.ip + - adversary.ip - target.user diff --git a/rules/windows/possible_exploit_over_reverse_tunneling_using_stolen_credentials.yml b/rules/windows/possible_exploit_over_reverse_tunneling_using_stolen_credentials.yml index 4be3d57c1..b28ce6ac1 100644 --- a/rules/windows/possible_exploit_over_reverse_tunneling_using_stolen_credentials.yml +++ b/rules/windows/possible_exploit_over_reverse_tunneling_using_stolen_credentials.yml @@ -14,5 +14,5 @@ references: - "https://attack.mitre.org/techniques/T1021/001/" where: equals("log.eventDataLogonType", "10") && oneOf("origin.ip", ["::1", "127.0.0.1"]) && oneOf("log.eventCode", [528, 540, 673, 4624, 4769]) groupBy: - - origin.ip + - adversary.ip - target.user diff --git a/rules/windows/ransom_multiple_file_deletion.yml b/rules/windows/ransom_multiple_file_deletion.yml index 2073ade06..96131cb11 100644 --- a/rules/windows/ransom_multiple_file_deletion.yml +++ b/rules/windows/ransom_multiple_file_deletion.yml @@ -36,5 +36,5 @@ afterEvents: within: 5m count: 50 groupBy: - - origin.ip + - adversary.ip - target.user diff --git a/rules/windows/sam_database_access.yml b/rules/windows/sam_database_access.yml index 68794dc8f..7d55c4948 100644 --- a/rules/windows/sam_database_access.yml +++ b/rules/windows/sam_database_access.yml @@ -34,5 +34,5 @@ where: | ) && oneOf("log.eventDataAccessMask", ["131097", "2032127", "64", "32", "1"]) groupBy: - - origin.host + - adversary.host - target.user diff --git a/rules/windows/sid_history_injection.yml b/rules/windows/sid_history_injection.yml index bcebcc1f6..b1ed4e622 100644 --- a/rules/windows/sid_history_injection.yml +++ b/rules/windows/sid_history_injection.yml @@ -30,5 +30,5 @@ where: | oneOf("log.eventCode", ["4765", "4766"]) && equals("log.channel", "Security") groupBy: - - origin.host + - adversary.host - target.user diff --git a/rules/windows/silver_ticket_detection.yml b/rules/windows/silver_ticket_detection.yml index 8f4e87eb9..68d28b42a 100644 --- a/rules/windows/silver_ticket_detection.yml +++ b/rules/windows/silver_ticket_detection.yml @@ -47,5 +47,5 @@ afterEvents: within: 15m count: 5 groupBy: - - origin.ip - - origin.host + - adversary.ip + - adversary.host diff --git a/rules/windows/smbv1_usage_detection.yml b/rules/windows/smbv1_usage_detection.yml index 4ebfcf5ff..ebb4547ca 100644 --- a/rules/windows/smbv1_usage_detection.yml +++ b/rules/windows/smbv1_usage_detection.yml @@ -26,5 +26,5 @@ description: | 7. Consider implementing network segmentation to limit exposure if SMBv1 cannot be immediately disabled where: equals("log.eventCode", "3000") && equals("log.providerName", "Microsoft-Windows-SMBServer") && contains("log.message", "SMB1") groupBy: - - origin.host - - origin.ip + - adversary.host + - adversary.ip