Detection rules › Panther
GreyNoise V3 Malicious IP Activity
Detects when an IP address in any log event is classified as malicious or unknown by GreyNoise V3 internet scanner intelligence. Known business services and benign IPs are excluded.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance | T1595.001 Active Scanning: Scanning IP Blocks |
Rule body yaml
AnalysisType: rule
Filename: greynoise_malicious_ip.py
RuleID: "Standard.GreyNoiseV3.MaliciousIP"
DisplayName: "GreyNoise V3 Malicious IP Activity"
Enabled: true
Severity: High
Description: >-
Detects when an IP address in any log event is classified as malicious or unknown
by GreyNoise V3 internet scanner intelligence. Known business services and benign
IPs are excluded.
Runbook: |
1. Review the alert context and open the GreyNoise URL to assess the IP's classification,
associated CVEs, tags, and scanning behavior.
2. Query the data lake for all events involving this IP to determine what assets or services
were contacted and whether any connections were successful.
3. If the IP is confirmed malicious and interaction was successful, block the IP, isolate
affected hosts, and reset any credentials that may have been exposed.
Reference: https://docs.panther.com/enrichment/greynoise
DedupPeriodMinutes: 60
Reports:
MITRE ATT&CK:
- TA0043:T1595.001
Tags:
- Reconnaissance:Active Scanning
- GreyNoise
- Threat Intelligence
SummaryAttributes:
- p_any_ip_addresses
- p_source_label
LogTypes:
- Amazon.EKS.Audit
- Asana.Audit
- Atlassian.Audit
- AWS.ALB
- AWS.CloudTrail
- AWS.VPCFlow
- Azure.Audit
- Azure.MonitorActivity
- Box.Event
- Cloudflare.Firewall
- Cloudflare.HttpRequest
- Crowdstrike.FDREvent
- GCP.AuditLog
- GSuite.ActivityEvent
- Notion.AuditLogs
- Okta.SystemLog
- OneLogin.Events
- OnePassword.SignInAttempt
- Zendesk.Audit
- Zoom.Activity
Tests:
- Name: "Malicious IP Detected"
ExpectedResult: true
Log:
p_any_ip_addresses:
- "142.93.204.250"
p_log_type: "AWS.CloudTrail"
p_enrichment:
greynoise_noise:
"142.93.204.250":
ip: "142.93.204.250"
internet_scanner_intelligence:
classification: "malicious"
found: true
actor: "alphastrike"
bot: false
cves:
- "CVE-2021-44228"
metadata:
asn: "AS14061"
organization: "DigitalOcean, LLC"
os: "Linux"
source_country: "United States"
spoofable: false
tags:
- id: "tag1"
name: "Log4j Scanner"
category: "activity"
intention: "malicious"
tor: false
vpn: false
vpn_service: ""
business_service_intelligence:
found: false
- Name: "Benign IP - No Alert"
ExpectedResult: false
Log:
p_any_ip_addresses:
- "8.8.8.8"
p_log_type: "Okta.SystemLog"
p_enrichment:
greynoise_noise:
"8.8.8.8":
ip: "8.8.8.8"
internet_scanner_intelligence:
classification: "benign"
found: true
actor: "Google DNS"
metadata:
organization: "Google LLC"
business_service_intelligence:
found: false
- Name: "No Enrichment Data"
ExpectedResult: false
Log:
p_any_ip_addresses:
- "10.0.0.1"
p_log_type: "AWS.VPCFlow"
- Name: "Unknown Classification"
ExpectedResult: true
Log:
p_any_ip_addresses:
- "203.0.113.50"
p_log_type: "Cloudflare.HttpRequest"
p_enrichment:
greynoise_noise:
"203.0.113.50":
ip: "203.0.113.50"
internet_scanner_intelligence:
classification: "unknown"
found: true
actor: ""
metadata:
organization: "Example ISP"
business_service_intelligence:
found: false
- Name: "Known Business Service - No Alert"
ExpectedResult: false
Log:
p_any_ip_addresses:
- "8.8.4.4"
p_log_type: "GSuite.ActivityEvent"
p_enrichment:
greynoise_noise:
"8.8.4.4":
ip: "8.8.4.4"
internet_scanner_intelligence:
classification: "benign"
found: true
actor: "Google DNS"
metadata:
organization: "Google LLC"
business_service_intelligence:
found: true
name: "Google DNS"
category: "public_dns"
trust_level: 1
Detection logic
Rule logic imperative Python
from panther_greynoise_helpers import (
get_greynoise_v3_business_service_object,
get_greynoise_v3_object,
greynoise_severity_decode,
greynoise_v3_alert_context,
severity_greater_than,
)
CLASSIFICATIONS_TO_ALERT = {"malicious", "unknown"}
MATCHED_IPS = {}
def rule(event):
global MATCHED_IPS
MATCHED_IPS = {}
scanner = get_greynoise_v3_object(event)
if not scanner:
return False
bsi = get_greynoise_v3_business_service_object(event)
for ip_addr in event.get("p_any_ip_addresses", []):
if bsi and bsi.found(ip_addr):
continue
classification = scanner.classification(ip_addr)
if not classification or classification == "benign":
continue
if classification in CLASSIFICATIONS_TO_ALERT:
MATCHED_IPS[ip_addr] = classification
return bool(MATCHED_IPS)
def title(event):
log_type = event.get("p_log_type", "Unknown")
if len(MATCHED_IPS) == 1:
ip_addr, classification = next(iter(MATCHED_IPS.items()))
return f"GreyNoise: {classification.title()} IP [{ip_addr}] detected in {log_type}"
return f"GreyNoise: {len(MATCHED_IPS)} suspicious IPs detected in {log_type}"
def severity(event):
highest = None
for classification in MATCHED_IPS.values():
sev = greynoise_severity_decode(classification, "DEFAULT")
if highest is None or severity_greater_than(sev, highest):
highest = sev
return highest or "DEFAULT"
def alert_context(event):
if not MATCHED_IPS:
return {}
ctx = {}
for ip_addr, classification in MATCHED_IPS.items():
ip_ctx = greynoise_v3_alert_context(event, ip_addr)
ip_ctx["MatchedClassification"] = classification
ctx[ip_addr] = ip_ctx
return ctx
The parser cannot express this rule's logic as a field filter; the imperative Python above is the detection.
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 |
|---|
p_log_type |