Known-Bad IOC Feed (STIX)

Every malicious package the VDB detects contributes its C2/exfil infrastructure — domains, IPs, URLs — back into a public threat feed. The detection engine then matches future packages against that feed, so infrastructure burned in one attack flags the next package that reuses it.

This closes a loop between two halves of the system:

registry scanners ──▶ MalwareIoc / MalwareHost ──▶ malscan-stix-processor
        ▲                                                    │
        │                                                    ▼
   detect.Detect  ◀── iocscan Matcher ◀── vulnetix.com/malscan-stix (STIX 2.1)

Publishing — malscan-stix-processor

cmd/malscan-stix-processor runs every 15 minutes. It reads the malware-IOC pipeline and writes per-ecosystem STIX 2.1 bundles to S3 (vdb-manager-artifacts), served unauthenticated at https://vulnetix.com/malscan-stix/…. Two feed kinds are published per ecosystem:

KindIndicatorsSource tables
dnsMalicious domain-name + ipv4-addr / ipv6-addrMalwareHost
urlsMalicious C2 / exfil urlMalwareIoc + OsmThreatIoc

Each indicator is a STIX indicator object with a pattern such as [domain-name:value = '…'], plus name, description, labels (carrying severity:* and ecosystem:*), external_references (the originating advisory) and valid_from.

Composite ecosystems (e.g. npm,pypi) fan out per ecosystem; anything outside a recognised package ecosystem lands in generic. Every feed is accompanied by a sibling <feed>.sha256, and a top-level index.json manifest lists every published feed with its URL, sha256 and indicator count. Feeds are deterministic — a file’s bytes change only when its indicator set changes.

Consuming — iocscan in the engine

The engine’s iocscan package consumes those feeds. A FeedLoader fetches index.json and the requested ecosystem’s dns + urls bundles (plus the always-included generic feed), caching each to the OS temp directory:

  • The cache filename encodes a timestamp; freshness is read from it via a glob with a configurable 1-hour TTL.
  • Fetched bytes are sha256-verified against the manifest before use.
  • If a refetch fails (offline) or its checksum mismatches, the newest cached copy is used and a warning is returned in the result (the engine owns no logger). With no cache at all, the load errors.

The parsed indicators become an in-memory IndicatorSet with O(1) domain/IP/URL lookups. A Matcher then scans content — line-by-line over source text (with surrounding context lines), and optionally string-extracted from ELF/binary files — emitting evidence for any file that references a known-bad indicator, with full provenance: file path, line/byte offset, the matched line, and the STIX indicator’s name, severity and external references.

Wiring — every registry scanner

Each of the 16 registry processors loads the IndicatorSet once per run (via the internal/malscan helper, gated by the ioc-scan capability) and reuses one Matcher for every package — so per-package matching is an in-memory set lookup with no extra network or disk I/O. A match:

  1. Mints — it is folded into the finding set as evidence (CWE-506) before the combination gate runs, so a package phoning a known C2 is flagged exactly like an inline payload.
  2. Records — the matched value is persisted as a MalwareIoc row whose references carry the file/line and the STIX provenance — which the publisher then re-exports on its next run.

The same Matcher / Options.Set API is available to other callers (the CLI, ad-hoc scans); see the engine’s iocscan package. IOCs embedded in shipped binaries are supported by the engine (MatchBytes / Scan with BinaryAnalysis) but are only reached when a processor extracts package files to disk.