MITRE ATT&CK, LOLBAS, and LOLDrivers are references you browse. Forensicnomicon is DFIR knowledge compiled into code — every LOLBin, BYOVD vulnerable driver, forensic artifact, and attack indicator as a zero-dependency Rust library and an offline binary, queryable in milliseconds with no browser and no network.
6,554 forensic artifacts. Every one enriched.
You're in an active IR. You need to know if a binary is abusable, right now, offline, without opening a browser.
brew install SecurityRonin/tap/4n6query
# or: cargo install forensicnomicon-cli
4n6query certutil.exe # LOLBin lookup, ATT&CK techniques, use cases
4n6query userassist # 5 artifact variants, decoded field schemas, triage priority
4n6query T1547.001 # all artifacts mapped to this technique
4n6query --triage # Critical artifacts to collect first, RFC 3227 order
Building DFIR tools in Rust? The same data is a zero-dependency library:
[dependencies]forensicnomicon = "1"# facade: stable engine + the full catalog, one import
Building a detection pipeline or *-forensic analyzer? Depend on the stable engine
instead — forensicnomicon-core (the
report model, catalog types, and lookup engine) holds still while the knowledge in
forensicnomicon-data (the 6,554
descriptors + IOC/MITRE tables) updates underneath it — so catalog changes never force
your crate to republish.
What makes it different
MITRE ATT&CK and lolbas-project.github.io are browser references. This is Knowledge-as-Code — a binary.
Offline. All 6,554 artifacts compile into the binary. Zero I/O at runtime, zero network calls.
Enriched. Not just where an artifact lives — how to decode it, how strong the evidence is, what to collect alongside it, which KAPE targets and Velociraptor artifacts collect it.
All six LOL/LOFL datasets unified. LOLBAS, GTFOBins, LOOBins, LOFL cmdlets, LOFL MMC snap-ins, LOFL WMI classes. One lookup, one API.
LOL + LOFL — six datasets, one lookup
LOL (Living Off the Land) is abuse of OS-shipped binaries. LOFL (Living Off Foreign Land) is abuse of third-party admin tools common on enterprise endpoints: cloud CLIs, container runtimes, Sysinternals, language runtimes. Both appear identically in process telemetry and EDR alerts. Unifying them in one lookup produces fewer missed detections.
The catalog covers 6,554 artifacts across Windows, Linux, macOS, and cloud environments. Hundreds of entries are fully hand-curated with decoded field schemas, analyst-written meanings, triage priorities, evidence strength ratings, and detection pivots. The rest are generated from seven authoritative DFIR source corpora.
Take UserAssist. Every artifact registry gives you the key path. This catalog gives you:
Field
Value
Decode
Value names are ROT13. Payload is a 72-byte struct: run count at offset 4, last execution FILETIME at offset 60
Meaning
Proves a specific user account interactively launched a program
Reliability
Strong, but the key can be cleared. Absence does not prove non-execution.
Triage priority
Critical
Volatility
Persistent (on-disk registry). Grab RAM first per RFC 3227.
Detection pivots
T1204.002, 3 Sigma rules, YARA template
What's in the catalog
Execution evidence: UserAssist, Prefetch, Shimcache/AppCompatCache, Amcache, BAM/DAM, MUICache, SRUM, AppShim, Windows Timeline, Background Activity Moderator
use forensicnomicon::catalog::{CATALOG, TriagePriority};
use forensicnomicon::volatility::acquisition_order;
// RFC 3227 acquisition order: RAM firstletorder = acquisition_order();
// Triage-ordered list, Critical firstletcritical: Vec<_> = CATALOG
.for_triage()
.into_iter()
.filter(|d| d.triage_priority == TriagePriority::Critical)
.collect();
// Keyword searchlethits = CATALOG.filter_by_keyword("prefetch");
// MITRE technique lookuplethits = CATALOG.by_mitre("T1547.001");
// Decode a raw artifactletd = CATALOG.by_id("userassist_exe").unwrap();
letrecord = CATALOG.decode(d, value_name, raw_bytes)?;
// record.fields — Vec<(&str, ArtifactValue)>: typed field pairs// record.timestamp — Option<String>: ISO 8601 UTC when present
Built-in decoders: Rot13Name (UserAssist), FiletimeAt (FILETIME to ISO 8601), BinaryRecord, MruListEx, MultiSz, Utf16Le.
use forensicnomicon::evidence::{evidence_for, EvidenceStrength};
use forensicnomicon::volatility::{volatility_for, acquisition_order};
lete = evidence_for("prefetch_dir").unwrap();
// e.evidence_strength — EvidenceStrength::Strong// e.evidence_caveats — &["Disabled by default on Server SKUs", ...]letorder = acquisition_order();
// order[0] → mem_running_processes (Volatile)// order[n] → mft_file (Persistent)
Abusable sites
The abusable_sites module maps domains attackers use for C2, phishing, payload delivery, and exfiltration. These are trusted domains that enterprises cannot block.
BlockingRisk encodes the key tradeoff. GitHub and AWS carry BlockingRisk::Critical because blocking them breaks your own CI/CD and cloud workloads. Use this field to choose between blocking (low risk) and detect-and-alert (high or critical risk).
The static catalog is one half of the leaf. The other is forensicnomicon::report — the shared finding vocabulary every SecurityRonin analyzer normalizes onto, so VMDK, VHDX, EWF, MBR/GPT/APM, ISO 9660, EVTX, SRUM, memory, and PE findings aggregate into one uniform Report instead of N bespoke result types. It is the union of the analyzers' data, not a flattening — and a Finding is an observation with evidence, never a verdict.
use forensicnomicon::report::{Finding, Severity, Category, Source};
// Each analyzer keeps its own typed AnomalyKind and converts to a canonical Finding.letfinding = Finding::observation(Severity::High, Category::Integrity, "VMDK-RGD-MISMATCH")
.note("redundant grain directory diverges from the primary")
.source(Source { analyzer: "vmdk-forensic".into(), scope: "VMDK".into(), version: None })
.mitre("T1565.001") // "consistent with", never an assertion
.evidence("primary_gte", "0x1234")
.build();
assert_eq!(finding.severity, Some(Severity::High));
5-level Severity (Info < Low < Medium < High < Critical), carried as Option so unrated (the analyzer didn't score it) is distinct from scored, benign.
Observation producer trait — implement severity/category/code/note on your typed anomaly and get to_finding() for free. Finding is builder-only and #[non_exhaustive], so the model gains fields without breaking the published fleet.
FindingContext carries the behavioral superset: confidence, occurrence counts, timestamps, MITRE ExternalRefs, and non-disk SubjectRefs (process / module / registry key).
Stable, scheme-prefixed codes (VMDK-RGD-MISMATCH, MEM-PROCESS-HOLLOWING, WINEVT-PROVIDER-GUID-SPOOFING) are the cross-scheme join key; Category::from_code classifies them, and disk4n6 / issen render Report { findings, provenance, timeline } uniformly.
forensicnomicon stays the zero-dependency leaf: every analyzer depends down onto it; it depends on no one.
State-history (history)
The third KNOWLEDGE vocabulary (alongside the catalog and report) is forensicnomicon::history — the [H] state-history layer: a cross-cutting functor that lifts every navigation primitive to a time-indexed variant (disk → VSS/APFS snapshots, memory → hiberfil chain, log → rotated/sealed journals, query → point-in-time exports; [C] git is the fixed point). It is pure declarative vocabulary — no parsing, no I/O.
use forensicnomicon::history::{epoch::LsnKind, profiles::SourceTemporalProfile};
// The canonical temporal profile of one source family — the fleet's single source// of truth, so consumers (sqlite-forensic, Issen, …) never re-assert and drift on it.letwal = SourceTemporalProfile::sqlite_wal();
assert!(wal.clock.ordering_only); // a WAL frame carries no wall clock// A WAL ordering key is salt-qualified: a checkpoint reset rolls the salts AND// renumbers frames, so "frame 7 of epoch A" is not "frame 7 of epoch B".let_lsn = LsnKind::SqliteWalFrame { salt1: 0xDEAD_BEEF, salt2: 0x0BAD_F00D, frame_seq: 7, commit_seq: 3 };
TemporalCohort<H> / TemporalState<H> — an artifact's states, ordered by wall_time (else by ordering key), grouped under one IdentityDiscipline; generic over a source-defined handle so there is no trait-object overhead.
ClockProvenance — four orthogonal trust axes (source / trust_grade / tamper_resistance / ordering_only), so "local but signed" (iOS APFS) is distinct from "external + attested" (Sigstore) instead of one flat trust level.
SourceTemporalProfile(new in 0.4) — one canonical { clock, safety, topology, ordering } per source family (SQLite WAL, EVTX, USN, ESE, journald, VSS, git). A consumer reads the profile rather than re-deriving the classification, so the fleet cannot drift on, e.g., whether a WAL timestamp is forgeable.
We found that forensicnomicon demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.It has 1 open source maintainer collaborating on the project.
On Front Lines’ Early Adopters: CISO podcast, Andrew Becherer shares lessons from Datadog, weighs the long-term costs of AI-built tools, and explains how founders can actually gain a CISO’s trust.
Socket for Origin scans dependency changes in pull requests and flags security risks based on your team’s policies. Now available in Cursor’s App Marketplace.