Adds per-CVE triage annotations that turn SKELETONKEY's JSON output
into something a SIEM/CTI/threat-intel pipeline can route on, and a
KEV badge in --list so operators see at-a-glance which modules
cover actively-exploited bugs.
New tool — tools/refresh-cve-metadata.py:
- Discovers CVEs by scanning modules/<dir>/ (no hardcoded list).
- Fetches CISA's Known Exploited Vulnerabilities catalog
(https://www.cisa.gov/.../known_exploited_vulnerabilities.csv).
- Fetches CWE classifications from NVD's CVE API 2.0
(services.nvd.nist.gov), throttled to the anonymous
5-req/30s limit (~3 minutes for 26 CVEs).
- Hand-curated ATT&CK technique mapping (T1068 default; T1611 for
container escapes, T1082 for kernel info leaks — MITRE doesn't
publish a clean CVE→technique feed).
- Generates three outputs:
docs/CVE_METADATA.json machine-readable, drift-checkable
docs/KEV_CROSSREF.md human-readable table
core/cve_metadata.c auto-generated lookup table
- --check mode diffs the committed JSON against a fresh fetch for
CI drift detection.
New core API — core/cve_metadata.{h,c}:
struct cve_metadata { cve, cwe, attack_technique, attack_subtechnique,
in_kev, kev_date_added };
const struct cve_metadata *cve_metadata_lookup(const char *cve);
Lookup keyed by CVE id, not module name — the metadata is properties
of the CVE (two modules covering the same bug see the same metadata).
The opsec_notes field stays on the module struct because exploit
technique varies per-module (different footprints).
Output surfacing:
- --list: new KEV column shows ★ for KEV-listed CVEs.
- --module-info (text): prints cwe / att&ck / 'in CISA KEV: YES (added
YYYY-MM-DD)' between summary and operations.
- --module-info / --scan (JSON): emits a 'triage' subobject with the
full record, plus an 'opsec_notes' field at top level when set.
Initial snapshot:
- 10 of 26 modules cover KEV-listed CVEs (dirty_cow, dirty_pipe,
pwnkit, sudo_samedit, ptrace_traceme, fuse_legacy, nf_tables,
overlayfs, overlayfs_setuid, netfilter_xtcompat).
- 24 of 26 have NVD CWE mappings; 2 unmapped (NVD has no weakness
record for CVE-2019-13272 and CVE-2026-46300 yet).
- All 26 mapped to an ATT&CK technique.
Verification:
- macOS local: 33 kernel_range + clean build, --module-info shows
'in CISA KEV: YES (added 2024-05-30)' for nf_tables, --list KEV
column renders.
- Linux (docker gcc:latest): 33 + 54 = 87 passes, 0 fails.
Follow-up commits will add per-module OPSEC notes and --explain mode.
Standalone Python script that pulls Debian's security-tracker JSON
and compares each module's hardcoded kernel_patched_from table
against the fixed-versions Debian actually ships. Surfaces real
drift the no-fabrication rule needs us to fix:
MISSING — Debian has a fix on a kernel branch we have no entry
for. Module's detect() would say VULNERABLE on a host
that's actually patched.
TOO_TIGHT — Our threshold is later than Debian's earliest fix on
the same branch. Module would call a patched host
VULNERABLE. False-positive on production fleets.
INFO — Our threshold is earlier than Debian's. We're more
permissive; usually fine (we tracked a different
upstream-stable cut), but flagged for review.
Three output modes:
default (text) — human-readable report on stderr
--json — machine-readable for CI / dashboards
--patch — unified-diff-style proposed C-source edits
--refresh — bypass the 12h cache TTL and re-fetch
Implementation:
- urllib (no pip deps) fetches the ~70MB tracker JSON.
- Cached at /tmp/skeletonkey-debian-tracker.json with 12h TTL.
- Parses every modules/*/skeletonkey_modules.c for the .cve = '...'
field + the kernel_patched_from <name>[] = { {M,m,p}, ... } array.
- Per CVE, builds {debian_release -> upstream_version_tuple} from
the tracker's 'releases.*.fixed_version' field (stripping Debian
-N / +bN / ~bpoN suffixes to recover the upstream version).
- Groups by (major, minor) branch; flags MISSING / TOO_TIGHT / INFO.
- Exits non-zero when MISSING or TOO_TIGHT findings exist (suitable
for a CI 'detect-drift' job).
First-run output found drift in 17 of 20 modules with kernel_range
tables — operator-reviewable. NOT auto-applied; this commit only
ships the diagnostic tool, not the suggested fixes.
README's Contributing section now points at the tool.
iamroot-fleet-scan.sh — bash wrapper that scp's the iamroot binary
to a host list, ssh-runs --scan --json on each, aggregates results
into a single JSON document. Supports:
- hosts list from file or stdin
- user@host:port syntax
- parallel xargs execution (default -P 4)
- ssh key / extra ssh opts pass-through
- --no-sudo for hosts where root isn't required
- --summary-only to suppress per-host detail
- --no-cleanup to leave the binary on disk
Critical fix during smoke-test: iamroot's exit codes are SEMANTIC
(0=OK, 2=VULNERABLE, 4=PRECOND_FAIL, 5=EXPLOIT_OK). The wrapper
must NOT treat nonzero exit as a transport failure; success is
defined by 'stdout contains valid JSON', failure by 'stdout empty'.
Verified end-to-end on kctf-mgr → kctf-fuzz:
fleet-scan reports ok=1, failed=0,
summary.vulnerable groups by CVE: copy_fail_gcm, dirty_frag_esp×2,
entrybleed. Per-host detail included.
docs/DETECTION_PLAYBOOK.md — operational integration guide:
- Lifecycle diagram (inventory → scan → fleet scan → deploy/mitigate/upgrade → monitor)
- Recipes by team size: single host, small fleet, large fleet
- SIEM integration patterns: Splunk, Elastic, Sigma
- Auditd-event lookup commands per module key
- VULNERABLE decision tree (patch vs mitigate vs compensate)
- Mitigation revert procedures + side-effect table
- False-positive tuning table per rule key
- Pre-patch quarantine pattern
- Maintenance contract / module-shipping SLA