Detectors
Every detector family and rule id vulnetix malscan can report, which ecosystems each runs for, its class, and the capability keys that turn detectors off.
The rule ids below are those of malscan-engine v0.6.1, the version cli@2449840 ships. A finding’s ruleId in SARIF is one of them.
Families
| Family | Rule ids | Reads | Ecosystems |
|---|---|---|---|
| iocscan | IOC-STIX-MATCH |
Every file in each install directory up to --max-file-size; printable strings of binaries unless --no-binary-analysis |
All |
| detect | Pattern rules (P-…, G-…, IS-G-…, and ecosystem-prefixed ones such as PHP-…, GO-…, MAVEN-…), shell analysis (SA-…, IS-SA-…), behavioural (JS-…, PY-…, GEM-…, CARGO-…), P-ONION-C2, P-HOMOGRAPH-MIXED-SCRIPT, P-IDN-PUNYCODE-HOST |
Each installed package’s primary manifest and install scripts | JavaScript, Python, Rust, Ruby, PHP |
| badhash | B-KNOWN-BAD-HASH |
Hashes declared in the manifest and install scripts | JavaScript, Python, Rust, Ruby, PHP |
| ioc | (no findings) | Extracts indicators from a package already judged malicious, for the upload | JavaScript, Python, Rust, Ruby, PHP |
Go, Conda, Java, .NET, Dart, Elixir and the --include-system binary directories get iocscan only: no manifest is read for them, so no detect, badhash or ioc finding is possible there.
Files the detect family reads
| Ecosystem | Primary manifest | Install scripts |
|---|---|---|
| JavaScript | package.json |
Its preinstall, install, postinstall, preuninstall, postuninstall, prepare and prepublish scripts |
| Python | setup.py, pyproject.toml, PKG-INFO |
none |
| Rust | Cargo.toml |
build.rs |
| Ruby | *.gemspec |
extconf.rb |
| PHP | composer.json |
none |
Each file is read up to 512 KiB. At most 4,000 packages are analysed per run across all directories; past that the report warns manifest detector cap (4000) reached and iocscan still covers every file.
IOC scan
IOC-STIX-MATCH is one rule for every indicator hit. The finding names the indicator type (domain, ipv4, ipv6 or url) and value, the file and line with three lines of context either side, and the STIX references. Its class depends on the file (Detection model); an evidence hit takes the indicator’s severity when the feed gives one.
Indicators come from:
| Source | Refresh | Cache |
|---|---|---|
Vulnetix STIX index, https://vulnetix.com/malscan-stix/index.json |
Each scan when the cached copy is older than 1 hour; --fetch-definitions forces it |
OS temp directory |
TweetFeed rolling 30-day STIX, https://raw.githubusercontent.com/0xDanielLopez/TweetFeed/master/stix/month.json |
24 hours | OS temp directory |
| badnet blocklists (IPv4, IPv6 and domains are matched; the email list is not used by the file scan) | Embedded in the CLI; --fetch-definitions adds a refreshed overlay |
<user cache>/vulnetix/malscan/definitions |
--no-ioc-feeds skips the whole IOC scan, including the badnet lists. If the feeds cannot be downloaded the scan continues with a warning iocscan(<eco>): feeds unavailable and detect and badhash still run.
Shell analysis
Runs over the manifest and, with an IS- prefix, over the install scripts.
| Rule | Class | Reports |
|---|---|---|
SA-VAR-CONCAT-EXEC |
evidence | Variable concatenation that resolves to a download piped to an executor |
SA-VAR-CONCAT-CMD |
evidence | Variable concatenation that resolves to a suspicious command |
SA-INDIRECT-EXEC |
evidence | A variable holding a command, used in execution position |
SA-CHARBYCHAR-CONSTRUCT |
evidence | Many printf/echo subshells building a command character by character |
SA-BINARY-DOWNLOAD-NOCOMPILE |
evidence | A download made executable with chmod +x and no build step |
SA-HIGH-ENTROPY-HEREDOC |
trigger | A heredoc of high entropy (an encoded payload) |
SA-DATA-BLOB-HEX |
context | A long embedded hex string |
SA-DATA-BLOB-BASE64 |
context | A long embedded base64 string |
Behavioural detectors
Multi-line sequences a single pattern cannot express. Capability bad-actor-behaviors.
| Rule | Ecosystem | Class | Reports |
|---|---|---|---|
JS-INSTALL-REMOTE-IMPORT |
npm | evidence | An install hook that require()s or imports from a remote https URL |
JS-HOOK-DECODE-EGRESS-SEQ |
npm | trigger | An install hook that decodes a payload (atob, Buffer.from) and opens a network connection |
PY-PTH-AUTOIMPORT-PERSISTENCE |
PyPI | evidence | setup.py writing a .pth file that contains an import line, which Python runs at every start |
PY-SETUP-CRED-EXFIL |
PyPI | evidence | setup.py opening ~/.aws/credentials, ~/.ssh/id_rsa, ~/.docker/config.json or ~/.netrc and making a network call |
PY-SETUP-IMPORTLIB-EXEC |
PyPI | evidence | setup.py loading a module with importlib/imp and passing it to exec, compile or eval |
GEM-GLOBAL-INSTALL-HOOK |
RubyGems | evidence | A gem registering Gem.post_install_hooks or Gem.pre_install_hooks |
CARGO-CONFIG-PERSISTENCE |
Cargo | evidence | build.rs writing ~/.cargo/config.toml or .cargo/config.toml |
The engine also has NUGET-ASSEMBLYLOAD-EGRESS for NuGet install hooks, but the CLI reads no NuGet manifests, so it never fires in vulnetix malscan.
Other detectors
| Rule | Class | Capability | Reports |
|---|---|---|---|
P-ONION-C2 |
evidence | onion-c2 |
A Tor .onion address in the manifest or install scripts |
P-HOMOGRAPH-MIXED-SCRIPT |
evidence | homograph |
A hostname label in a URL that mixes scripts (Latin with Cyrillic or Greek look-alikes) |
P-IDN-PUNYCODE-HOST |
context | homograph |
An IDN (xn--) host in a URL |
B-KNOWN-BAD-HASH |
evidence | (always on in the CLI) | A declared hash on the known-bad list: the engine’s embedded seed plus TweetFeed’s SHA-256 and MD5 file hashes |
The pattern rules, 306 regular expressions in four sections, are listed on Pattern rules.
The engine’s registry-metadata, name, checksum-array and git-history detectors (M-…, T-…, B-MAINTAINER-…, B-TYPOSQUAT, B-NAME-IMPERSONATE, B-ORPHAN-TAKEOVER, P-NO-CHECKSUMS and similar) need data a local install directory does not hold, or apply to AUR packages only. They run in the Vulnetix VDB pipeline, not in the CLI.
Turning detectors off
Each ecosystem has a capability map. The CLI resolves it per install directory from two layers:
- The defaults embedded in the engine (
config/defaults/<slug>.json). Atv0.6.1every capability is on for every ecosystem. - An override file
<dir>/<slug>.json, where<dir>is--catalog, else$MALSCAN_CONFIG_DIR, else<user config dir>/malscan-engine(~/.config/malscan-engineon Linux).
The override is merged key by key, so a file need list only what it turns off. A key that is absent stays on.
{ "ecosystem": "npm", "capabilities": { "gtfobins": false, "shell-obfuscation": false } }
mkdir -p ./malscan-config
echo '{"ecosystem":"npm","capabilities":{"gtfobins":false}}' > ./malscan-config/npm.json
vulnetix malscan --catalog ./malscan-config
| Slug | Used for |
|---|---|
npm |
JavaScript |
pypi |
Python, Conda |
cargo |
Rust |
rubygems |
Ruby |
go |
Go |
nuget |
.NET |
generic |
PHP, Java, Dart, Elixir, system binary directories |
| Capability | Gates |
|---|---|
manifest-patterns |
Pattern section pkgbuild_analysis on the primary manifest |
source-url-patterns |
Pattern section source_url_analysis |
gtfobins |
Pattern section gtfobins_analysis, on the manifest and install scripts |
shell-obfuscation |
The SA- shell analysis |
install-script |
Pattern section install_script_analysis and every install-script check |
onion-c2 |
P-ONION-C2 |
homograph |
P-HOMOGRAPH-MIXED-SCRIPT, P-IDN-PUNYCODE-HOST |
bad-actor-behaviors |
The behavioural detectors |
The engine also defines ioc-scan, badhash and ioc-extract, but the CLI does not consult them: use --no-ioc-feeds to skip the IOC scan. The remaining keys (checksum, name-typosquat, bin-source, manifest-diff, orphan-takeover, git-history, metadata-reputation, maintainer-batch, github-stars, registry-comments, ownership-hijack, goodkeys) gate detectors that do not fire in a local scan.