Automating IOC Enrichment
When to Use
Use this skill when:
- Building a SOAR playbook that automatically enriches SIEM alerts with threat intelligence context before routing to analysts
- Creating a Python pipeline for bulk IOC enrichment from phishing email submissions
- Reducing analyst mean time to triage (MTTT) by pre-populating alert context with VT, Shodan, and MISP data
Do not use this skill for fully automated blocking decisions without human review — enrichment automation should inform decisions, not execute blocks autonomously for high-impact actions.
Detection Gaps & Validation
- Silent API failures: a 429 or timeout that returns empty stats reads downstream as a clean IOC. Failed calls must raise/log and trigger fallback, never default
vt_malicious=0. The retry_on_429 decorator returns None after max retries -- callers must handle that, not treat it as benign.
- Missing caching: re-querying the same IOC burns rate budget (VT free = 4/min) and stalls the pipeline; cache results ~24h keyed by value+type.
- Composite-score blind spots: the weighted formula (VT 60% / AbuseIPDB 40%) over-trusts AV consensus and under-weights shared-infra context -- a CDN IP can score high. Never auto-block on score alone.
- Latency cliff: if enrichment exceeds ~5 min, analysts work unenriched alerts; enforce timeouts and emit partial results.
To validate: run a known-malicious and a known-benign IOC through the pipeline and confirm scores land in the expected High/Medium/Low routing tier; force (or mock) a 429 and confirm the IOC is flagged "enrichment incomplete," not "clean." Track analyst overrides of the composite score weekly as ground truth and re-tune weights; confirm the cache returns identical results for repeat lookups and that rate-limit decorators actually space VT calls to <=4/min.
Prerequisites
- SOAR platform (Cortex XSOAR, Splunk SOAR, Tines, or n8n) or Python 3.9+ environment
- API keys: VirusTotal, AbuseIPDB, Shodan, and at minimum one TIP (MISP or OpenCTI)
- SIEM integration endpoint for alert consumption
- Rate limit budgets documented per API (VT: 4/min free, 500/min enterprise)
Workflow
Step 1: Design Enrichment Pipeline Architecture
Define the enrichment flow for each IOC type:
SIEM Alert → Extract IOCs → Classify Type → Route to enrichment functions
IP Address → AbuseIPDB + Shodan + VirusTotal IP + MISP
Domain → VirusTotal Domain + PassiveTotal + Shodan + MISP
URL → URLScan.io + VirusTotal URL + Google Safe Browse
File Hash → VirusTotal Files + MalwareBazaar + MISP
→ Aggregate results → Calculate confidence score → Update alert → Notify analyst
Step 2: Implement Python Enrichment Functions
import requests
import time
from dataclasses import dataclass, field
from typing import Optional
RATE_LIMIT_DELAY = 0.25 # 4 requests/second for VT free tier
@dataclass
class EnrichmentResult:
ioc_value: str
ioc_type: str
vt_malicious: int = 0
vt_total: int = 0
abuse_confidence: int = 0
shodan_ports: list = field(default_factory=list)
misp_events: list = field(default_factory=list)
confidence_score: int = 0
def enrich_ip(ip: str, vt_key: str, abuse_key: str, shodan_key: str) -> EnrichmentResult:
result = EnrichmentResult(ip, "ip")
# VirusTotal IP lookup
vt_resp = requests.get(
f"https://www.virustotal.com/api/v3/ip_addresses/{ip}",
headers={"x-apikey": vt_key}
)
if vt_resp.status_code == 200:
stats = vt_resp.json()["data"]["attributes"]["last_analysis_stats"]
result.vt_malicious = stats.get("malicious", 0)
result.vt_total = sum(stats.values())
time.sleep(RATE_LIMIT_DELAY)
# AbuseIPDB
abuse_resp = requests.get(
"https://api.abuseipdb.com/api/v2/check",
headers={"Key": abuse_key, "Accept": "application/json"},
params={"ipAddress": ip, "maxAgeInDays": 90}
)
if abuse_resp.status_code == 200:
result.abuse_confidence = abuse_resp.json()["data"]["abuseConfidenceScore"]
# Calculate composite confidence score
result.confidence_score = min(
(result.vt_malicious / max(result.vt_total, 1)) * 60 +
(result.abuse_confidence / 100) * 40, 100
)
return result
def enrich_hash(sha256: str, vt_key: str) -> EnrichmentResult:
result = EnrichmentResult(sha256, "sha256")
vt_resp = requests.get(
f"https://www.virustotal.com/api/v3/files/{sha256}",
headers={"x-apikey": vt_key}
)
if vt_resp.status_code == 200:
stats = vt_resp.json()["data"]["attributes"]["last_analysis_stats"]
result.vt_malicious = stats.get("malicious", 0)
result.vt_total = sum(stats.values())
result.confidence_score = int((result.vt_malicious / max(result.vt_total, 1)) * 100)
return result
Step 3: Build SOAR Playbook (Cortex XSOAR)
In Cortex XSOAR, create an enrichment playbook:
- Trigger: Alert created in SIEM (via webhook or polling)
- Extract IOCs: Use "Extract Indicators" task with regex patterns for IP, domain, URL, hash
- Parallel enrichment: Fan-out to multiple enrichment tasks simultaneously
- VT Enrichment: Call
!vt-file-scan or !vt-ip-scan commands
- AbuseIPDB check: Call
!abuseipdb-check-ip command
- MISP Lookup: Call
!misp-search for cross-referencing
- Score aggregation: Python transform task computing composite score
- Conditional routing: If score ≥70 → High Priority queue; if 40–69 → Medium; <40 → Auto-close with note
- Alert enrichment: Write enrichment results to alert context for analyst view
Step 4: Handle Rate Limiting and Failures
import time
from functools import wraps
def rate_limited(max_per_second):
min_interval = 1.0 / max_per_second
def decorator(func):
last_called = [0.0]
@wraps(func)
def wrapper(*args, **kwargs):
elapsed = time.time() - last_called[0]
wait = min_interval - elapsed
if wait > 0:
time.sleep(wait)
result = func(*args, **kwargs)
last_called[0] = time.time()
return result
return wrapper
return decorator
def retry_on_429(max_retries=3):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
response = func(*args, **kwargs)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
time.sleep(retry_after)
else:
return response
return wrapper
return decorator
Step 5: Metrics and Tuning
Track pipeline performance weekly:
- Enrichment latency: Target <30 seconds from alert trigger to enriched output
- API success rate: Target >99% (identify rate limit or outage events)
- True positive rate: Track analyst overrides of automated confidence scores
- Cost: Track API call volume against budget (VT Enterprise: $X per 1M lookups)
Key Concepts
| Term |
Definition |
| SOAR |
Security Orchestration, Automation, and Response — platform for automating security workflows and integrating disparate tools |
| Enrichment Playbook |
Automated workflow sequence that adds contextual intelligence to raw security events |
| Rate Limiting |
API provider restrictions on request frequency (e.g., VT free: 4 requests/minute); pipelines must respect these limits |
| Composite Confidence Score |
Single score aggregating signals from multiple enrichment sources using weighted formula |
| Fan-out Pattern |
Parallel execution of multiple enrichment queries simultaneously to minimize total enrichment latency |
Tools & Systems
- Cortex XSOAR (Palo Alto): Enterprise SOAR with 700+ marketplace integrations including VT, MISP, Shodan, and AbuseIPDB
- Splunk SOAR (Phantom): SOAR platform with Python-based playbooks; native Splunk SIEM integration
- Tines: No-code SOAR platform with webhook-driven automation; cost-effective for smaller teams
- TheHive + Cortex: Open-source IR/enrichment platform with observable enrichment via Cortex analyzers
Common Pitfalls
- Blocking on enrichment latency: If enrichment takes >5 minutes, analysts start working unenriched alerts, defeating the purpose. Set timeout limits and provide partial results.
- No caching: Querying the same IOC 50 times generates unnecessary API costs. Cache enrichment results for 24 hours by default.
- Ignoring API failures silently: Failed enrichment calls should be logged and trigger fallback logic, not silently produce empty results that appear as clean IOCs.
- Automating blocks on enrichment score alone: Composite scores contain false positives; require human confirmation for blocking decisions against shared infrastructure.
1---2name: automating-ioc-enrichment3description: Automates the enrichment of raw indicators of compromise with multi-source threat intelligence context using SOAR platforms, Python pipelines, or TIP playbooks to reduce analyst triage time and standardize enrichment outputs. Use when building automated enrichment workflows integrated with SIEM alerts, email submission pipelines, or bulk IOC processing from threat feeds. Activates for requests involving SOAR enrichment, Cortex XSOAR, Splunk SOAR, TheHive, Python enrichment pipelines, or automated IOC processing.4license: Apache-2.05---6# Automating IOC Enrichment
7
8## When to Use
9
10Use this skill when:
11- Building a SOAR playbook that automatically enriches SIEM alerts with threat intelligence context before routing to analysts
12- Creating a Python pipeline for bulk IOC enrichment from phishing email submissions
13- Reducing analyst mean time to triage (MTTT) by pre-populating alert context with VT, Shodan, and MISP data
14
15**Do not use** this skill for fully automated blocking decisions without human review — enrichment automation should inform decisions, not execute blocks autonomously for high-impact actions.
16
17## Detection Gaps & Validation
18
19- **Silent API failures:** a 429 or timeout that returns empty stats reads downstream as a clean IOC. Failed calls must raise/log and trigger fallback, never default `vt_malicious=0`. The `retry_on_429` decorator returns `None` after max retries -- callers must handle that, not treat it as benign.
20- **Missing caching:** re-querying the same IOC burns rate budget (VT free = 4/min) and stalls the pipeline; cache results ~24h keyed by value+type.
21- **Composite-score blind spots:** the weighted formula (VT 60% / AbuseIPDB 40%) over-trusts AV consensus and under-weights shared-infra context -- a CDN IP can score high. Never auto-block on score alone.
22- **Latency cliff:** if enrichment exceeds ~5 min, analysts work unenriched alerts; enforce timeouts and emit partial results.
23
24To validate: run a known-malicious and a known-benign IOC through the pipeline and confirm scores land in the expected High/Medium/Low routing tier; force (or mock) a 429 and confirm the IOC is flagged "enrichment incomplete," not "clean." Track analyst overrides of the composite score weekly as ground truth and re-tune weights; confirm the cache returns identical results for repeat lookups and that rate-limit decorators actually space VT calls to <=4/min.
25
26## Prerequisites
27
28- SOAR platform (Cortex XSOAR, Splunk SOAR, Tines, or n8n) or Python 3.9+ environment
29- API keys: VirusTotal, AbuseIPDB, Shodan, and at minimum one TIP (MISP or OpenCTI)
30- SIEM integration endpoint for alert consumption
31- Rate limit budgets documented per API (VT: 4/min free, 500/min enterprise)
32
33## Workflow
34
35### Step 1: Design Enrichment Pipeline Architecture
36
37Define the enrichment flow for each IOC type:
38```
39SIEM Alert → Extract IOCs → Classify Type → Route to enrichment functions
40 IP Address → AbuseIPDB + Shodan + VirusTotal IP + MISP
41 Domain → VirusTotal Domain + PassiveTotal + Shodan + MISP
42 URL → URLScan.io + VirusTotal URL + Google Safe Browse
43 File Hash → VirusTotal Files + MalwareBazaar + MISP
44→ Aggregate results → Calculate confidence score → Update alert → Notify analyst
45```
46
47### Step 2: Implement Python Enrichment Functions
48
49```python
50import requests
51import time
52from dataclasses import dataclass, field
53from typing import Optional
54
55RATE_LIMIT_DELAY = 0.25 # 4 requests/second for VT free tier
56
57@dataclass
58class EnrichmentResult:
59 ioc_value: str
60 ioc_type: str
61 vt_malicious: int = 0
62 vt_total: int = 0
63 abuse_confidence: int = 0
64 shodan_ports: list = field(default_factory=list)
65 misp_events: list = field(default_factory=list)
66 confidence_score: int = 0
67
68def enrich_ip(ip: str, vt_key: str, abuse_key: str, shodan_key: str) -> EnrichmentResult:
69 result = EnrichmentResult(ip, "ip")
70
71 # VirusTotal IP lookup
72 vt_resp = requests.get(
73 f"https://www.virustotal.com/api/v3/ip_addresses/{ip}",
74 headers={"x-apikey": vt_key}
75 )
76 if vt_resp.status_code == 200:
77 stats = vt_resp.json()["data"]["attributes"]["last_analysis_stats"]
78 result.vt_malicious = stats.get("malicious", 0)
79 result.vt_total = sum(stats.values())
80
81 time.sleep(RATE_LIMIT_DELAY)
82
83 # AbuseIPDB
84 abuse_resp = requests.get(
85 "https://api.abuseipdb.com/api/v2/check",
86 headers={"Key": abuse_key, "Accept": "application/json"},
87 params={"ipAddress": ip, "maxAgeInDays": 90}
88 )
89 if abuse_resp.status_code == 200:
90 result.abuse_confidence = abuse_resp.json()["data"]["abuseConfidenceScore"]
91
92 # Calculate composite confidence score
93 result.confidence_score = min(
94 (result.vt_malicious / max(result.vt_total, 1)) * 60 +
95 (result.abuse_confidence / 100) * 40, 100
96 )
97
98 return result
99
100def enrich_hash(sha256: str, vt_key: str) -> EnrichmentResult:
101 result = EnrichmentResult(sha256, "sha256")
102 vt_resp = requests.get(
103 f"https://www.virustotal.com/api/v3/files/{sha256}",
104 headers={"x-apikey": vt_key}
105 )
106 if vt_resp.status_code == 200:
107 stats = vt_resp.json()["data"]["attributes"]["last_analysis_stats"]
108 result.vt_malicious = stats.get("malicious", 0)
109 result.vt_total = sum(stats.values())
110 result.confidence_score = int((result.vt_malicious / max(result.vt_total, 1)) * 100)
111 return result
112```
113
114### Step 3: Build SOAR Playbook (Cortex XSOAR)
115
116In Cortex XSOAR, create an enrichment playbook:
1171. **Trigger**: Alert created in SIEM (via webhook or polling)
1182. **Extract IOCs**: Use "Extract Indicators" task with regex patterns for IP, domain, URL, hash
1193. **Parallel enrichment**: Fan-out to multiple enrichment tasks simultaneously
1204. **VT Enrichment**: Call `!vt-file-scan` or `!vt-ip-scan` commands
1215. **AbuseIPDB check**: Call `!abuseipdb-check-ip` command
1226. **MISP Lookup**: Call `!misp-search` for cross-referencing
1237. **Score aggregation**: Python transform task computing composite score
1248. **Conditional routing**: If score ≥70 → High Priority queue; if 40–69 → Medium; <40 → Auto-close with note
1259. **Alert enrichment**: Write enrichment results to alert context for analyst view
126
127### Step 4: Handle Rate Limiting and Failures
128
129```python
130import time
131from functools import wraps
132
133def rate_limited(max_per_second):
134 min_interval = 1.0 / max_per_second
135 def decorator(func):
136 last_called = [0.0]
137 @wraps(func)
138 def wrapper(*args, **kwargs):
139 elapsed = time.time() - last_called[0]
140 wait = min_interval - elapsed
141 if wait > 0:
142 time.sleep(wait)
143 result = func(*args, **kwargs)
144 last_called[0] = time.time()
145 return result
146 return wrapper
147 return decorator
148
149def retry_on_429(max_retries=3):
150 def decorator(func):
151 @wraps(func)
152 def wrapper(*args, **kwargs):
153 for attempt in range(max_retries):
154 response = func(*args, **kwargs)
155 if response.status_code == 429:
156 retry_after = int(response.headers.get("Retry-After", 60))
157 time.sleep(retry_after)
158 else:
159 return response
160 return wrapper
161 return decorator
162```
163
164### Step 5: Metrics and Tuning
165
166Track pipeline performance weekly:
167- **Enrichment latency**: Target <30 seconds from alert trigger to enriched output
168- **API success rate**: Target >99% (identify rate limit or outage events)
169- **True positive rate**: Track analyst overrides of automated confidence scores
170- **Cost**: Track API call volume against budget (VT Enterprise: $X per 1M lookups)
171
172## Key Concepts
173
174| Term | Definition |
175|------|-----------|
176| **SOAR** | Security Orchestration, Automation, and Response — platform for automating security workflows and integrating disparate tools |
177| **Enrichment Playbook** | Automated workflow sequence that adds contextual intelligence to raw security events |
178| **Rate Limiting** | API provider restrictions on request frequency (e.g., VT free: 4 requests/minute); pipelines must respect these limits |
179| **Composite Confidence Score** | Single score aggregating signals from multiple enrichment sources using weighted formula |
180| **Fan-out Pattern** | Parallel execution of multiple enrichment queries simultaneously to minimize total enrichment latency |
181
182## Tools & Systems
183
184- **Cortex XSOAR (Palo Alto)**: Enterprise SOAR with 700+ marketplace integrations including VT, MISP, Shodan, and AbuseIPDB
185- **Splunk SOAR (Phantom)**: SOAR platform with Python-based playbooks; native Splunk SIEM integration
186- **Tines**: No-code SOAR platform with webhook-driven automation; cost-effective for smaller teams
187- **TheHive + Cortex**: Open-source IR/enrichment platform with observable enrichment via Cortex analyzers
188
189## Common Pitfalls
190
191- **Blocking on enrichment latency**: If enrichment takes >5 minutes, analysts start working unenriched alerts, defeating the purpose. Set timeout limits and provide partial results.
192- **No caching**: Querying the same IOC 50 times generates unnecessary API costs. Cache enrichment results for 24 hours by default.
193- **Ignoring API failures silently**: Failed enrichment calls should be logged and trigger fallback logic, not silently produce empty results that appear as clean IOCs.
194- **Automating blocks on enrichment score alone**: Composite scores contain false positives; require human confirmation for blocking decisions against shared infrastructure.