soc operations

Building Threat Intelligence Enrichment in Splunk

Build automated threat intelligence enrichment pipelines in Splunk Enterprise Security using lookup tables, modular inputs, and the Threat Intelligence Framework.

enrichmententerprise-securityioclookupsiemsocsplunkthreat-intelligence
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Splunk's Threat Intelligence Framework in Enterprise Security enables SOC teams to automatically correlate indicators of compromise (IOCs) against security events. The framework ingests threat feeds, normalizes indicators into KV Store collections, and uses lookup-based correlation searches to flag matching events. Splunk Threat Intelligence Management centralizes collection, normalization, and enrichment from multiple sources, reducing triage time by providing analysts with immediate context.

When to Use

  • When deploying or configuring building threat intelligence enrichment in splunk capabilities in your environment
  • When establishing security controls aligned to compliance requirements
  • When building or improving security architecture for this domain
  • When conducting security assessments that require this implementation

Prerequisites

  • Splunk Enterprise Security (ES) 7.x or later
  • Threat Intelligence Management add-on or Threat Intelligence Framework
  • API keys for external threat intelligence feeds (MISP, OTX, VirusTotal, AbuseIPDB)
  • KV Store enabled and properly configured
  • Admin access for modular input configuration

Threat Intelligence Framework Architecture

External TI Sources (STIX/TAXII, CSV, API)
    |
    v
Modular Inputs (download and parse feeds)
    |
    v
KV Store Collections (normalized IOC storage)
    |-- ip_intel
    |-- domain_intel
    |-- file_intel
    |-- url_intel
    |-- email_intel
    |
    v
Threat Intelligence Lookups
    |
    v
Correlation Searches (match events against IOCs)
    |
    v
Notable Events (enriched with TI context)

Configuring Threat Intelligence Sources

STIX/TAXII Feed Integration

# inputs.conf - TAXII feed configuration
[threatlist://taxii_feed_example]
description = TAXII 2.1 Threat Feed
type = taxii
url = https://threatfeed.example.com/taxii2/
collection = threat-indicators-v21
polling_interval = 3600
api_key = <encrypted_api_key>
disabled = false

CSV-Based Threat List

# inputs.conf - CSV threat list
[threatlist://custom_blocklist]
description = Internal threat blocklist
type = csv
url = https://internal.company.com/threat-feeds/blocklist.csv
polling_interval = 1800
disabled = false

Custom Modular Input for API-Based Feeds

# bin/threatfeed_otx.py - OTX AlienVault feed collector
import json
import sys
import requests
from splunklib.modularinput import Script, Scheme, Argument, Event
 
 
class OTXFeedInput(Script):
    def get_scheme(self):
        scheme = Scheme("OTX AlienVault Feed")
        scheme.description = "Collects IOCs from AlienVault OTX"
        scheme.use_external_validation = False
        scheme.streaming_mode = Scheme.streaming_mode_xml
 
        api_key_arg = Argument("api_key")
        api_key_arg.data_type = Argument.data_type_string
        api_key_arg.required_on_create = True
        scheme.add_argument(api_key_arg)
 
        pulse_days_arg = Argument("pulse_days")
        pulse_days_arg.data_type = Argument.data_type_number
        pulse_days_arg.required_on_create = False
        scheme.add_argument(pulse_days_arg)
 
        return scheme
 
    def stream_events(self, inputs, ew):
        for input_name, input_item in inputs.inputs.items():
            api_key = input_item["api_key"]
            pulse_days = int(input_item.get("pulse_days", 30))
 
            headers = {"X-OTX-API-KEY": api_key}
            url = f"https://otx.alienvault.com/api/v1/pulses/subscribed?modified_since={pulse_days}d"
 
            try:
                response = requests.get(url, headers=headers, timeout=60)
                response.raise_for_status()
                data = response.json()
 
                for pulse in data.get("results", []):
                    for indicator in pulse.get("indicators", []):
                        event = Event()
                        event.stanza = input_name
                        event.data = json.dumps({
                            "indicator": indicator["indicator"],
                            "type": indicator["type"],
                            "pulse_name": pulse["name"],
                            "pulse_id": pulse["id"],
                            "description": indicator.get("description", ""),
                            "created": indicator.get("created", ""),
                            "threat_source": "OTX",
                            "confidence": pulse.get("adversary", "unknown"),
                        })
                        ew.write_event(event)
            except requests.RequestException as e:
                ew.log("ERROR", f"OTX feed collection failed: {str(e)}")
 
 
if __name__ == "__main__":
    sys.exit(OTXFeedInput().run(sys.argv))

Building Enrichment Lookups

KV Store Collection Configuration

# collections.conf
[ip_threat_intel]
field.ip = string
field.threat_type = string
field.confidence = number
field.source = string
field.description = string
field.first_seen = time
field.last_seen = time
field.severity = string
 
[domain_threat_intel]
field.domain = string
field.threat_type = string
field.confidence = number
field.source = string
field.whois_registrar = string
field.whois_created = string
 
[file_hash_intel]
field.file_hash = string
field.hash_type = string
field.malware_family = string
field.confidence = number
field.source = string
field.detection_names = string

Lookup Table Definitions

# transforms.conf
[ip_threat_intel_lookup]
external_type = kvstore
collection = ip_threat_intel
fields_list = ip, threat_type, confidence, source, description, severity
 
[domain_threat_intel_lookup]
external_type = kvstore
collection = domain_threat_intel
fields_list = domain, threat_type, confidence, source
 
[file_hash_intel_lookup]
external_type = kvstore
collection = file_hash_intel
fields_list = file_hash, hash_type, malware_family, confidence, source

Enrichment Correlation Searches

IP-Based Threat Intelligence Correlation

| tstats summariesonly=true count from datamodel=Network_Traffic
    where All_Traffic.action=allowed
    by All_Traffic.src_ip, All_Traffic.dest_ip, All_Traffic.dest_port, _time span=5m
| rename "All_Traffic.*" as *
| lookup ip_threat_intel_lookup ip as dest_ip OUTPUT threat_type, confidence, source as ti_source, severity as ti_severity
| where isnotnull(threat_type)
| lookup asset_lookup ip as src_ip OUTPUT asset_name, asset_owner, asset_priority
| eval urgency=case(
    ti_severity=="critical" AND asset_priority=="critical", "critical",
    ti_severity=="high" OR asset_priority=="critical", "high",
    ti_severity=="medium", "medium",
    true(), "low"
)
| eval description="Connection from ".src_ip." (".asset_name.") to known malicious IP ".dest_ip." (".threat_type.") - Source: ".ti_source

Domain-Based Threat Intelligence Correlation

index=dns sourcetype=stream:dns query_type=A OR query_type=AAAA
| lookup domain_threat_intel_lookup domain as query OUTPUT threat_type as domain_threat, confidence as domain_confidence, source as ti_source
| where isnotnull(domain_threat) AND domain_confidence > 70
| stats count dc(src_ip) as unique_sources values(src_ip) as source_ips by query, domain_threat, ti_source
| eval severity=case(domain_confidence > 90, "critical", domain_confidence > 70, "high", true(), "medium")
| eval description="DNS queries to malicious domain ".query." from ".unique_sources." hosts - Threat: ".domain_threat

File Hash Correlation

index=endpoint sourcetype=sysmon EventCode=1
| lookup file_hash_intel_lookup file_hash as Hashes OUTPUT malware_family, confidence as hash_confidence, source as ti_source
| where isnotnull(malware_family)
| stats count values(ParentCommandLine) as parent_commands by Computer, User, Image, malware_family, ti_source
| eval severity="critical"
| eval description="Known malware ".malware_family." executed on ".Computer." by ".User." - Binary: ".Image

Multi-Source Enrichment Pipeline

index=firewall sourcetype=pan:traffic action=allowed
| eval indicators=mvappend(src_ip, dest_ip)
| mvexpand indicators
| lookup ip_threat_intel_lookup ip as indicators OUTPUT threat_type as ip_threat, confidence as ip_confidence, source as ip_ti_source
| lookup geo_ip_lookup ip as indicators OUTPUT country, city, latitude, longitude
| lookup whois_lookup ip as indicators OUTPUT org as ip_org, asn as ip_asn
| where isnotnull(ip_threat)
| stats count
    values(ip_threat) as threat_types
    values(ip_ti_source) as intel_sources
    values(country) as countries
    values(ip_org) as organizations
    latest(_time) as last_seen
    earliest(_time) as first_seen
    by src_ip, dest_ip, dest_port
| eval enrichment_context="Threat: ".mvjoin(threat_types, ", ")." | Geo: ".mvjoin(countries, ", ")." | Org: ".mvjoin(organizations, ", ")

Threat Intelligence Dashboards

IOC Coverage Statistics

| inputlookup ip_threat_intel_lookup
| stats count by source, threat_type
| sort -count
| head 20

Feed Freshness Monitoring

| inputlookup ip_threat_intel_lookup
| eval age_days=round((now() - strptime(last_seen, "%Y-%m-%dT%H:%M:%S")) / 86400, 0)
| stats count avg(age_days) as avg_age_days max(age_days) as max_age_days by source
| eval status=case(avg_age_days > 30, "STALE", avg_age_days > 7, "AGING", true(), "FRESH")

References

Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md2.0 KB

API Reference: Threat Intelligence Enrichment in Splunk

Splunk KV Store REST API

# Create collection
curl -k -u admin:pass -X POST \
  "https://localhost:8089/servicesNS/nobody/SA-ThreatIntelligence/storage/collections/config" \
  -d name=ip_intel
 
# Insert record
curl -k -u admin:pass -X POST \
  "https://localhost:8089/servicesNS/nobody/SA-ThreatIntelligence/storage/collections/data/ip_intel" \
  -H "Content-Type: application/json" \
  -d '{"ip":"198.51.100.42","threat_key":"c2_server","weight":"3"}'
 
# Batch insert
curl -k -u admin:pass -X POST \
  "https://localhost:8089/servicesNS/nobody/SA-ThreatIntelligence/storage/collections/data/ip_intel/batch_save" \
  -H "Content-Type: application/json" \
  -d '[{"ip":"1.2.3.4","threat_key":"malware"},{"ip":"5.6.7.8","threat_key":"c2"}]'

Splunk Enterprise Security TI Framework

Collection Lookup Data Model
ip_intel ip_intel_lookup Network_Traffic
domain_intel domain_intel_lookup Network_Resolution
file_intel file_intel_lookup Endpoint
email_intel email_intel_lookup Email
http_intel http_intel_lookup Web

SPL Threat Matching

| tstats summariesonly=t count from datamodel=Network_Traffic
  by All_Traffic.dest_ip
| rename All_Traffic.dest_ip as ip
| lookup ip_intel_lookup ip OUTPUT threat_key description
| where isnotnull(threat_key)

AlienVault OTX API

# Get pulse indicators
curl "https://otx.alienvault.com/api/v1/pulses/PULSE_ID/indicators"
 
# Search pulses
curl -H "X-OTX-API-KEY: $OTX_KEY" \
  "https://otx.alienvault.com/api/v1/search/pulses?q=ransomware&page=1"

Splunk Python SDK

import splunklib.client as client
 
service = client.connect(
    host="localhost", port=8089,
    username="admin", password="changeme"
)
 
# Access KV store collection
collection = service.kvstore["ip_intel"]
collection.data.insert(json.dumps({
    "ip": "198.51.100.42",
    "threat_key": "c2_server"
}))
standards.md1.4 KB

Standards - Threat Intelligence Enrichment in Splunk

Threat Intelligence Standards

STIX (Structured Threat Information eXpression)

  • Version 2.1 is the current standard for representing threat intelligence
  • Defines objects: Indicator, Malware, Attack Pattern, Threat Actor, Campaign
  • Used as the interchange format between TI platforms and SIEMs

TAXII (Trusted Automated eXchange of Indicator Information)

  • Transport mechanism for STIX data
  • TAXII 2.1 provides RESTful API for feed collection
  • Supports Collection and Channel sharing models

OpenIOC

  • Mandiant's open framework for sharing IOCs
  • XML-based format for indicator definitions

OCSF (Open Cybersecurity Schema Framework)

  • Industry standard for normalizing security event data
  • Version 1.0 released at BlackHat 2023

Splunk CIM Data Models for TI

Data Model TI Correlation Fields
Network_Traffic src_ip, dest_ip, dest_port
Web url, http_user_agent, domain
Email src_user, file_hash, url
Endpoint process_hash, file_hash, dest
Authentication src_ip, user, app
DNS query, answer, src_ip

IOC Types and Confidence Levels

IOC Type Splunk Field Confidence Threshold
IP Address ip_intel > 70%
Domain domain_intel > 70%
File Hash (SHA256) file_intel > 80%
URL url_intel > 75%
Email Address email_intel > 80%
workflows.md1.7 KB

Workflows - Threat Intelligence Enrichment in Splunk

TI Feed Integration Workflow

1. Identify Relevant TI Sources
   - Commercial feeds (Recorded Future, Mandiant)
   - Open source (OTX, AbuseIPDB, VirusTotal)
   - Industry ISACs
   - Internal threat lists
   |
   v
2. Configure Modular Inputs
   - Set polling intervals
   - Configure authentication
   - Map feed fields to Splunk schema
   |
   v
3. Normalize to KV Store
   - Parse raw feed data
   - Map to standard field names
   - Set confidence scores
   - Add source attribution
   |
   v
4. Create Lookup Definitions
   - Define transforms.conf entries
   - Set field mappings
   - Enable automatic lookups where appropriate
   |
   v
5. Build Correlation Searches
   - Match events against IOC lookups
   - Add asset/identity enrichment
   - Set severity based on confidence
   |
   v
6. Monitor and Maintain
   - Track feed freshness
   - Remove stale indicators
   - Measure hit rates per source

IOC Lifecycle Management

Ingestion --> Validation --> Active Use --> Aging --> Expiration --> Removal
    |              |            |            |           |
    v              v            v            v           v
  Raw feeds    Dedup and    Correlation   Reduce      Archive
  parsed       confidence   matching     confidence   or delete
               scoring                   weighting

Feed Quality Assessment

Metric Good Warning Critical
Feed latency < 1 hour 1-24 hours > 24 hours
False positive rate < 5% 5-15% > 15%
Hit rate > 1% 0.1-1% < 0.1%
Coverage overlap < 30% 30-60% > 60%
Indicator freshness < 7 days 7-30 days > 30 days

Scripts 2

agent.py6.5 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Threat intelligence enrichment pipeline for Splunk.

Manages threat intel lookups, KV store collections, and modular inputs
for enriching Splunk events with IOC context from MISP, OTX, and CSV feeds.
"""

import json
import csv
import datetime
import io

try:
    import requests
    HAS_REQUESTS = True
except ImportError:
    HAS_REQUESTS = False


SPLUNK_TI_COLLECTIONS = {
    "ip_intel": {
        "fields": ["ip", "threat_key", "description", "source", "weight", "time"],
        "lookup_name": "ip_intel_lookup",
    },
    "domain_intel": {
        "fields": ["domain", "threat_key", "description", "source", "weight", "time"],
        "lookup_name": "domain_intel_lookup",
    },
    "file_intel": {
        "fields": ["file_hash", "file_name", "threat_key", "description", "source", "weight", "time"],
        "lookup_name": "file_intel_lookup",
    },
    "email_intel": {
        "fields": ["src_user", "threat_key", "description", "source", "weight", "time"],
        "lookup_name": "email_intel_lookup",
    },
}


def fetch_otx_pulse_iocs(pulse_id):
    """Fetch IOCs from AlienVault OTX pulse."""
    if not HAS_REQUESTS:
        return {"error": "requests not installed"}
    url = "https://otx.alienvault.com/api/v1/pulses/{}/indicators".format(pulse_id)
    try:
        resp = requests.get(url, timeout=15)
        if resp.status_code == 200:
            data = resp.json()
            iocs = []
            for ind in data.get("results", []):
                iocs.append({
                    "type": ind.get("type", ""),
                    "indicator": ind.get("indicator", ""),
                    "title": ind.get("title", ""),
                    "created": ind.get("created", ""),
                })
            return {"pulse_id": pulse_id, "count": len(iocs), "indicators": iocs}
        return {"error": "HTTP {}".format(resp.status_code)}
    except Exception as e:
        return {"error": str(e)}


def convert_iocs_to_splunk_lookup(iocs, collection_type="ip_intel"):
    """Convert IOC list to Splunk KV store format."""
    collection = SPLUNK_TI_COLLECTIONS.get(collection_type, SPLUNK_TI_COLLECTIONS["ip_intel"])
    rows = []
    now = datetime.datetime.utcnow().isoformat() + "Z"
    for ioc in iocs:
        if collection_type == "ip_intel" and ioc.get("type") in ("IPv4", "IPv6"):
            rows.append({
                "ip": ioc["indicator"],
                "threat_key": ioc.get("title", "malicious_ip"),
                "description": "OTX: " + ioc.get("title", ""),
                "source": "otx",
                "weight": "3",
                "time": now,
            })
        elif collection_type == "domain_intel" and ioc.get("type") in ("domain", "hostname"):
            rows.append({
                "domain": ioc["indicator"],
                "threat_key": ioc.get("title", "malicious_domain"),
                "description": "OTX: " + ioc.get("title", ""),
                "source": "otx",
                "weight": "3",
                "time": now,
            })
        elif collection_type == "file_intel" and ioc.get("type") in ("FileHash-SHA256", "FileHash-MD5"):
            rows.append({
                "file_hash": ioc["indicator"],
                "file_name": "",
                "threat_key": ioc.get("title", "malicious_file"),
                "description": "OTX: " + ioc.get("title", ""),
                "source": "otx",
                "weight": "3",
                "time": now,
            })
    return {"collection": collection_type, "lookup_name": collection["lookup_name"], "row_count": len(rows), "rows": rows}


def generate_splunk_lookup_csv(rows, output_path=None):
    """Generate CSV file for Splunk lookup table."""
    if not rows:
        return ""
    output = io.StringIO()
    writer = csv.DictWriter(output, fieldnames=rows[0].keys())
    writer.writeheader()
    writer.writerows(rows)
    csv_content = output.getvalue()
    if output_path:
        with open(output_path, "w", encoding="utf-8") as f:
            f.write(csv_content)
    return csv_content


def build_spl_correlation_search(collection_type="ip_intel"):
    """Build SPL query for threat intelligence correlation."""
    queries = {
        "ip_intel": (
            '| tstats summariesonly=t count from datamodel=Network_Traffic '
            'by All_Traffic.dest_ip '
            '| rename All_Traffic.dest_ip as ip '
            '| lookup ip_intel_lookup ip OUTPUT threat_key description source '
            '| where isnotnull(threat_key) '
            '| table ip threat_key description source count'
        ),
        "domain_intel": (
            '| tstats summariesonly=t count from datamodel=Network_Resolution '
            'by DNS.query '
            '| rename DNS.query as domain '
            '| lookup domain_intel_lookup domain OUTPUT threat_key description source '
            '| where isnotnull(threat_key) '
            '| table domain threat_key description source count'
        ),
        "file_intel": (
            'index=endpoint sourcetype=sysmon EventCode=1 '
            '| lookup file_intel_lookup file_hash as Hashes OUTPUT threat_key description '
            '| where isnotnull(threat_key) '
            '| table _time Computer Image Hashes threat_key description'
        ),
    }
    return queries.get(collection_type, "| makeresults | eval error=\"Unknown collection\"")


if __name__ == "__main__":
    print("=" * 60)
    print("Threat Intelligence Enrichment in Splunk")
    print("KV store collections, lookup tables, SPL correlation")
    print("=" * 60)
    print("  requests available: {}".format(HAS_REQUESTS))

    print("\n--- Splunk TI Collections ---")
    for name, info in SPLUNK_TI_COLLECTIONS.items():
        print("  {}: lookup={}, fields={}".format(name, info["lookup_name"], len(info["fields"])))

    print("\n--- SPL Correlation Queries ---")
    for ctype in ["ip_intel", "domain_intel", "file_intel"]:
        spl = build_spl_correlation_search(ctype)
        print("  [{}] {}...".format(ctype, spl[:80]))

    demo_iocs = [
        {"type": "IPv4", "indicator": "198.51.100.42", "title": "C2 Server"},
        {"type": "domain", "indicator": "evil.example.com", "title": "Phishing Domain"},
        {"type": "FileHash-SHA256", "indicator": "a" * 64, "title": "Malware Sample"},
    ]
    for ctype in ["ip_intel", "domain_intel", "file_intel"]:
        result = convert_iocs_to_splunk_lookup(demo_iocs, ctype)
        if result["row_count"] > 0:
            print("\n  Converted {} IOCs to {} format".format(result["row_count"], ctype))

    print("\n" + json.dumps({"collections_configured": len(SPLUNK_TI_COLLECTIONS)}, indent=2))
process.py10.1 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Splunk Threat Intelligence Enrichment Pipeline

Manages threat intelligence feed ingestion, normalization,
and enrichment workflows for Splunk Enterprise Security.
"""

import json
import hashlib
from datetime import datetime, timedelta
from typing import Optional


class ThreatIndicator:
    """Represents a single threat intelligence indicator."""

    def __init__(self, value: str, indicator_type: str, source: str,
                 threat_type: str, confidence: int, description: str = "",
                 severity: str = "medium", first_seen: Optional[str] = None,
                 last_seen: Optional[str] = None, tags: Optional[list] = None):
        self.value = value
        self.indicator_type = indicator_type
        self.source = source
        self.threat_type = threat_type
        self.confidence = min(100, max(0, confidence))
        self.description = description
        self.severity = severity
        self.first_seen = first_seen or datetime.utcnow().isoformat()
        self.last_seen = last_seen or datetime.utcnow().isoformat()
        self.tags = tags or []
        self.indicator_id = self._generate_id()

    def _generate_id(self) -> str:
        hash_input = f"{self.indicator_type}:{self.value}:{self.source}"
        return hashlib.sha256(hash_input.encode()).hexdigest()[:16]

    def is_expired(self, max_age_days: int = 90) -> bool:
        last = datetime.fromisoformat(self.last_seen.replace("Z", "+00:00").replace("+00:00", ""))
        return (datetime.utcnow() - last).days > max_age_days

    def to_kv_store_record(self) -> dict:
        type_field_map = {
            "ip": "ip",
            "domain": "domain",
            "file_hash": "file_hash",
            "url": "url",
            "email": "email",
        }
        key_field = type_field_map.get(self.indicator_type, "value")
        return {
            "_key": self.indicator_id,
            key_field: self.value,
            "threat_type": self.threat_type,
            "confidence": self.confidence,
            "source": self.source,
            "description": self.description,
            "severity": self.severity,
            "first_seen": self.first_seen,
            "last_seen": self.last_seen,
            "tags": ",".join(self.tags),
        }


class ThreatFeed:
    """Represents a threat intelligence feed source."""

    def __init__(self, name: str, feed_type: str, url: str,
                 polling_interval: int = 3600, api_key: str = ""):
        self.name = name
        self.feed_type = feed_type
        self.url = url
        self.polling_interval = polling_interval
        self.api_key = api_key
        self.indicators = []
        self.last_poll = None
        self.stats = {"total_ingested": 0, "duplicates_skipped": 0, "expired_removed": 0}

    def add_indicator(self, indicator: ThreatIndicator):
        self.indicators.append(indicator)
        self.stats["total_ingested"] += 1

    def get_active_indicators(self, max_age_days: int = 90) -> list:
        active = [i for i in self.indicators if not i.is_expired(max_age_days)]
        self.stats["expired_removed"] = len(self.indicators) - len(active)
        return active

    def generate_splunk_input_conf(self) -> str:
        return f"""[threatlist://{self.name}]
description = {self.name} threat feed
type = {self.feed_type}
url = {self.url}
polling_interval = {self.polling_interval}
disabled = false
"""


class EnrichmentPipeline:
    """Manages the complete TI enrichment pipeline for Splunk."""

    def __init__(self):
        self.feeds = []
        self.kv_store = {"ip_intel": [], "domain_intel": [], "file_intel": [], "url_intel": []}
        self.correlation_hits = []

    def add_feed(self, feed: ThreatFeed):
        self.feeds.append(feed)

    def ingest_all_feeds(self):
        for feed in self.feeds:
            active = feed.get_active_indicators()
            for indicator in active:
                collection = self._get_collection(indicator.indicator_type)
                if collection is not None:
                    self.kv_store[collection].append(indicator.to_kv_store_record())

    def _get_collection(self, indicator_type: str) -> Optional[str]:
        mapping = {
            "ip": "ip_intel",
            "domain": "domain_intel",
            "file_hash": "file_intel",
            "url": "url_intel",
        }
        return mapping.get(indicator_type)

    def simulate_correlation(self, events: list) -> list:
        """Simulate correlating events against TI indicators."""
        hits = []
        ip_iocs = {r["ip"]: r for r in self.kv_store.get("ip_intel", []) if "ip" in r}
        domain_iocs = {r["domain"]: r for r in self.kv_store.get("domain_intel", []) if "domain" in r}

        for event in events:
            dest_ip = event.get("dest_ip", "")
            domain = event.get("domain", "")

            if dest_ip in ip_iocs:
                ioc = ip_iocs[dest_ip]
                hits.append({
                    "event": event,
                    "match_type": "ip",
                    "matched_value": dest_ip,
                    "threat_type": ioc["threat_type"],
                    "confidence": ioc["confidence"],
                    "source": ioc["source"],
                    "severity": ioc["severity"],
                })

            if domain in domain_iocs:
                ioc = domain_iocs[domain]
                hits.append({
                    "event": event,
                    "match_type": "domain",
                    "matched_value": domain,
                    "threat_type": ioc["threat_type"],
                    "confidence": ioc["confidence"],
                    "source": ioc["source"],
                    "severity": ioc["severity"],
                })

        self.correlation_hits = hits
        return hits

    def get_pipeline_stats(self) -> dict:
        return {
            "total_feeds": len(self.feeds),
            "kv_store_sizes": {k: len(v) for k, v in self.kv_store.items()},
            "total_indicators": sum(len(v) for v in self.kv_store.values()),
            "correlation_hits": len(self.correlation_hits),
            "feed_stats": [
                {"name": f.name, "indicators": len(f.indicators), "stats": f.stats}
                for f in self.feeds
            ],
        }

    def generate_spl_correlation(self, indicator_type: str) -> str:
        templates = {
            "ip": (
                '| tstats summariesonly=true count from datamodel=Network_Traffic '
                'where All_Traffic.action=allowed by All_Traffic.dest_ip, All_Traffic.src_ip, _time span=5m\n'
                '| rename "All_Traffic.*" as *\n'
                '| lookup ip_threat_intel_lookup ip as dest_ip '
                'OUTPUT threat_type, confidence, source as ti_source, severity as ti_severity\n'
                '| where isnotnull(threat_type) AND confidence > 70\n'
                '| eval description="TI Hit: ".dest_ip." (".threat_type.") from ".ti_source'
            ),
            "domain": (
                'index=dns sourcetype=stream:dns\n'
                '| lookup domain_threat_intel_lookup domain as query '
                'OUTPUT threat_type, confidence, source as ti_source\n'
                '| where isnotnull(threat_type) AND confidence > 70\n'
                '| stats count dc(src_ip) as unique_sources by query, threat_type, ti_source\n'
                '| eval description="DNS to malicious domain ".query." from ".unique_sources." sources"'
            ),
            "file_hash": (
                'index=endpoint sourcetype=sysmon EventCode=1\n'
                '| lookup file_hash_intel_lookup file_hash as Hashes '
                'OUTPUT malware_family, confidence, source as ti_source\n'
                '| where isnotnull(malware_family)\n'
                '| eval description="Known malware ".malware_family." on ".Computer'
            ),
        }
        return templates.get(indicator_type, "# No template for this indicator type")


if __name__ == "__main__":
    pipeline = EnrichmentPipeline()

    # Create sample feeds
    otx_feed = ThreatFeed("AlienVault_OTX", "api", "https://otx.alienvault.com/api/v1/pulses/subscribed")
    otx_feed.add_indicator(ThreatIndicator("203.0.113.50", "ip", "OTX", "C2", 85, "Known C2 server", "high"))
    otx_feed.add_indicator(ThreatIndicator("198.51.100.25", "ip", "OTX", "Scanner", 60, "Port scanner", "medium"))
    otx_feed.add_indicator(ThreatIndicator("evil-domain.com", "domain", "OTX", "Phishing", 92, "Phishing domain", "critical"))

    abuse_feed = ThreatFeed("AbuseIPDB", "csv", "https://api.abuseipdb.com/api/v2/blacklist")
    abuse_feed.add_indicator(ThreatIndicator("203.0.113.50", "ip", "AbuseIPDB", "C2", 90, "Reported C2", "high"))
    abuse_feed.add_indicator(ThreatIndicator("192.0.2.100", "ip", "AbuseIPDB", "Brute Force", 75, "SSH brute forcer", "medium"))

    pipeline.add_feed(otx_feed)
    pipeline.add_feed(abuse_feed)
    pipeline.ingest_all_feeds()

    # Simulate events
    events = [
        {"src_ip": "10.0.0.50", "dest_ip": "203.0.113.50", "dest_port": 443, "domain": ""},
        {"src_ip": "10.0.1.100", "dest_ip": "8.8.8.8", "dest_port": 53, "domain": "google.com"},
        {"src_ip": "10.0.2.75", "dest_ip": "93.184.216.34", "dest_port": 80, "domain": "evil-domain.com"},
        {"src_ip": "10.0.0.10", "dest_ip": "192.0.2.100", "dest_port": 22, "domain": ""},
    ]

    hits = pipeline.simulate_correlation(events)

    print("=" * 70)
    print("THREAT INTELLIGENCE ENRICHMENT PIPELINE")
    print("=" * 70)

    stats = pipeline.get_pipeline_stats()
    print(f"\nFeeds: {stats['total_feeds']}")
    print(f"Total Indicators: {stats['total_indicators']}")
    print(f"KV Store: {stats['kv_store_sizes']}")

    print(f"\nCorrelation Hits: {len(hits)}")
    for hit in hits:
        print(f"  [{hit['severity'].upper()}] {hit['match_type']}: {hit['matched_value']} "
              f"({hit['threat_type']}) - Confidence: {hit['confidence']}% - Source: {hit['source']}")

    print(f"\n{'=' * 70}")
    print("GENERATED SPL CORRELATION SEARCHES")
    print("=" * 70)
    for ioc_type in ["ip", "domain", "file_hash"]:
        print(f"\n--- {ioc_type.upper()} Correlation ---")
        print(pipeline.generate_spl_correlation(ioc_type))

Assets 1

template.mdtext/markdown · 1.3 KB
Keep exploring