Skip to content

Threat-intel definitions

Where malscan's known-bad indicators come from, how --fetch-definitions refreshes them, running offline, and the feeds.json format for your own blocklists.

Updated 02 Nov 2026Checked against cli@2449840, malscan-engine@3dfe895

The IOC scan matches files against three indicator sets. None needs an account.

Set Contents Where it comes from Refreshed
Vulnetix STIX Domains, IPs and URLs from Vulnetix malware advisories https://vulnetix.com/malscan-stix/index.json and the feeds it lists On a scan, when the cached copy is over 1 hour old
TweetFeed A rolling 30 days of community-reported IOCs, plus SHA-256 and MD5 file hashes used by badhash https://raw.githubusercontent.com/0xDanielLopez/TweetFeed/master/stix/month.json On a scan, when over 24 hours old
badnet Bad IPv4, IPv6 and domains aggregated from 17 public blocklists Embedded in the CLI at build time, plus an overlay you refresh Only by --fetch-definitions

STIX and TweetFeed copies are cached in the OS temp directory. The badnet overlay is written to <user cache>/vulnetix/malscan/definitions (~/.cache/vulnetix/malscan/definitions on Linux), or to $VULNETIX_MALSCAN_DEFS_DIR when set, as bad-ipv4.txt, bad-ipv6.txt, bad-domains.txt and bad-emails.txt. Every scan loads the overlay on top of the embedded lists.

Refresh

vulnetix malscan --fetch-definitions

This downloads every enabled badnet feed, merges the result with the definitions already on disk (so a feed that is down this time loses nothing), writes the overlay, then force-refreshes the STIX index and TweetFeed. It scans nothing and exits.

Fetching malscan threat-intel definitions → /home/dev/.cache/vulnetix/malscan/definitions
  ok   dshield-block                  ipv4=20     ipv6=0    domains=0       emails=0
  warn binarydefense-banlist          context deadline exceeded
Definitions updated: ipv4=… ipv6=… domains=… emails=… (3 files changed)
Refreshing STIX feeds (vulnetix index + TweetFeed)…
STIX feeds refreshed (0 warning(s)).

The whole refresh has a 5-minute timeout. It fails only when no badnet feed could be fetched (no threat-intel feeds could be fetched … definitions left unchanged); a failed STIX refresh keeps the cached copy and prints a warning.

Run it on a schedule, for example in the job that builds your CI image, so scans do not depend on live downloads.

Offline and air-gapped

vulnetix malscan --no-ioc-feeds

skips the IOC scan entirely: no download, and no badnet or STIX matching. The detect and badhash families still run over manifests and install scripts. The report’s headline reads INCOMPLETE when no indicators were loaded unless you chose --no-ioc-feeds.

When the feeds cannot be reached, a scan falls back to the newest cached STIX copy with a stale-cache warning, and skips a feed it has never cached. To keep IOC matching in an air-gapped environment, run --fetch-definitions on a connected machine and copy the definitions directory to the same path on the target (or point VULNETIX_MALSCAN_DEFS_DIR at it). The STIX caches are in the OS temp directory, which is often cleared, so rely on the badnet set there.

The default badnet feeds

dshield-block, dshield-ipsascii, crowdsec-intelligence, dandelionsprout-antimalware, feodotracker-recommended, urlhaus-hosts, isc-intelfeed, isc-cloudips, firehol-level3, binarydefense-banlist, aper-phishing-reply, projecthoneypot-p-rss, projecthoneypot-rss, cinsscore-badguys, alienvault-generic, alienvault-data, bruteforceblocker.

Your own feeds

--feeds <file> replaces the default feed list for one --fetch-definitions run. It is refused without --fetch-definitions (--feeds is only used with --fetch-definitions). It changes the badnet refresh only; the STIX refresh is unchanged.

vulnetix malscan --fetch-definitions --feeds ./feeds.json
{
  "schema_version": "badnet-feeds/v1",
  "feeds": [
    {
      "key": "corp-blocked-hosts",
      "name": "Security team blocked hosts",
      "url": "https://intel.example.com/blocked-hosts.txt",
      "parser": "hosts",
      "enabled": true
    }
  ]
}
Field Required Meaning
schema_version no badnet-feeds/v1
feeds yes Array of feed sources
feeds[].key yes Short id, shown in the fetch output. An entry without key or url is skipped.
feeds[].url yes HTTP or HTTPS URL
feeds[].parser yes One of the parsers below. An unknown parser fails the whole file.
feeds[].enabled no Default true. false keeps a source documented but unused.
feeds[].name, feeds[].detail no Labels; ignored by the fetcher

A file with no enabled feed is an error (no enabled feeds).

Parsers

Parser For Keeps
iplist One IP per line, comments and trailing text tolerated Single IPv4 and IPv6 addresses; CIDR ranges are skipped
netset FireHOL .netset files As iplist
hosts Hosts-file format (0.0.0.0 bad.example) The hostnames; the sinkhole IP is ignored
rss RSS or XML feeds IPs, domains and emails in titles, descriptions and CDATA
emails Lists of email addresses Email addresses
mixed Free text IPs, URLs (their hosts), domains and emails
misp MISP event JSON Event.Attribute[] values of types such as ip-src, ip-dst, domain, hostname, url, domain|ip, email-src, email-dst; attributes with to_ids: false or a benign reputation tag are skipped

Every parsed value goes through the shared allowlist before it is kept: reserved and documentation IPs, localhost, and well-known registry, CDN and documentation domains are dropped, so a feed cannot make registry.npmjs.org an indicator.

Edit this pageSources: cli/cmd/malscan.go, malscan-engine/iocscan/feed.go, malscan-engine/badnet/badnet.go, malscan-engine/badnet/feeds.go, malscan-engine/badnet/data/feeds.json, malscan-engine/badnet/parse.go, malscan-engine/allow/allow.go
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.