File Integrity Monitoring: The Silent Tripwire Attackers Forget About
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:
- Sysmon configuration and binaries
- EDR agent installation directories
- Windows Defender paths and definitions
- LSASS protection configuration
Linux:
- Sysmon for Linux configuration
- EDR/agent configuration directories
- Audit system configuration (
/etc/audit/) - Forensics tool directories
macOS:
- Agent configuration paths
- System Integrity Protection directories
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:
- Startup folders (each user and all users)
- Scheduled task XML definitions (
C:\Windows\System32\Tasks\) - Service binaries and DLLs
- WMI repository
- COM object registrations
- Shell extension directories
Windows Registry (often overlooked, but FIM can monitor registry keys too):
RunandRunOncekeys (HKLM and HKCU)- Service registration keys
- WinLogon entries (Userinit, Shell)
- Image File Execution Options (debugger hijacking)
- AppInit_DLLs
Linux:
- Cron directories (
/etc/cron.d/,/var/spool/cron/) - Systemd unit files (
/etc/systemd/system/) - Shell profile files (
.bashrc,.profile,/etc/profile.d/) - SSH authorized_keys
- At job directories
- Init scripts
macOS:
- LaunchAgents and LaunchDaemons (each user and system wide)
- Login items (
/Library/Preferences/com.apple.loginitems.plist) - Shell profiles
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:
- SAM database
- NTDS.dit (on domain controllers)
- LSA secrets registry hive
- DPAPI key directories
- Certificate stores
- Browser credential databases
Linux:
/etc/shadowand/etc/passwd- SSH private keys (
/etc/ssh/,~/.ssh/) - Kerberos keytabs
- PAM configuration
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:
- IIS web roots (
C:\inetpub\wwwroot\) - Custom application directories
- Tomcat/Java webapp directories
Linux:
- Apache web roots (
/var/www/) - Nginx sites
- PHP FPM directories
- Java/Tomcat webapps
- Custom application deploy paths
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:
C:\Users\Public\and subdirectories (Downloads, Documents, Music, Videos)C:\ProgramData\(commonly used by RATs and droppers)- Temp directories (
%TEMP%,C:\Windows\Temp\) - Recycle Bin (
C:\$Recycle.Bin\) - WMI staging paths
- Print spool directory (
C:\Windows\System32\spool\drivers\)
Linux:
/tmp/and/var/tmp//dev/shm/(RAM backed filesystem; nothing legitimate lives here permanently)- Hidden directories in
/opt/ /var/log/(attackers hiding in plain sight)
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:
- Forensics collection tools executing outside of authorized investigations
- Forensic acquisition utilities running on endpoints that aren’t being investigated
- Evidence collection scripts modified or executed by unauthorized personnel
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:
- Custom tooling that doesn’t match behavioral signatures
- Legitimate looking binaries that are actually packed malware
- Scripts that are benign individually but malicious in context
- Tools that live on disk briefly before execution
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:
- Windows Update modifying system directories
- Package managers updating binaries
- EDR agents updating their own definitions
- Application updaters
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:
- Security tool directories
- Persistence locations
- Credential stores
- Web shells
- 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:
- Single configuration format across Windows, Linux, macOS
- Full whodata/audit integration on all three platforms
- Windows registry key monitoring as native objects
- File diff reporting (shows what changed within text files, great for catching config tampering)
- Checksum tracking: MD5, SHA1, SHA256. Catches modifications that don’t change file size
- Attribute monitoring: permissions, ownership, timestamps. Catches permission escalation and timestomping
- Centralized policy management from the Wazuh manager
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:
- FIM detects file change. Wazuh syscheck event generated.
- Event enriched. SIEM adds hostname, user, process tree from Sysmon.
- Correlation rules fire. Custom rules match patterns: new persistence, tool tampering, canary access.
- YARA scan triggered. New files scanned against malware signatures.
- MITRE mapping applied. Alert tagged with ATT&CK technique ID.
- 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.