Detection rules › Panther

GreyNoise V3 Malicious IP Activity

Severity
high
Entities
ip_addresses
Log types
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
Tags
Reconnaissance:Active Scanning, GreyNoise, Threat Intelligence
Reference
https://docs.panther.com/enrichment/greynoise
Source
github.com/panther-labs/panther-analysis

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

TacticTechniques
ReconnaissanceT1595.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