AIDE file integrity monitoring in practice: detect server file tampering
After a backdoor is planted, logs often look clean. AIDE catches tampering by hashing a baseline and diffing. This article covers AIDE FIM: tiered watch scope, initial baseline generation with immutable protection, reading aide check diffs, scheduled scan plus mail alerts, false-positive handling, and Tripwire / Wazuh selection.
Applicable scenarios
After a backdoor is planted, attackers typically modify system binaries, replace sshd config, or drop persistence scripts into cron directories. Logs alone rarely catch this class of silent tampering — legitimate commands may keep working. AIDE (Advanced Intrusion Detection Environment) builds a cryptographic hash baseline of critical directories and, on later scans, diffs added, deleted, and modified files. Lowest-cost, most direct FIM on a Linux host. Fits ops teams that need MLPS compliance, or that want tamper detection with no commercial HIDS budget.
Prerequisites
- Debian/Ubuntu or RHEL/CentOS-family host, root
- Enough free disk for the baseline DB (depends on watch scope; typically 20MB to hundreds of MB)
- Host already hardened. Baseline must be generated on a confirmed-clean system, or the backdoor is written into the baseline
- Working mail delivery (postfix or mailx installed) for alerts
How it works
AIDE's model is three steps: read the rule set in the config → walk target files and compute attribute digests → compare against the baseline DB and emit a diff report.
- Database: default baseline is `/var/lib/aide/aide.db.gz`; newly generated DB is `aide.db.new.gz`. Manual replace before it takes effect
- Rule set: config is selection lines plus macro definitions. A selection line looks like `/etc Full`; the latter decides which attributes are recorded
- Attribute macros: common ones are p (perms), i (inode), n (link count), u/g (owner/group), s (size), m (mtime), a (atime), c (ctime), S (SHA512 digest), md5, sha256. Production should use the two built-in macros R (full check for read-only files) and L (loose check for growing log-class files)
Procedure
1. Install AIDE
# Debian / Ubuntu
sudo apt update && sudo apt install -y aide aide-common
# RHEL / CentOS / Rocky
sudo yum install -y aide
2. Configure watch scope
Edit /etc/aide/aide.conf (RHEL family: /etc/aide.conf). Append custom rules at the end. Core idea: strict checks on critical config; exclude or loosen directories that change constantly.
# Custom rule set — /etc/aide/aide.conf
# Strict checks: alert on any attribute change
StrictChecks = p+i+n+u+g+s+m+c+sha256+sha512
# Loose checks: for log and cache-class files
LooseChecks = p+u+g+n+sha256
# — Strict monitoring: system and security configuration —
/etc StrictChecks
/boot StrictChecks
/bin StrictChecks
/sbin StrictChecks
/usr/bin StrictChecks
/usr/sbin StrictChecks
/usr/lib/systemd StrictChecks
/root StrictChecks
/etc/ssh StrictChecks
# — Loose monitoring: frequently changing but still need awareness —
/var/spool/cron LooseChecks
/etc/cron.d LooseChecks
# — Explicit excludes: avoid false positives and performance overhead —
!/var/log
!/var/cache
!/var/tmp
!/tmp
!/proc
!/sys
!/dev
!/run
!/var/lib/aide
!/var/lib/docker
Note: a leading ! means exclude. Exclude rules must come after include rules. Watching a Docker host's `/var/lib/docker` makes scans much slower; exclude it and watch the upper-layer config directories separately.
3. Generate the initial baseline database
# Initialize (duration depends on file count; may take several minutes)
sudo aide --init
# Check the generated files
sudo ls -lh /var/lib/aide/
# After confirmation, replace as the official baseline
sudo cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
Debian family can wrap it in one shot: `sudo aideinit`, then `sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db`.
The baseline file itself must be protected. Set the immutable attribute and copy an offline backup:
sudo chattr +i /var/lib/aide/aide.db.gz
sudo cp /var/lib/aide/aide.db.gz /backup/aide/
4. Run an integrity check
sudo aide --check
No anomaly looks like:
AIDE found no differences between database and filesystem. Looks okay!!
On change, a detailed diff, e.g.:
File: /etc/ssh/sshd_config
Size : 3042 , 3080
Mtime : 2026-09-01 08:12:03 , 2026-09-11 03:40:11
SHA256 : 3f2a...c91d , 8b7e...04af
Added files:
f++++++++++++++++: /usr/local/bin/kworker
How to read: Size / Mtime / SHA256 mismatch means the file was modified. Files in Added files with `+++` are new — inspect hard, especially executables under /bin, /usr/bin, /etc/cron*, or hidden under /usr/local/bin.
5. Update the baseline after confirming false positives
For confirmed-legitimate change (normal system updates, config edits), rebuild the baseline or every check will re-alert.
# Generate the new database
sudo aide --update
# Replace the old baseline
sudo chattr -i /var/lib/aide/aide.db.gz
sudo cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
sudo chattr +i /var/lib/aide/aide.db.gz
6. Schedule scans and alerts
# /etc/cron.d/aide-check — daily 03:20 scan and send mail
20 3 * * * root /usr/bin/aide --check 2>&1 | mail -s "[AIDE] $(hostname) integrity report" ops@example.com
If mail is not installed, write a local file and let the log pipeline (rsyslog, Wazuh) pick it up:
20 3 * * * root /usr/bin/aide --check >> /var/log/aide/report.log 2>&1
# Append a rule to avoid self-monitoring loop
# !/var/log/aide
Configuration verification
Confirm config syntax and that the DB is readable:
sudo aide --config-check
sudo ls -lh /var/lib/aide/aide.db.gz
Do a deliberate tamper test and verify the alert fires:
# Create a change
sudo touch /etc/aide_test_marker
# Run the check
sudo aide --check | grep -A2 "aide_test_marker"
# Expected: the Added files section should contain this file
# Cleanup
sudo rm -f /etc/aide_test_marker
Check scan duration, assess business impact:
sudo time aide --check
If a single scan exceeds several minutes and burns heavy IO, shrink the watch scope or use the limit parameter for batched checks (AIDE 0.17+).
FAQ
FAQ 1: Every check dumps a wall of alerts. Is the baseline broken?
Most likely "legitimate change never synced into the baseline" or "high-churn directories not excluded". Triage:
- See which directory the alerts cluster in. Cluster in `/var/log`, `/var/cache`, `/var/lib/docker` → exclude rules did not take effect. Check that ! prefix rules sit after include rules
- Cluster in a few files under `/etc` → recent system update or manual edit. Confirm content is clean, then `aide --update` to rebuild the baseline
- Large `Added files` with random names (e.g. `.tmp_XXXX`) → treat as intrusion. Check cron, systemd services, and SSH authorized_keys first
Put "baseline update" into the change process: after any config change, the change owner updates the baseline and records why.
FAQ 2: AIDE vs Tripwire vs Wazuh — how to choose?
Different jobs. Pick by scale and budget:
- AIDE: single host, fully local, no daemon. Lowest deploy cost. Fits small/medium server fleets and compliance baseline checks. No central management; you solve log aggregation yourself
- Tripwire: commercial edition gives central policy and compliance reports. Open-source edition is close to AIDE but harder to configure
- Wazuh / OSSEC: HIDS. Beyond FIM: log analysis, rootcheck, realtime monitoring, central console. Fits large host counts that need unified alerting. Deploy and tune cost is clearly higher
Practice: under ~20 hosts, start with AIDE + log aggregation. When you need realtime detection instead of daily scans, introduce HIDS.
FAQ 3: Can the baseline DB live on the monitored machine? Can an attacker rewrite it too?
It can be stored there. It must not live in only one place. After root, the attacker can recompute hashes and overwrite the local baseline. Correct approach is three points together:
- chattr +i on the baseline file — raises the bar for direct tamper
- Daily push of baseline copy and check reports to a remote log server or object storage — external trusted copy
- On critical servers, keep the baseline on read-only media or a dedicated management-net share; read it from remote at scan time
Summary
AIDE's value is turning "guess whether anything was changed" into "hash evidence you can compare". Four landing points: baseline must be generated on a clean system, or the backdoor is ingested; rules must be tiered — strict checks on system binaries and security config, exclude or loosen log/cache directories; protect the baseline — chattr immutable plus an external copy; sync on change — fold baseline updates into the config-change process so alert fatigue does not hide a real intrusion. After deploy, AIDE complements log audit and WebShell hunting, covering the "file was changed" blind spot that logs miss.