Enrich Alerts with Threat Intel APIs (VirusTotal, AbuseIPDB)
The cat detective checks the neighborhood before filing the report. Is that IP a known troublemaker, or just a tourist? The APIs know.
An alert is a question, not an answer
Picture this: it's Tuesday morning, and your EDR — the agent on every laptop, like CrowdStrike or SentinelOne — fires an alert. One workstation made an outbound connection to 203.0.113.77, an IP address nobody recognizes. Your SIEM — Splunk or Microsoft Sentinel, the big central log brain — dutifully logs the event and pages the SOC queue.
Now what? An IP address by itself tells you almost nothing. It could be a brand-new file-sharing server your company legitimately uses. It could be a command-and-control server run by a ransomware gang. The alert is a question: "Hey, is this bad?" Someone — or something — has to find the answer before this becomes a ServiceNow ticket worth an analyst's morning.
That someone is threat intelligence, and today we're going to automate the asking.
What enrichment actually means
Enrichment is the SOC word for "looking stuff up before you panic." Every alert arrives thin — an IP, a file hash, a domain, a URL. Enrichment bolts extra context onto it:
- VirusTotal — asks 70+ antivirus engines and threat feeds: "Have you seen this file hash / URL / domain / IP before, and did any of you call it malicious?" Free API key, generous enough for a lab.
- AbuseIPDB — a community-powered rap sheet for IP addresses: "Has anyone reported this IP for abuse, and how confident are we it's up to no good?" Free API key, 1,000 lookups a day.
Where does this sit in a real team's workflow? The EDR surfaces the event. Your enrichment script (or SOAR playbook — remember Level 1?) consumes the thin alert, queries the APIs, and attaches a verdict. The SIEM correlates it with everything else it knows. Only alerts that come back "yeah, this one's dirty" get an ITSM ticket in ServiceNow. The rest get quietly logged and closed. That's how you turn 10,000 alarms into 10 tickets that matter.
Never open a ticket on a raw indicator. If you can't answer "what do three independent sources say about this IP/hash/domain?" you don't have an incident — you have a question mark with a deadline.
The lab: a 40-line neighborhood check
Here's the plan. We'll write a small Python script that takes a suspicious indicator, asks both APIs, and prints a plain-English verdict. Free accounts on both sites give you API keys in about five minutes — store them as environment variables, never hardcode them.
Step 1 — VirusTotal. Hit their v3 API with the hash or IP. The response includes how many engines flagged it:
import os, requests
VT_KEY = os.environ["VT_API_KEY"]
def vt_reputation(indicator):
url = f"https://www.virustotal.com/api/v3/ip_addresses/{indicator}"
r = requests.get(url, headers={"x-apikey": VT_KEY}, timeout=15)
r.raise_for_status()
stats = r.json()["data"]["attributes"]["last_analysis_stats"]
return stats # {"harmless": 70, "malicious": 12, "suspicious": 3, ...}
Step 2 — AbuseIPDB. Their check endpoint returns a confidence score from 0 to 100 that the IP is abusive, plus how many people reported it and for what:
ABUSE_KEY = os.environ["ABUSEIPDB_API_KEY"]
def abuseipdb_score(ip):
url = "https://api.abuseipdb.com/api/v2/check"
r = requests.get(url,
headers={"Key": ABUSE_KEY, "Accept": "application/json"},
params={"ipAddress": ip, "maxAgeInDays": 90},
timeout=15)
r.raise_for_status()
data = r.json()["data"]
return data["abuseConfidenceScore"], data["totalReports"]
Step 3 — the verdict. Combine the two answers into a decision a human can act on — this is the part that would feed the ServiceNow ticket:
def verdict(ip):
vt = vt_reputation(ip)
score, reports = abuseipdb_score(ip)
if vt["malicious"] >= 5 or score >= 75:
return f"OPEN TICKET — {ip}: {vt['malicious']} VT hits, AbuseIPDB {score}%"
if vt["suspicious"] > 0 or score >= 25:
return f"WATCH — {ip}: mild signals, log and re-check in 24h"
return f"CLEAR — {ip}: nothing dirty, auto-close"
Where each tool fits (the full picture)
Zoom out and here's the pipeline a real team runs every day:
- EDR (CrowdStrike / SentinelOne) sees the laptop talk to a weird IP and fires the alert.
- SIEM (Splunk / Microsoft Sentinel) ingests the alert, correlates it with firewall logs and auth events, and hands the thin indicator to the enrichment step.
- Enrichment (this lab's script, or a SOAR playbook) asks VirusTotal and AbuseIPDB what the world knows about the indicator.
- ITSM (ServiceNow) receives a ticket — but only for the alerts that came back dirty, pre-filled with the VT hits and the AbuseIPDB score so the analyst starts informed, not blind.
Notice what changed: the analyst no longer opens a browser, Googles the IP, squints at three tabs, and writes a ticket from scratch. The script did the legwork. The human does the judgment.
The gotchas (because there are always gotchas)
- Rate limits. Free VirusTotal keys allow 4 lookups a minute; AbuseIPDB gives you 1,000 a day. That's plenty for a lab, but a production SOAR caches answers so it never asks the same question twice. Don't be the script that burns its quota by breakfast.
- False positives are real. Shared hosting means a malicious site can share an IP with a bakery. A low AbuseIPDB score on an IP hosting 200 domains deserves a raised eyebrow, not a siren. Enrichment informs the ticket — it doesn't replace the analyst reading it.
- Don't upload secrets. VirusTotal is a public-ish service — samples you upload can be seen by other users. Never send it internal files, configs, or anything with credentials. Query by hash, not by uploading the file, when the file is yours.
Try it yourself
Grab free API keys from VirusTotal and AbuseIPDB, set the environment variables, and run the verdict function against a few IPs from your own firewall logs (start with the ones your firewall already blocked — those are the interesting ones). Count how many come back CLEAR. That's the number of alerts that never needed a human. Now imagine doing that by hand, 10,000 times a day. Yeah.
Next in this series at Level 9: tune a noisy SIEM rule without blinding yourself. Enrichment taught us to ask better questions — next we learn to stop asking dumb ones.