An AI-powered agent that pulls all available intel on an Indicator of Compromise.
Give it an IP, domain, URL, or file hash. It queries nine threat-intelligence sources in parallel, then uses Claude to correlate the results into an analyst-grade verdict with a risk score, key findings, attribution, and recommended SOC actions.
Built for blue teams, SOC analysts, and DFIR. Every lookup is a read-only reputation query — this is a defensive tool.
======================================================================
IOC ENRICHMENT REPORT
======================================================================
Indicator : 45.142.212.61
Type : ip
VERDICT : MALICIOUS risk 92/100 confidence: high
Engine : Claude (claude-sonnet-4-6)
This IP is a confirmed Cobalt Strike C2 node. 14 VirusTotal engines
flag it, AbuseIPDB shows 96% abuse confidence across 412 reports, and
ThreatFox links it directly to an active campaign. The hosting ASN is
a known bulletproof provider and the IP has no reverse DNS.
Key findings:
- VirusTotal: 14 engines flag as malicious
- ThreatFox: direct Cobalt Strike C2 match
- AbuseIPDB: 96% abuse confidence (412 reports)
- GreyNoise: classified malicious, actively scanning
Recommended actions:
- Block at perimeter firewall and proxy immediately
- Hunt retrospectively across DNS/proxy/EDR for the last 90 days
- Escalate to IR if any internal host contacted this IP
Attribution: Cobalt Strike
- Features
- Quick start
- Getting API keys
- Usage
- Understanding the output
- Use it as a library
- Adding your own intel source
- Project layout
- Development
- Troubleshooting
- Responsible use
- License
- Auto-detects the IOC type — IPv4, IPv6, domain, URL, MD5, SHA1, SHA256.
- Handles defanged indicators — paste
hxxps://evil[.]comstraight from a ticket and it re-fangs it for you. - Nine sources, queried concurrently — the whole investigation takes seconds, not a minute.
- Works with zero API keys — sources you haven't configured are cleanly skipped. DNS and RDAP/WHOIS need no key at all, so you always get something.
- AI synthesis via Claude — correlates signals across sources into a verdict a human can act on. Falls back to transparent rule-based scoring automatically.
- Batch mode — feed it a file of indicators from an alert dump.
- Machine-readable output — JSON for your SIEM/SOAR, Markdown for your ticket.
- Severity-based exit codes — drops straight into a pipeline.
| Source | What it tells you | Applies to | Key needed |
|---|---|---|---|
| VirusTotal | Multi-engine AV verdicts, reputation, tags | all | optional |
| AbuseIPDB | Crowdsourced abuse reports, confidence score | IP | optional |
| AlienVault OTX | Campaign/malware pulses, adversary links | all | optional |
| ThreatFox (abuse.ch) | Direct malware & C2 IOC matches | all | optional |
| GreyNoise | Internet background noise vs. targeted; known-good services | IP | optional |
| Shodan | Exposed ports, banners, known CVEs | IP | optional |
| urlscan.io | Historical sandbox scans & verdicts | URL/domain/IP | optional |
| RDAP / WHOIS | Registration date, registrar, owner | IP/domain/URL | none |
| DNS | Forward & reverse resolution | IP/domain/URL | none |
git clone https://github.com/<your-username>/ioc-agent.git
cd ioc-agent
pip install -r requirements.txtWindows / PowerShell users — click here
If you have several Python installs, call the interpreter explicitly so the package lands in the right place:
git clone https://github.com/<your-username>/ioc-agent.git
cd ioc-agent
# Use the SAME python you'll run the tool with
python -m pip install -r requirements.txt
# If `python` isn't on your PATH, call the interpreter by its full path, e.g.:
& "C:\Users\<you>\AppData\Local\Programs\Python\Python312\python.exe" -m pip install -r requirements.txt
# Then run it (note: the indicator is a required argument!)
python -m ioc_agent 8.8.8.8 --no-aiA handy session alias so you don't retype a long interpreter path:
Set-Alias py "C:\Users\<you>\AppData\Local\Programs\Python\Python312\python.exe"
py -m ioc_agent 8.8.8.8 --no-aipython -m ioc_agent 8.8.8.8 --no-aiThis proves the install works. You'll get DNS + WHOIS data and a rule-based verdict; every key-gated source will politely report itself as skipped.
cp .env.example .env # Windows: copy .env.example .envOpen .env and paste in whatever keys you have. All of them are optional —
add one, add all eight, the agent adapts. .env is already gitignored, so your
secrets never reach GitHub.
python -m ioc_agent 45.142.212.61Drop --no-ai and, with ANTHROPIC_API_KEY set, Claude will synthesise the
findings into a proper analyst verdict.
Every one of these has a free tier that's plenty for individual use.
| Key | Where to get it |
|---|---|
ANTHROPIC_API_KEY |
https://console.anthropic.com/settings/keys |
VT_API_KEY |
https://www.virustotal.com/gui/my-apikey |
ABUSEIPDB_API_KEY |
https://www.abuseipdb.com/account/api |
OTX_API_KEY |
https://otx.alienvault.com/api |
THREATFOX_API_KEY |
https://auth.abuse.ch |
GREYNOISE_API_KEY |
https://viz.greynoise.io/signup |
SHODAN_API_KEY |
https://account.shodan.io |
URLSCAN_API_KEY |
https://urlscan.io/user/signup |
Where to start: if you only get one, get VirusTotal. If you get two, add
AbuseIPDB (for IPs). Add ANTHROPIC_API_KEY to turn on the AI synthesis,
which is where the tool goes from "a list of API responses" to "an answer".
# Basic — any IOC type is auto-detected
python -m ioc_agent 8.8.8.8
python -m ioc_agent evil-domain.com
python -m ioc_agent "https://sketchy.site/payload" # quote URLs!
python -m ioc_agent 44d88612fea8a8f36de82e1278abb02f
# Defanged input works too
python -m ioc_agent "hxxps://evil[.]com/beacon"
# Skip the AI, use deterministic rule-based scoring
python -m ioc_agent 8.8.8.8 --no-ai
# Use a more capable model for a hard case
python -m ioc_agent 8.8.8.8 --model claude-opus-4-8
# Export
python -m ioc_agent evil.com --json report.json --markdown report.md
# Batch mode — one indicator per line, # comments allowed
python -m ioc_agent --file iocs.txt --json batch.json --quiet| Flag | Description |
|---|---|
ioc |
The indicator to investigate (positional, required unless --file) |
-f, --file PATH |
Batch mode: a text file of indicators, one per line |
--no-ai |
Skip Claude; use the built-in rule-based scoring |
--model NAME |
Claude model (default claude-sonnet-4-6) |
--json PATH |
Write the full report as JSON |
--markdown PATH |
Write a Markdown report |
-q, --quiet |
Suppress the console report |
--env PATH |
Path to your .env file (default ./.env) |
-V, --version |
Print version |
Designed for automation — pipe it into a SOAR playbook or a CI gate:
| Code | Meaning |
|---|---|
0 |
Benign / low risk / insufficient data |
1 |
Suspicious |
2 |
Malicious |
3 |
Usage error |
python -m ioc_agent "$SUSPECT_IP" --quiet --json out.json
if [ $? -eq 2 ]; then
echo "Malicious — blocking at firewall"
./block_ip.sh "$SUSPECT_IP"
fiVerdict — one of MALICIOUS, SUSPICIOUS, LOW RISK, BENIGN, or
INSUFFICIENT DATA. That last one is important and honest: if you've configured
no keys, the tool tells you it doesn't know rather than guessing.
Risk score (0–100) — with the AI engine, Claude's holistic judgement. With
the rule-based engine, a transparent weighted sum you can read and tune in
ioc_agent/synthesis.py.
Engine — tells you which produced the verdict, so you always know whether you're reading AI reasoning or deterministic scoring.
⚠️ The verdict is decision support, not ground truth. Before you block something in production, read the Source Detail section — every raw signal that fed the verdict is printed there. Both engines can be wrong; the raw data is what you defend in a post-incident review.
from ioc_agent import investigate
report = investigate("8.8.8.8")
print(report["verdict"]["verdict"]) # "BENIGN"
print(report["verdict"]["risk_score"]) # 0
print(report["verdict"]["key_findings"]) # [...]
for source in report["sources"]:
if source["data"]:
print(source["source"], source["data"])
# Skip the AI call
report = investigate("evil.com", use_ai=False)This is the part designed to be extended. Open
ioc_agent/sources.py and write one function:
def enrich_mysource(ioc: str, ioc_type: str) -> dict:
# 1. Bail out politely if this source doesn't apply to this IOC type
if ioc_type != "ip":
return result("MySource", skipped=True, error="IPs only")
# 2. Bail out politely if the user hasn't configured a key
api_key = _key("MYSOURCE_API_KEY")
if not api_key:
return result("MySource", skipped=True, error="MYSOURCE_API_KEY not set")
# 3. Query it. NEVER let an exception escape — degrade, don't crash.
try:
data = _get(f"https://api.mysource.com/v1/{ioc}",
headers={"Authorization": f"Bearer {api_key}"})
return result("MySource", {
"score": data.get("score"),
"categories": data.get("categories", []),
})
except Exception as e:
return result("MySource", error=str(e))Then add it to the SOURCES list at the bottom of the file:
SOURCES = [
enrich_virustotal,
...
enrich_mysource, # <- your new source
]That's the entire change. Concurrency, console/JSON/Markdown rendering, and the AI synthesis all pick it up automatically. Good candidates to add: Censys, Pulsedive, MISP, MalwareBazaar, Recorded Future, or your own internal TIP.
ioc-agent/
├── ioc_agent/
│ ├── __init__.py # public API: investigate()
│ ├── __main__.py # enables `python -m ioc_agent`
│ ├── cli.py # arg parsing, .env loading, batch mode, exit codes
│ ├── detect.py # IOC type detection + defanging
│ ├── sources.py # ← ALL intel providers live here. Add yours here.
│ ├── agent.py # runs every source concurrently
│ ├── synthesis.py # Claude verdict + rule-based fallback
│ └── report.py # console / JSON / Markdown rendering
├── tests/ # offline unit tests (no network, no keys)
├── .github/workflows/ # CI: pytest on Python 3.9–3.13
├── .env.example # copy to .env and fill in your keys
├── iocs.example.txt # sample batch input
├── pyproject.toml
└── requirements.txt
pip install -r requirements-dev.txt
pytest -vTests are fully offline — they use mocked source data, so they need no API keys and no network. CI runs them on Python 3.9 through 3.13 on every push.
Install as an editable package to get the ioc-agent command globally:
pip install -e .
ioc-agent 8.8.8.8Missing dependency: pip install requests
requests isn't installed for the interpreter you're running. Install it into
that specific Python: python -m pip install requests (use the full path to
the interpreter if you have several installs).
error: the following arguments are required: ioc
You ran it with no indicator. It needs one: python -m ioc_agent 8.8.8.8.
Everything says "skipped"
You haven't configured any API keys. That's expected on a fresh clone — copy
.env.example to .env and add at least a VirusTotal key.
403 from urlscan.io or RDAP
Usually rate limiting or a restricted network. Add a URLSCAN_API_KEY, or just
ignore it — the other sources carry the report.
PowerShell mangles my URL
Quote it: python -m ioc_agent "https://evil.com/x?a=1". The &, ?, and =
characters are special to the shell.
This is a defensive tool. It performs read-only reputation lookups against public threat-intelligence services — it does not scan, probe, or interact with the indicators themselves.
- Respect each provider's rate limits and terms of service.
- Don't submit confidential or classified indicators to third-party APIs unless your organisation permits it — a lookup can reveal what you're investigating.
- Verify before you block. Check the raw Source Detail behind every verdict.
MIT — see LICENSE. Use it, fork it, ship it.