MITHRIL(1)

πŸ“¦ version 0.4.0, in nixpkgs unstable

πŸ’– mithril(1) πŸ’–

okay bestie, this is the manual page for mithril β€” she lives in your terminal and she's literally so good at firmware forensics. grab a boba, we're going in 🧚

✨ NAME ✨

mithril β€” analyze firmware contents for secrets, SBOM, CVEs, and licenses πŸ’…

✨ SYNOPSIS ✨

mithril [options] <file|dir>
mithril --fetch-db|--update-db [--with-kernel-feed]

✨ DESCRIPTION ✨

so the way that mithril works is she reads the contents of firmware and IoT software β€” either a single file or an unpacked rootfs β€” and then reports four things as structured findings. not me being obsessed with structured output 😭

  • secrets: embedded credential and key material (API tokens, cloud keys, JWTs, private keys, /etc/shadow hashes), and each one comes with a deterministic offline confidence rating. no vibes-based guessing here, it's giving rigor ✨
  • SBOM: a software bill of materials built from package databases (dpkg/opkg/apk/rpm), language manifests, ELF version strings, versioned libc filenames, and the kernel banner. emitted as CycloneDX and SPDX πŸŽ€
  • CVEs: the SBOM joined against a local offline vulnerability mirror (OSV plus NVD/CPE) and a curated, version- and kconfig-gated kernel-CVE checklist, annotated with CISA KEV and EPSS. yes queen, all of that πŸ”₯
  • licenses: the open-source licenses present, for compliance and provenance πŸ‘‘

mithril pairs with moria(1), which handles structural identification and unpacking. a typical pipeline runs moria -e to unpack an image, then mithril over the extracted tree. ship it 🫢

a scan makes no network calls. the secrets, SBOM, and license passes are fully offline. the CVE pass reads a local mirror that is refreshed out of band (see THE CVE MIRROR below), so it is air-gappable once the mirror is in place. output is human-readable by default; pass -j/--json for a single JSON object designed to be consumed by a tool or model πŸ€–

with no pass flag, every pass runs. naming one or more passes (--secrets, --sbom, --cve, --licenses, --boot, --keys) restricts the run to just those. main character energy: you pick who gets to show up 😌


🌸 ARGUMENTS 🌸

  • <file|dir> β€” a single file, or a directory (typically moria's .extracted/ output) to scan recursively πŸ¦‹

✨ OPTIONS ✨

πŸ’… Passes

  • --secrets β€” scan file content for credential and key material πŸ”‘
  • --sbom β€” inventory software components and their versions πŸ“¦
  • --cve β€” match the detected components against known vulnerabilities in the local mirror. needs the mirror (see --fetch-db) πŸ’€
  • --licenses β€” identify open-source licenses πŸ“œ
  • --boot β€” boot-security intel: U-Boot environment, device tree, and FIT/AVB/UEFI verified-boot posture πŸ₯Ύ
  • --keys β€” public-key weakness. this one is so much: keys whose private half is public (hardcoded/default keys, the compromised eMudhra intermediate CAs), the ROCA fingerprint (CVE-2017-15361), undersized or broken RSA keys, RSA factoring recoveries (Fermat/batch-GCD/Wiener), and near-zero EC private scalars recovered by a bounded search (the Oct 2026 eMudhra P-384 compromise). literally the whole key drama πŸ—οΈ
  • -A, --all β€” run every pass. this is the default when no pass is named πŸ‘‘

πŸŽ€ Output detail

  • -j, --json β€” emit a single JSON object instead of the human-readable view. JSON always carries the complete result set, so the detail options below only affect the human view. use this when a tool or model will parse the output πŸ€–
  • -v, --verbose β€” human view: show full values instead of tail-truncating long ones with "..." πŸ‘€
  • --license-paths β€” list the file locations of each license, not just a count (human output). implies --licenses πŸ“
  • --component-cves-full β€” human view: list every component CVE, not just the high-signal set (on CISA KEV, or CVSS 9.0 and above, or EPSS 0.10 and above). implies --cve 🚨
  • --kernel-cves-full β€” list every CVE for the detected kernel version, matched branch-aware, instead of the curated high-signal checklist. needs the kernel feed (see --with-kernel-feed). implies --cve 🐧
  • --full β€” maximum human detail in one switch: equivalent to --verbose, --license-paths, --component-cves-full, and --kernel-cves-full together. no effect on JSON, which is always complete. one button, everything, slay ✨
  • --dump-kconfig β€” print the recovered kernel .config to stdout, then exit πŸ–¨οΈ

πŸ¦‹ CVE mirror

  • --fetch-db β€” download a prebuilt CVE mirror and verify it against a published SHA-256, then exit. needs curl. this is the networked first-run step; see THE CVE MIRROR below 🌐
  • --update-db β€” rebuild the local CVE mirror from the upstream feeds, then exit. slower than --fetch-db; needs curl and unzip 🐒
  • --with-kernel-feed β€” given with --fetch-db or --update-db, also fetch the optional full kernel.org (Linux CNA) CVE feed that backs --kernel-cves-full 🐧
  • --no-fetch β€” suppress the interactive offer to download the CVE mirror. when the --cve pass runs with no local mirror on an interactive terminal, mithril otherwise asks whether to fetch it. with --no-fetch (or the environment variable MITHRIL_NO_FETCH), it never asks: a missing mirror is reported as an error and the scan exits nonzero, as in a non-interactive run. use it in scripts and CI πŸ™ˆ

🌈 Configuration

  • --rules FILE β€” add user-defined detection rules from a JSON file πŸ“
  • -C, --outdir DIR β€” also write the report to DIR, regardless of the stdout format: mithril-report.json (the complete JSON result set) and, when the SBOM pass runs, sbom.cdx.json and sbom.spdx.json (the CycloneDX and SPDX bills of materials). these standard-format SBOMs are produced only through -C. ⚠️ note that moria(1) also has a -C but means its extraction output directory; here it is the report output directory. don't mix them up bestie, iykyk πŸ’€
  • --threads N β€” worker threads for tree scans. default: automatic ⚑

⭐ Information

  • -h, --help β€” print help and exit 🫢
  • --version β€” print version and exit 🏷️

πŸ’œ THE CVE MIRROR πŸ’œ

the --cve pass reads a local mirror under ~/.local/share/mithril (or $MITHRIL_DB) β€” a compact memory-mapped index built from OSV.dev, the NVD 2.0 API, the CISA KEV catalog, and FIRST's EPSS scores ✨

on a first run, populate it once with mithril --fetch-db before the first CVE scan. without it, --cve reports the missing database and exits nonzero 😭

only --fetch-db and --update-db ever touch the network; a normal scan does not. no cap, that's the whole point πŸ”’


πŸ“ EXIT STATUS πŸ“

  • 0 β€” success πŸŽ‰
  • 1 β€” runtime error (for example, an unreadable target or a missing CVE mirror for --cve) 😬
  • 2 β€” usage error (unknown option, missing argument) πŸ™ƒ

🧚 ENVIRONMENT 🧚

  • MITHRIL_DB β€” path to the local CVE mirror (overrides the default under ~/.local/share/mithril)
  • MITHRIL_DB_URL β€” base URL that --fetch-db downloads the prebuilt mirror from
  • NVD_API_KEY β€” optional NVD API key used by --update-db to raise the upstream rate limit. it works without one, just more slowly 🐌
  • XDG_DATA_HOME β€” if set, mithril locates the mirror under $XDG_DATA_HOME/mithril instead of ~/.local/share/mithril
  • NO_COLOR β€” disables bold section headers in --help output 🎨

🌸 FILES 🌸

  • ~/.local/share/mithril/ β€” default location of the local CVE mirror (OSV plus NVD/CPE index, KEV, EPSS, and the optional kernel feed) πŸ“

πŸ”₯ EXAMPLES πŸ”₯

run every analysis on an unpacked tree:

mithril ./rootfs/

only the secrets pass:

mithril --secrets ./rootfs/

JSON output for a tool or model:

mithril -j ./rootfs/

first run: download the CVE mirror:

mithril --fetch-db

the full pipeline with moria:

moria -e firmware.bin && mithril firmware.bin.extracted/

πŸ‘‘ SEE ALSO πŸ‘‘

moria(1), trufflehog(1)

full documentation: README.md and docs/ in the source tree (https://github.com/nmatt0/mithril) πŸ“š


and that's the whole page, bestie 🫢 this is mithril version 0.4.0, dated 2026-10-22 β€” go forth, scan some firmware, and stay iconic πŸ’…βœ¨

πŸ“¦ where this page comes from