Skip to main content

VirusTotal IOC Enrichment Tool for SOC/DFIR workflows

Project description

vex logo

vex

IOC enrichment hub for SOC triage and DFIR — VirusTotal + AbuseIPDB / Shodan / WHOIS / MISP / OpenCTI, straight from your terminal.

 ██╗   ██╗███████╗██╗  ██╗
 ██║   ██║██╔════╝╚██╗██╔╝
 ██║   ██║█████╗   ╚███╔╝
 ╚██╗ ██╔╝██╔══╝   ██╔██╗
  ╚████╔╝ ███████╗██╔╝ ██╗
   ╚═══╝  ╚══════╝╚═╝  ╚═╝

PyPI Python License


vex takes an indicator (a hash, IP, domain, or URL) and turns it into a verdict with context. It started as a VirusTotal CLI and grew into an enrichment hub: one primary source (VirusTotal) plus optional secondary sources that add reputation and your own threat-intel. It runs standalone, and it slots into a pipeline (barb → vex → sift) over stdin/stdout.

[!NOTE] Current release: vex 1.5.0 (on PyPI). Full manual: docs/.

Install

pip install vex-ioc          # core
pip install vex-ioc[ai]      # + Anthropic / OpenAI explanations (Ollama needs no extra)

Quickstart

vex config --set-api-key YOUR_VT_KEY     # or set VT_API_KEY
vex triage 8.8.8.8
vex triage evil.com --explain            # add an AI narrative
vex investigate <sha256> -o rich         # deep dive
IOC: 8.8.8.8 (ipv4)
Verdict: CLEAN
Detections: 0 malicious / 0 suspicious / 94 engines
Reputation: 0

What you get

  • Two modestriage (fast verdict, minimal API calls) and investigate (deep DFIR: PE info, sandbox behavior, passive DNS, relationships, MITRE ATT&CK, timeline).
  • Multi-source enrichment — VirusTotal primary; AbuseIPDB, Shodan, WHOIS, MISP, and OpenCTI as secondary enrichers (key-gated, fail-open).
  • Batch correlation--correlate clusters a batch of IOCs by shared infrastructure (ASN, malware family, contacted IPs/domains).
  • AI explanations — opt-in --explain narratives (Claude / OpenAI / local Ollama / deterministic template), with prompt-injection defense on the untrusted data fed to the model.
  • Pipeline-ready output — JSON, NDJSON (streaming), CSV, STIX 2.1, ATT&CK Navigator, self-contained HTML; verdict-mapped exit codes.
  • Quota-aware batches — IOC dedup, an up-front ETA, and a --max-quota budget guard.
  • Local knowledge basevex tag / note / watchlist annotate IOCs in ~/.vex/.

Verdicts

Verdict Meaning
🟢 CLEAN No engine flagged it.
🟡 UNKNOWN Not enough data, or the IOC was not found.
🟠 SUSPICIOUS Low-confidence detections.
🔴 MALICIOUS Meets the malicious-detection threshold.

Enrichment sources

VirusTotal is the primary source; it produces the verdict. The others are secondary enrichers that augment an investigate result.

Source IOC types Enable via
VirusTotal all VT_API_KEY / --api-key / vex config
AbuseIPDB IP VEX_ABUSEIPDB_API_KEY
Shodan IP VEX_SHODAN_API_KEY
WHOIS domain core (no key)
MISP all MISP_URL + MISP_API_KEY
OpenCTI all OPENCTI_URL + OPENCTI_TOKEN

[!IMPORTANT] Secondary enrichers run on investigate only, not triage (triage stays fast). Each is no-op without its key, fail-open (a failure or timeout never blocks the run), and they run concurrently. Run vex doctor --probe to see which are configured and reachable.

Correlation

vex triage -f iocs.txt --correlate            # deterministic cluster table
vex triage -f iocs.txt --correlate --explain  # + per-cluster AI campaign narrative

--correlate groups a batch by shared ASN, malware family, contacted IPs, and contacted domains — surfacing likely campaigns. It is deterministic by default; the AI narrative is added only when --explain is also set.

AI explanations

--explain adds a narrative and next steps; --explain-model overrides the model. Providers: Anthropic, OpenAI, local Ollama, and a deterministic template fallback when none is configured.

export VEX_AI_PROVIDER=ollama        # local, no cloud key
vex investigate evil.com --explain

[!WARNING] Enrichment data (sandbox strings, file names, family labels) is attacker-influenceable. Before it reaches an LLM, vex scans it for prompt-injection patterns and redacts critical attempts, and the system prompt instructs the model to treat the data as untrusted. This is defense-in-depth, not a guarantee.

Output formats

-o rich (default TTY) · -o console · -o json · -o ndjson (one result per line, streamed) · --csv · --stix · --navigator (ATT&CK layer, investigate) · --html <path> (self-contained report). All formats show real IOCs by default; --defang defangs them (e.g. evil[.]com); HTML reports always defang.

sift triage alerts.json -o json | vex triage --from-sift -o ndjson

Pipeline

flowchart LR
    barb -->|URLs / IOCs| vex
    vex -->|enriched IOCs| sift
    sift -.->|extracted IOCs| vex

vex is the enrichment hub. --from-barb reads barb JSON; --from-sift reads a sift TriageReport, extracts its IOCs, and enriches them — closing the sift ↔ vex loop. NDJSON output and verdict exit codes make it scriptable downstream.

Scale

For large batches, such as IOCs extracted from a SIEM export:

  • Dedup — duplicate IOCs are collapsed before any query, which saves quota; --no-dedup disables it.
  • ETA + counters — an up-front estimate and a processed (N from API, M cached) summary on stderr.
  • --max-quota N — caps fresh API lookups; cached IOCs are still served, the rest are skipped with a notice. Re-run to continue — the SQLite cache resumes where you left off.

Configuration

Priority: --api-key flag → environment variable → ~/.vex/config.yaml → defaults.

vex config --show        # active config, masked secrets
vex doctor               # what's configured (no network)
vex doctor --probe       # + live connectivity, surfaces the actual error
Variable Purpose
VT_API_KEY VirusTotal
VEX_AI_API_KEY / VEX_AI_PROVIDER / VEX_AI_MODEL AI provider
VEX_ABUSEIPDB_API_KEY AbuseIPDB enricher
VEX_SHODAN_API_KEY Shodan enricher
MISP_URL / MISP_API_KEY MISP lookup
OPENCTI_URL / OPENCTI_TOKEN OpenCTI lookup

enrichment.stix_tlp_version (1.0 default, 2.0) selects which STIX TLP marking ids the export emits.

Security

[!WARNING]

  • Secondary enrichers (AbuseIPDB / Shodan / WHOIS / MISP / OpenCTI) fail-open — never block a run — and are no-op without a key.
  • The VT API key is never hard-coded — env var, config, or --api-key.
  • Premium calls are gated behind config.is_premium; the free tier is never broken.
  • Restricted TI: TLP/markings are carried through; marked intel is never emitted unmarked.
  • Machine output stays clean — all notices go to stderr, not stdout.

Exit codes

Computed from the highest-severity verdict in the run:

Code Meaning
0 CLEAN / UNKNOWN
1 SUSPICIOUS — or a runtime error / bad input
2 MALICIOUS

Known limitations

  • MISP (v2.5.38) and OpenCTI (demo v7.26) lookups are live-verified end-to-end, and both have mock tests. They query real instances and stay version-sensitive: if a server's schema differs, the enricher fails open (no enrichment, no crash). Use vex doctor --probe to confirm connectivity. pycti is a possible future fallback if OpenCTI's GraphQL schema drifts.
  • AI: Anthropic and Ollama are live-verified end-to-end; OpenAI shares the same code path (mock-tested). Without a provider, --explain falls back to a deterministic template.
  • Secondary enrichers run on investigate, not triage. A triage --explain gets no AbuseIPDB / Shodan / MISP / OpenCTI context by design.
  • STIX TLP:CLEAR maps to the TLP 1.0 WHITE marking id by default — set enrichment.stix_tlp_version: "2.0" to switch to TLP 2.0 ids.
  • The VirusTotal free tier allows roughly 4 requests/min, so large batches are quota-bound — see Scale.

Docs

Full manual: docs/ — getting started, every command/flag, the enrichment model, output formats, the barb/sift pipeline contracts, and configuration. Built-in guide: vex manual (and vex manual ai / pipeline / config). Changelog: CHANGELOG.md.

Author

Christian Huhn — building security tooling for SOC/DFIR workflows.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vex_ioc-1.6.1.tar.gz (157.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vex_ioc-1.6.1-py3-none-any.whl (109.3 kB view details)

Uploaded Python 3

File details

Details for the file vex_ioc-1.6.1.tar.gz.

File metadata

  • Download URL: vex_ioc-1.6.1.tar.gz
  • Upload date:
  • Size: 157.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for vex_ioc-1.6.1.tar.gz
Algorithm Hash digest
SHA256 01a2a593c5b5f16e4beda50bcad22cabfc93ac03ed6e2438961e71f972753d6c
MD5 2a2a0c996017868523778cf909b7fcdc
BLAKE2b-256 2dd80f264fc47719f93314e5632ba8d0e21529bf328244f716af573588b73093

See more details on using hashes here.

Provenance

The following attestation bundles were made for vex_ioc-1.6.1.tar.gz:

Publisher: publish.yml on duathron/vex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vex_ioc-1.6.1-py3-none-any.whl.

File metadata

  • Download URL: vex_ioc-1.6.1-py3-none-any.whl
  • Upload date:
  • Size: 109.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for vex_ioc-1.6.1-py3-none-any.whl
Algorithm Hash digest
SHA256 346925b0ac769db2d8da9fb5eceea88ad9f3badb6367b30c6215e7986c4ff069
MD5 e72f634ae11831ac566900840971e12e
BLAKE2b-256 fb489bd7fc4410c16518f17b7ca7f095237dde3dc9bb796b247034c1c605ed55

See more details on using hashes here.

Provenance

The following attestation bundles were made for vex_ioc-1.6.1-py3-none-any.whl:

Publisher: publish.yml on duathron/vex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page