A host-based file integrity monitoring project using Linux Auditd. Configured watch rules for protected files, simulated attacks using ELF binaries, and analyzed audit logs to attribute each modification, demonstrating compliance monitoring and incident response skills.
This project involves designing and implementing a host-based file integrity monitoring system using Linux Auditd which is the Linux kernel's built-in audit framework. Watch rules were configured for a set of protected files to detect unauthorized modifications at the kernel level, achieving 100% detection coverage across all monitored files. Three simulated attack executables were then run against the protected directory, and audit logs were analyzed exclusively using ausearch and filter keys to correlate each attack binary with the file it modified, demonstrating a practical incident response and forensic analysis workflow.
| Component | Role |
|---|---|
| Kernel audit system | Generates audit events when a watched file is accessed or modified |
| auditd | Userspace daemon that writes events to /var/log/audit/audit.log |
| auditctl | Sets and lists rules in the running kernel (temporary; lost on restart) |
/etc/audit/rules.d/audit.rules |
Persistent rules file, loaded on boot via sudo augenrules --load |
| ausearch / aureport | Tools for querying and summarizing the audit log |
| File | Description |
|---|---|
attack-a, attack-b, attack-c |
Compiled ELF binaries that each modify a different protected file |
protected_files/ |
The 10 files monitored by auditd watch rules |
audit.rules |
Watch rules loaded into auditd via augenrules |
audit.log |
Audit log captured during the attack runs |
| Component | Details |
|---|---|
| OS | Kali Linux |
| Virtualization | VirtualBox |
| Shell | Bash |
| Audit Daemon | auditd |
| Key Tools | auditctl, ausearch, aureport, augenrules |
The attack binaries hardcode a specific file path for the protected files
directory. This was discovered by inspecting the binaries with strings:
strings attack-a | grep -i protected
The expected path was then recreated to ensure the binaries could locate and modify the target files:
sudo mkdir -p /home/codepath/project2-main
sudo cp -r ~/protected_files /home/codepath/project2-main/protected_files
sudo chown -R kali:kali /home/codepathNote: The
chowncommand was necessary because files copied withsudo cpdefault to root ownership, which would have prevented the attack binaries from writing to the protected files.
Auditd was started and its status was verified to confirm it was actively listening for events:
sudo systemctl start auditd
sudo systemctl status auditd
The output confirms the service is active (running) and that auditd
4.1.2 is listening for events. Note the disabled flag in the loaded
line so if you want it to persist across reboots, the service should also be enabled:
sudo systemctl enable auditdThe project files were downloaded and extracted into the working directory:
wget https://github.com/codepath/project2/archive/main.zip
unzip main.zip
The extracted directory contains two key components:
- Attack binaries — three compiled ELF executables (
attack-a,attack-b,attack-c) that each modify a different protected file - Protected files — the 10 files that will be monitored by auditd
Watch rules were written for each of the 10 protected files in
/etc/audit/rules.d/audit.rules. Each rule follows this syntax:
-w <absolute path> -p <permissions> -k <key>-w— the file to watch (absolute path only,~is not expanded)-p w— monitor write operations only-k— a unique key per file for easy log filtering
Rules were loaded permanently using:
sudo augenrules --loadAnd verified with:
sudo auditctl -l
Before running each attack, the audit log was cleared to ensure only events from that binary appeared in the output:
sudo truncate -s 0 /var/log/audit/audit.logEach binary was then executed and the log was queried immediately after using the attack's path as a filter:
./attack-a
sudo ausearch -i -x ./attack-a
The ausearch output shows:
key=cloudia— the watch rule forcloudia.txtfiredexe=./attack-a— the responsible binarya2=O_WRONLY|O_CREAT|O_APPEND— a write operation
The ausearch output shows:
key=oakley&key=squeaky— the watch rule foroakley.txtandsqueaky.txtfiredexe=./attack-b— the responsible binarya2=O_WRONLY|O_CREAT|O_APPEND— a write operation
The ausearch output shows:
key=precipitation— the watch rule forprecipitation.csvfiredexe=./attack-c— the responsible binarya2=O_WRONLY|O_CREAT|O_APPEND— a write operation
| Attack Binary | Modified File(s) | Operation |
|---|---|---|
attack-a |
cloudia.txt |
O_WRONLY|O_CREAT|O_APPEND |
attack-b |
oakley.txt, squeaky.txt |
O_WRONLY|O_CREAT|O_APPEND |
attack-c |
precipitation.csv |
O_WRONLY|O_CREAT|O_APPEND |
-
The first set of rules I wrote used
~/protected_files/...as the watch path. The rules loaded without error but never fired so I knew auditd treats the tilde literally. Switching to the full absolute path fixed it. -
At one point a rule had an incorrect path which caused
auditctl -Rto exit withres=noand stop processing the file entirely, meaning none of the rules below it loaded. I didn't catch it until I ransudo auditctl -land noticed the list was shorter than expected. Now I verify loaded rules after every reload. -
I initially ran the binaries from my working directory and got no log entries. Running
strings attack-a | grep protectedrevealed they expected files at a completely different hardcoded path. Once I recreated that directory structure the attacks worked. -
After copying the protected files with
sudo cp, they were owned by root so the binaries couldn't write to them — again no log entries. Asudo chown -R kali:kali /home/codepathresolved it. -
In a real environment, an attacker with root access can wipe the audit log entirely. Forwarding logs to a remote SIEM in real time ensures the record survives even if the host is compromised.
- auditd — Linux kernel audit daemon that writes events to
/var/log/audit/audit.log - auditctl — Set and verified watch rules in the running kernel
- augenrules — Loaded the persistent rules file into the running kernel without a restart
- ausearch — Queried the audit log by executable (
-x) and usedgrepto filter output by attack file name - strings — Inspected the attack binaries to discover the hardcoded protected files path
