File Integrity Monitoring: The Silent Tripwire Attackers Forget About

Nobody writes blog posts about FIM. It doesn’t get conference talks. No vendor is building a Series B around “we watch files for changes.” And that’s exactly why it works.

Attackers spend weeks evading EDR. They test their payloads against Defender. They buy Cobalt Strike licenses specifically to avoid AV signatures. Then they drop their payload to C:\Users\Public\Downloads and FIM catches it because nobody on the red team bothered to check whether the target monitors that path.

We’ve been running FIM across a mixed Windows, Linux, and macOS environment for a while now, and it consistently catches things that our more expensive tools miss. Not because FIM is smarter. Because attackers don’t think about it.

Here’s how we set it up, what actually works, what doesn’t, and the mistakes we made along the way.

What FIM Actually Does

The concept is almost embarrassingly simple. You tell the agent “watch these directories.” When a file gets created, modified, deleted, or has its permissions changed, the agent logs the event with details about what happened.

The detection flow:

Agent (FIM module) > SIEM Manager > Log Pipeline > SOC Dashboard

Three monitoring modes matter:

Mode How It Works Best For
Realtime Kernel file system hooks (inotify on Linux, ReadDirectoryChangesW on Windows, FSEvents on macOS) Critical directories where you need immediate alerts
Whodata Extends realtime with audit subsystem to capture the user, process, and PID that made the change Sensitive paths where attribution matters
Scheduled Periodic scans at defined intervals (e.g., every 5 minutes) Large directories where realtime would be too noisy

The difference between realtime and whodata matters more than you’d think. Realtime tells you “this file changed.” Whodata tells you “this file was changed by user X, using process Y, at PID Z.” During an incident, that attribution is the difference between “something happened” and knowing exactly which process on which account touched the file at what time. We default to whodata on every critical path. For high volume directories where you only need to know a file appeared (not who put it there), realtime keeps things clean.

The Philosophy: Watch What Attackers Touch

Most FIM deployments start with the default paths: /etc and C:\Windows\System32. That catches some things, but it mostly generates Windows Update noise and misses the paths attackers actually use. The key insight: monitor the paths where attackers stage and persist, not just the paths the compliance checklist tells you to watch.

We organize monitored paths into five categories:

1. Security Tool Directories (Whodata Mode)

If an attacker tampers with your security tools, every other detection becomes unreliable. These paths get the most thorough monitoring:

Windows:

Linux:

macOS:

Why whodata here specifically: if someone modifies your Sysmon config, you need to know which user and process did it. A sysadmin updating a config during a change window looks nothing like NT AUTHORITY\SYSTEM writing to the config via a spawned process at 2 AM.

MITRE mapping: T1562.001 (Impair Defenses: Disable or Modify Tools)

2. Persistence Locations (Realtime Mode)

Every persistence mechanism that writes to disk can be caught by FIM. These are the directories where attackers establish their foothold:

Windows:

Windows Registry (often overlooked, but FIM can monitor registry keys too):

Linux:

macOS:

MITRE mapping: T1547.001, T1053.005, T1543.002, T1546.004, T1547.004, T1547.009

3. Credential Stores and Authentication

Attackers need credentials. Monitor where credentials live:

Windows:

Linux:

MITRE mapping: T1003.002 (SAM), T1003.003 (NTDS), T1552.004 (Private Keys), T1556.003 (PAM Modification)

4. Web Shell Locations (Realtime Mode)

Web shells are one of the most common persistence mechanisms for internet facing servers. Monitor every web root:

Windows:

Linux:

Why realtime and not whodata here: web shell creation is almost always malicious, and you want the alert immediately. Attribution is useful but secondary to knowing it happened at all. Speed over context.

MITRE mapping: T1505.003 (Web Shell)

5. Malware Drop Zones

This is where the default path approach completely falls apart. Attackers don’t drop tools to C:\Windows\System32. They use paths that legitimate software rarely touches, and that default FIM configurations never monitor.

We went through our YARA rule library (10,000+ rules covering 120+ malware families) and mapped which staging directories actually show up in real malware. The usual suspects:

Windows:

Linux:

Extension filtering matters here. We restrict FIM scans to executable and script extensions only (.exe, .dll, .ps1, .bat, .py, .sh, .elf, .so, etc.) in drop zone directories. Without this filter, monitoring /tmp on a busy server will bury you in noise from legitimate application temp files. With it, you catch actual payloads and ignore the rest.

MITRE mapping: T1036 (Masquerading), T1059 (Command and Scripting Interpreter), T1204 (User Execution)

Custom Detection Rules

The “file changed” alert by itself isn’t that useful. The value comes from building rules that combine FIM events with context:

Security Tool Tampering

Rule Triggers When Severity
EDR Config Modified Sysmon config file written by unauthorized process Critical
Agent Binary Replaced EDR agent executable hash changes outside maintenance window Critical
Audit Config Tampered Changes to audit daemon configuration Critical
Defender Exclusion Added Modification to exclusion paths or policy High

Canary Files (The Best Detection We Run)

This is my favorite thing in our entire detection stack. It’s also the simplest.

We place files in directories that attackers enumerate: security tool paths, credential directories, admin shares. A config.bak in the EDR directory. A credentials.old in an admin’s home folder. A backup_keys.pem in an SSH directory. A passwords.xlsx on a file share.

These files look legitimate but have zero operational purpose. Nobody should ever touch them. No cron job reads them. No application writes to them. They just sit there, waiting.

When FIM detects access to one of these files, it fires a critical alert. In our deployment, we have yet to see a false positive from a canary file. Not “low false positive rate.” Zero. The only things that trigger canary files are people or tools actively exploring the filesystem, which is either an attacker, an insider doing something they shouldn’t, or a misconfigured vulnerability scanner (which happened once, and we fixed the scan scope).

The flip side: canary files only catch things that enumerate or read specific files. An attacker who knows exactly what they’re looking for and goes straight to it without browsing around won’t trigger them. They’re a tripwire, not a wall.

Service and Scheduled Task Tampering

Rule Triggers When Why It Matters
New service binary File created in service directories T1543.003 (Service Creation)
Scheduled task XML modified Changes to task definitions outside of GPO T1053.005 (Scheduled Task)
Cron job added New file in cron directories T1053.003 (Cron)
Systemd unit created New unit file outside package manager T1543.002 (Systemd Service)

Forensics Tool Abuse

If your forensics tools are being used outside of IR activities, that’s worth investigating:

YARA Integration: Scanning What FIM Finds

FIM tells you a file changed. YARA tells you what the file is. The combination is where things get interesting.

When FIM detects a new or modified file in a monitored directory, we trigger a YARA scan against that file. Our rule library:

Source Count Coverage
Community signature rules ~4,300 Known malware families, hack tools
Threat intel vendor rules ~2,900 Commercial grade malware detection
Curated community ruleset ~2,100 Confident, low false positive community rules
Custom rules ~700+ Organization specific detections, tooling signatures

FIM catches the file creation event. YARA identifies the file. The SIEM correlates both and produces an alert with full context: what file, where, who created it, and what it is.

This catches things that EDR misses:

We scope YARA scans to the directories that matter most: persistence paths, security tool directories, credential stores. High volume paths like /tmp get extension filtered FIM without the YARA scan, so the alert fires fast and YARA runs only where identification adds value.

Where FIM Fits in the Stack

FIM covers the filesystem. Attackers know this, and the sophisticated ones try to avoid touching disk entirely. That’s why FIM isn’t the only layer.

In memory attacks (fileless malware, reflective DLL injection, process hollowing) never write to disk, so FIM doesn’t see them. That’s what Sysmon process telemetry and behavioral detection handle. When an attacker uses an in memory technique to stage, they almost always touch the filesystem eventually (persistence, exfil staging, tool drops), and that’s where FIM picks them up.

Kernel level evasion (rootkits, io_uring abuse on Linux) can theoretically hide filesystem changes from FIM and the audit subsystem. This is an advanced threat that also blinds most userspace security tools. We address this with boot time integrity checks and hardware backed attestation where the environment supports it.

Timestomping can sometimes fool scheduled FIM scans that compare metadata. Realtime and whodata modes are immune to this because they watch the actual filesystem event, not the timestamps after the fact. This is one of the reasons we default to realtime over scheduled for anything security critical.

Speed matters. Realtime FIM detects file creation the moment it happens, but there’s still a pipeline from event to alert. For fast moving threats like ransomware, we wire FIM directly into automated response workflows that isolate the host within seconds of detecting encryption indicators.

macOS platform differences. FSEvents doesn’t provide the same granularity as inotify on Linux or ReadDirectoryChangesW on Windows. We compensate with tighter directory scoping and supplementary Sysmon telemetry on macOS endpoints to fill attribution gaps.

The point is that FIM handles the filesystem layer, and it handles it well. The rest of the stack covers what FIM doesn’t see.

Noise Reduction: The Hard Part

This is where most FIM deployments die. Not in the configuration, not in the monitoring. They die when the SOC gets 50,000 FIM events per day and turns the whole thing off because they can’t find the signal.

The fix is deliberate scoping. Monitor specific paths with specific filters, not entire directories unfiltered. A temp directory on a busy web server generates thousands of events per hour from legitimate PHP session files. Without extension filtering, that noise buries everything useful. With it, you only see executables and scripts landing in that directory, which is exactly what you care about.

1. Extension Filtering

In busy directories (temp, ProgramData, cache), only monitor file types that matter:

Monitor: .exe, .dll, .sys, .ps1, .bat, .cmd, .vbs, .js, .hta, .py, .sh, .elf, .so
Ignore: .tmp, .log, .etl, .cache, .dat (in temp directories only)

2. Process Exclusions

Exclude trusted processes that legitimately modify monitored paths:

Be surgical with exclusions. Every exclusion is a blind spot. We’ve seen real attacks hide behind Windows Update paths because the defender over excluded. We track every exclusion in version control and review them quarterly. If an exclusion can’t be justified with a specific noise source, it gets removed.

3. Directory Scoping

Don’t monitor entire drives. Monitor specific paths with specific configurations:

Path Type Mode Frequency Restrictions
Security tools Whodata Realtime None (monitor everything)
Persistence dirs Realtime Continuous None
Credential stores Whodata Realtime None
Web roots Realtime Continuous Script and binary extensions only
Malware drop zones Realtime Continuous Executable extensions only
System config Scheduled Every 5 min None

4. Consistency Across Platforms

Apply the same monitoring philosophy across all three platforms. The specific paths differ, but the categories are identical:

  1. Security tool directories
  2. Persistence locations
  3. Credential stores
  4. Web shells
  5. Malware drop zones

This gives you a consistent detection surface regardless of operating system. When we add a new category on Windows, we add the equivalent on Linux and macOS at the same time.

What We Use

Our FIM deployment is built on open source tools:

Component Role Cost
Wazuh (syscheck module) FIM agent on every endpoint Free
Graylog Log management, correlation, alerting Free (open source edition)
Filebeat Log shipping from Wazuh to SIEM Free
YARA File classification when FIM triggers Free
Sysmon Supplementary telemetry (process to file correlation) Free

No commercial FIM product. The value isn’t in the tooling. It’s in the engineering: what you monitor, how you filter noise, and what you do with the alerts. You can buy a six figure FIM product and get worse results than a well configured Wazuh syscheck deployment, because the product doesn’t know your environment. You do.

Wazuh Syscheck

The syscheck module does more than you’d expect:

The syscheck module runs on the agent itself, not agentless scanning. This means it works even when the network is compromised. An attacker who controls the network can’t prevent the agent from logging local file changes. They’d need to tamper with the agent directly, which brings us back to why monitoring security tool directories is priority number one.

Putting It All Together

A single FIM event by itself is a data point. FIM integrated into the rest of the detection pipeline is where it earns its place.

The chain:

  1. FIM detects file change. Wazuh syscheck event generated.
  2. Event enriched. SIEM adds hostname, user, process tree from Sysmon.
  3. Correlation rules fire. Custom rules match patterns: new persistence, tool tampering, canary access.
  4. YARA scan triggered. New files scanned against malware signatures.
  5. MITRE mapping applied. Alert tagged with ATT&CK technique ID.
  6. SOC investigates. Analyst has file path, user, process, technique, and YARA classification.

A FIM event that starts as “new .exe in Public Downloads” becomes: “user JSMITH created cobalt_strike_beacon.exe (YARA match: CobaltStrike_Beacon_Encoded) in C:\Users\Public\Downloads at 3:47 AM, spawned from PowerShell.exe with encoded command line, maps to T1059.001 + T1204.002.”

That’s an alert an analyst can act on immediately. No digging through raw logs. No “is this a false positive?” guesswork.

Getting Started

If you’re deploying FIM from scratch, here’s what I’d prioritize:

Priority What to Monitor Why
1 Security tool directories (whodata) Detect defense evasion first
2 Persistence locations (realtime) Catch attacker footholds
3 Credential stores (whodata) Detect credential theft
4 Web server roots (realtime) Catch web shells
5 Malware drop zones (realtime, filtered) Catch payload staging
6 Deploy canary files Zero false positive tripwires
7 Add YARA scanning Identify what FIM finds
8 Build custom correlation rules Confident detections

Start with the first three. They cover the techniques most commonly seen in real intrusions: defense evasion, persistence, and credential access. Get those working and tuned before you expand scope.

Deploy canary files on day one. Seriously. They take almost no effort to configure, they produce the cleanest alerts in your entire stack, and they catch things that more complex detections miss. Drop a passwords.xlsx on an admin share and wait. You’ll either catch something real or confirm nobody is poking around where they shouldn’t be. Either outcome is valuable.

The Numbers

For context on what our deployment looks like:

Metric Count
Monitored directories (Windows) ~25
Monitored registry keys (Windows) ~12
Monitored directories (Linux) ~25
Monitored directories (macOS) ~10
Custom FIM detection rules ~25
YARA rules for file classification ~10,000
Noise exclusion rules ~30
MITRE ATT&CK techniques covered 15+

This isn’t something you buy and deploy in an afternoon. Each monitored path, each exclusion rule, each custom detection is configured based on real attacker behavior and real noise from our specific environment. Your paths will be different. Your noise patterns will be different. The methodology transfers; the specific config does not.

FIM doesn’t care about obfuscation. It doesn’t care about living off the land techniques. It doesn’t care about zero day exploits. If the attack touches the filesystem in a location you’re watching, FIM sees it. And since almost every attack eventually touches the filesystem (persistence, staging, credential access, exfiltration), a well scoped FIM deployment catches things that more complex tools miss entirely.

It’s the boring control that keeps working when the flashy ones get bypassed.