Detection rules › Panther

Panther rules: gti

GTI/VirusTotal Threat Intelligence Indicator Match

#
Status
Experimental
Severity
high
Entities
domain_names, ip_addresses, sha256_hashes
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, GTI, VirusTotal, Threat Intelligence
Reference
www.virustotal.com
Source
github.com/panther-labs/panther-analysis

Detects when an IP address, domain, or file hash in any log event matches a known malicious indicator from Google Threat Intelligence (GTI) / VirusTotal enrichment. Severity is elevated based on GTI's threat severity verdict and the number of vendors flagging the indicator as malicious.

MITRE ATT&CK coverage

TacticTechniques
Reconnaissance

Telemetry coverage

Detection logic

from panther_gti_helpers import (
    get_gti_object,
    gti_alert_context,
    gti_severity,
    severity_greater_than,
)

INDICATOR_FIELDS = (
    "p_any_ip_addresses",
    "p_any_domain_names",
    "p_any_md5_hashes",
    "p_any_sha1_hashes",
    "p_any_sha256_hashes",
)

MATCHED_INDICATORS = {}  # {indicator: indicator_type}


def _first(value):
    """Collapse a possibly list-shaped lookup value (multiple LUT hits) to a single value."""
    if isinstance(value, list):
        return next((entry for entry in value if entry), None)
    return value


def rule(event):
    global MATCHED_INDICATORS  # pylint: disable=global-statement
    MATCHED_INDICATORS = {}

    gti = get_gti_object(event)
    if not gti:
        return False

    for field in INDICATOR_FIELDS:
        for value in event.get(field, []) or []:
            if value in MATCHED_INDICATORS:
                continue
            indicator_type = _first(gti.indicator_type(value))
            if not indicator_type:
                continue
            if not gti.is_malicious(value):
                continue
            MATCHED_INDICATORS[value] = indicator_type

    return bool(MATCHED_INDICATORS)


def title(event):
    log_type = event.get("p_log_type", "Unknown")
    if len(MATCHED_INDICATORS) == 1:
        indicator, ioc_type = next(iter(MATCHED_INDICATORS.items()))
        return f"GTI: Known malicious {ioc_type} [{indicator}] detected in {log_type}"
    return f"GTI: {len(MATCHED_INDICATORS)} threat indicators detected in {log_type}"


def severity(event):
    highest = None
    for indicator in MATCHED_INDICATORS:
        sev = gti_severity(event, indicator)
        if highest is None or severity_greater_than(sev, highest):
            highest = sev
    return highest or "DEFAULT"


def alert_context(event):
    if not MATCHED_INDICATORS:
        return {}
    ctx = {}
    for indicator, indicator_type in MATCHED_INDICATORS.items():
        indicator_ctx = gti_alert_context(event, indicator)
        indicator_ctx["MatchedIndicatorType"] = indicator_type
        ctx[indicator] = indicator_ctx
    return ctx

Rule specification

AnalysisType: rule
Filename: gti_malicious_indicator.py
RuleID: "Standard.GTI.MaliciousIndicator"
DisplayName: "GTI/VirusTotal Threat Intelligence Indicator Match"
Enabled: false
Status: Experimental
Severity: High
Description: >-
  Detects when an IP address, domain, or file hash in any log event matches a known
  malicious indicator from Google Threat Intelligence (GTI) / VirusTotal enrichment.
  Severity is elevated based on GTI's threat severity verdict and the number of
  vendors flagging the indicator as malicious.
Runbook: |
  1. Review the alert context and open the GTI/VirusTotal URL to assess the indicator's
     verdict, detection ratio, and suggested threat label.
  2. Query the data lake for all events involving this indicator to determine what assets
     or services were contacted and whether any connections were successful.
  3. If the indicator is confirmed malicious and interaction was observed, block the
     indicator, isolate affected hosts, and reset any credentials that may have been exposed.
Reference: https://www.virustotal.com
DedupPeriodMinutes: 60
Reports:
  MITRE ATT&CK:
    - TA0043:T1595.001
Tags:
  - Reconnaissance:Active Scanning
  - GTI
  - VirusTotal
  - 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

Stages and Predicates

Rule logic imperative Python

The parser could not express this rule's Python logic as a structured condition; the complete logic is under Detection logic above.

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
p_log_type

Response runbook

1. Review the alert context and open the GTI/VirusTotal URL to assess the indicator's

verdict, detection ratio, and suggested threat label.

2. Query the data lake for all events involving this indicator to determine what assets

or services were contacted and whether any connections were successful.

3. If the indicator is confirmed malicious and interaction was observed, block the

indicator, isolate affected hosts, and reset any credentials that may have been exposed.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "p_any_sha256_hashes": [
    "0000000000000000000000000000000000000000000000000000000000000000"
  ],
  "p_enrichment": {
    "vt_iocstream": {
      "0000000000000000000000000000000000000000000000000000000000000000": {
        "gti_url": "https://www.virustotal.com/gui/file/0000000000000000000000000000000000000000000000000000000000000000",
        "id": "0000000000000000000000000000000000000000000000000000000000000000",
        "last_analysis_stats": {
          "harmless": 0,
          "malicious": 57,
          "suspicious": 0,
          "undetected": 13
        },
        "md5": "00000000000000000000000000000000",
        "meaningful_name": "example-malware.exe",
        "names": [
          "example-malware.exe",
          "svchost32.exe"
        ],
        "popular_threat_classification": {
          "popular_threat_category": [
            {
              "count": 32,
              "value": "trojan"
            }
          ],
          "popular_threat_name": [
            {
              "count": 18,
              "value": "remcos"
            }
          ],
          "suggested_threat_label": "trojan.remcos/rescoms"
        },
        "reputation": -12,
        "sha256": "0000000000000000000000000000000000000000000000000000000000000000",
        "tags": [
          "peexe",
          "payload",
          "malware"
        ],
        "threat_severity": {
          "level_description": "Severity HIGH because it was considered trojan.",
          "threat_severity_level": "SEVERITY_HIGH"
        },
        "type": "file",
        "type_description": "Win32 EXE"
      }
    }
  },
  "p_log_type": "Crowdstrike.FDREvent"
}