Detection rules › Elastic
Newly Observed Palo Alto Network Alert
This rule detects Palo Alto Network alerts that are observed for the first time in the previous 5 days of alert history. Analysts can use this to prioritize triage and response.
Rule body elastic
[metadata]
creation_date = "2026/01/21"
integration = ["panw"]
maturity = "production"
updated_date = "2026/04/10"
[rule]
author = ["Elastic"]
description = """
This rule detects Palo Alto Network alerts that are observed for the first time in the previous 5 days of alert history.
Analysts can use this to prioritize triage and response.
"""
from = "now-7205m"
interval = "5m"
language = "esql"
license = "Elastic License v2"
name = "Newly Observed Palo Alto Network Alert"
risk_score = 99
rule_id = "283683eb-f2ce-40a5-be16-fa931cb5f504"
severity = "critical"
tags = ["Use Case: Threat Detection", "Rule Type: Higher-Order Rule", "Resources: Investigation Guide", "Domain: Network", "Data Source: PAN-OS"]
timestamp_override = "event.ingested"
type = "esql"
query = '''
FROM logs-panw.panos-*, filebeat-* metadata _id
// exclude Informational and Low severity levels (4 and 5)
| where data_stream.dataset == "panw.panos" and TO_INTEGER(event.severity) <= 3 and event.action != "flood_detected"
| STATS Esql.alerts_count = count(*),
Esql.first_time_seen = MIN(@timestamp),
Esql.distinct_count_src_ip = COUNT_DISTINCT(source.ip),
Esql.distinct_count_dst_ip = COUNT_DISTINCT(destination.ip),
src_ip = VALUES(source.ip),
dst_ip = VALUES(destination.ip),
url_dom = VALUES(url.domain),
url_path = VALUES(url.path) by rule.name, event.action, event.type, event.kind, event.severity
// first time seen is within 10m of the rule execution time within last 5 days
| eval Esql.recent = DATE_DIFF("minute", Esql.first_time_seen, now())
| where Esql.recent <= 10 and Esql.alerts_count <= 5 and Esql.distinct_count_src_ip <= 2 and Esql.distinct_count_dst_ip <= 2
// move dynamic fields to ECS quivalent for rule exceptions
| eval source.ip = MV_FIRST(src_ip),
destination.ip = MV_FIRST(dst_ip),
url.domain = MV_FIRST(url_dom),
url.path = MV_FIRST(url_path)
| keep rule.name, event.*, Esql.*, source.ip, destination.ip, url.domain, url.path
'''
note = """## Triage and analysis
### Investigating Newly Observed Palo Alto Network Alert
This rule surfaces newly observed, low-frequency high severity Palo Alto Network alert within the last 5 days.
Because the alert has not been seen previously for this rule and host, it should be prioritized for validation to determine
whether it represents a true compromise or rare benign activity.
### Investigation Steps
- Identify the source address, affected host and review the associated rule name to understand the behavior that triggered the alert.
- Validate the source address under which the activity occurred and assess whether it aligns with normal behavior.
- Refer to the specific alert details like event.original to get more context.
### False Positive Considerations
- Vulnerability scanners and pentesting.
- Administrative scripts or automation tools can trigger detections when first introduced.
- Development or testing environments may produce one-off behaviors that resemble malicious techniques.
### Response and Remediation
- If the activity is confirmed malicious, isolate the affected host to prevent further execution or lateral movement.
- Terminate malicious processes and remove any dropped files or persistence mechanisms.
- Collect forensic artifacts to understand initial access and execution flow.
- Patch or remediate any vulnerabilities or misconfigurations that enabled the behavior.
- If benign, document the finding and consider tuning or exception handling to reduce future noise.
- Continue monitoring the host and environment for recurrence of the behavior or related alerts."""
references = ["https://www.elastic.co/docs/reference/integrations/panw"]
Stages and Predicates
Stage 1: from
FROM logs-panw.panos-*, filebeat-* metadata _id
Stage 2: where
| where data_stream.dataset == "panw.panos" and TO_INTEGER(event.severity) <= 3 and event.action != "flood_detected"
Stage 3: stats
| STATS Esql.alerts_count = count(*),
Esql.first_time_seen = MIN(@timestamp),
Esql.distinct_count_src_ip = COUNT_DISTINCT(source.ip),
Esql.distinct_count_dst_ip = COUNT_DISTINCT(destination.ip),
src_ip = VALUES(source.ip),
dst_ip = VALUES(destination.ip),
url_dom = VALUES(url.domain),
url_path = VALUES(url.path) by rule.name, event.action, event.type, event.kind, event.severity
Stage 4: eval
| eval Esql.recent = DATE_DIFF("minute", Esql.first_time_seen, now())
Stage 5: where
| where Esql.recent <= 10 and Esql.alerts_count <= 5 and Esql.distinct_count_src_ip <= 2 and Esql.distinct_count_dst_ip <= 2
Stage 6: eval
| eval source.ip = MV_FIRST(src_ip),
destination.ip = MV_FIRST(dst_ip),
url.domain = MV_FIRST(url_dom),
url.path = MV_FIRST(url_path)
Stage 7: keep
| keep rule.name, event.*, Esql.*, source.ip, destination.ip, url.domain, url.path
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
Esql.alerts_count | le |
|
Esql.distinct_count_dst_ip | le |
|
Esql.distinct_count_src_ip | le |
|
Esql.recent | le |
|
data_stream.dataset | eq |
|
event.action | ne |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
rule.name | KEEP rule.name |
event.* | KEEP event.* |
Esql.* | KEEP Esql.* |
source.ip | KEEP source.ip |
destination.ip | KEEP destination.ip |
url.domain | KEEP url.domain |
url.path | KEEP url.path |