Linux Intrusion Detection Scripting in Practice: Automatically Finding Intrusion Traces
Hands-on Linux intrusion detection scripting: auto-find intrusion traces via file-hash baselines, UID=0 account audit, failed-login stats, and port-baseline comparison. Complete runnable Python and crontab config.
Applicable Scenarios
Intrusion detection scripts are a core tool in server security incident response. This article centers on a Linux intrusion detection script and shows how to write an unattended detector for new users, anomalous logins, malicious processes, and tampered files. Security ops and server admins can land it in compliance assessments and daily inspections.
Prerequisites
- OS: Linux (kernel 3.10+), root required for audit commands
- Runtime: Python 3.6+ and bash 4+
- Prefer a dedicated audit host, or keep alert logs off the production box
How It Works
Core idea: diff plus behavioral signals. Four dimensions catch intrusion traces:
- File integrity: compare mtime and hashes of sensitive files (/etc/passwd, /etc/shadow, /etc/ssh/sshd_config, and others) to catch tampering and planted backdoor files
- Account audit: diff user snapshots; catch hidden accounts attackers create (especially high-risk UID=0)
- Login and process monitoring: count failed logins; inspect unexpected listening ports
- Cron and startup items: inspect crontab and systemd services for persistence backdoors
The script writes findings to logs. Anomalies fire a Webhook in real time. Detect first, then contain.
Procedure
Step 1: Create script directories and the sensitive-file list
mkdir -p /opt/idetect/{bin,data,log}
cd /opt/idetect
cat > /opt/idetect/data/file_list.txt <<'LIST_END'
/etc/passwd
/etc/shadow
/etc/group
/etc/sudoers
/etc/ssh/sshd_config
/etc/rc.local
/etc/crontab
LIST_END
Step 2: Write the baseline snapshot script
#!/bin/bash
# /opt/idetect/bin/snapshot.sh - generate user and listening-port baseline
getent passwd | cut -d: -f1,3,6 > /opt/idetect/data/users.baseline
ss -tlnp | md5sum > /opt/idetect/data/ports.md5
echo "[$(date '+%F %T')] snapshot updated" >> /opt/idetect/log/snapshot.log
Step 3: Write the core detection script detect.py
#!/usr/bin/env python3
# /opt/idetect/bin/detect.py - Linux intrusion detection script (core)
import hashlib, os, subprocess, sys, datetime
BASE = '/opt/idetect'
ALERT = []
def run(cmd):
return subprocess.run(cmd, shell=True, capture_output=True,
text=True).stdout.strip()
def check_file_integrity():
"""Compare hashes of sensitive files; catch tampering and planted backdoors"""
flist = f'{BASE}/data/file_list.txt'
baseline = set(open(f'{BASE}/data/files.baseline').read().splitlines())
for path in [l.strip() for l in open(flist) if l.strip()]:
if not os.path.exists(path):
ALERT.append(f'[FILE MISSING] {path}')
continue
h = hashlib.sha256(open(path, 'rb').read()).hexdigest()
if f'{h} {path}' not in baseline:
ALERT.append(f'[FILE CHANGED] {path} (sha256: {h[:16]})')
def check_new_users():
"""Audit UID=0 accounts and newly added users"""
for line in run('getent passwd').splitlines():
name, _, uid = line.split(':')[:3]
if uid == '0' and name != 'root':
ALERT.append(f'[HIGH-RISK ACCOUNT] UID=0 non-root: {name}')
def check_login_failures():
"""Count failed logins in the last 24 hours (brute-force indicator)"""
log = '/var/log/auth.log'
if os.path.exists('/var/log/secure'):
log = '/var/log/secure'
day = datetime.datetime.now().strftime('%b %e')
fails = run(f"grep 'Failed password' {log} | grep -c '{day}'")
if fails.isdigit() and int(fails) > 50:
ALERT.append(f'[BRUTE-FORCE SIGNS] 24h failed logins: {fails}')
def check_listening_ports():
"""Detect unexpected listening ports (diff against baseline)"""
cur = run('ss -tlnp | md5sum')
if cur != open(f'{BASE}/data/ports.md5').read().strip():
ALERT.append('[PORT CHANGE] Listening ports differ from baseline; review manually')
def main():
check_file_integrity(); check_new_users()
check_login_failures(); check_listening_ports()
if ALERT:
msg = '\n'.join(ALERT)
print(f'[ALERT {datetime.datetime.now():%F %T}]\n{msg}')
# Alert: uncomment and replace the URL to hook up Webhook
# run(f'curl -s -X POST -d "alert={msg}" http://alert.example.com/hook')
else:
print('[OK] No anomalies found')
if __name__ == '__main__':
main()
Step 4: Initialize the baseline and install cron jobs
chmod +x /opt/idetect/bin/snapshot.sh /opt/idetect/bin/detect.py
/opt/idetect/bin/snapshot.sh
# Generate file-hash baseline (in list order)
while read f; do sha256sum "$f"; done < /opt/idetect/data/file_list.txt > /opt/idetect/data/files.baseline
# Update snapshot at 03:00 daily; run detection at 06:10
(crontab -l 2>/dev/null; echo '0 3 * * * /opt/idetect/bin/snapshot.sh') | crontab -
(crontab -l 2>/dev/null; echo '10 6 * * * /usr/bin/python3 /opt/idetect/bin/detect.py >> /opt/idetect/log/detect.log 2>&1') | crontab -
Verification
# 1. Run in a clean state
python3 /opt/idetect/bin/detect.py
# Expected: [OK] No anomalies found
# 2. Plant one "intrusion trace" to verify detection
useradd -o -u 0 backdoor_test
python3 /opt/idetect/bin/detect.py
# Expected: [ALERT ...] [HIGH-RISK ACCOUNT] UID=0 non-root: backdoor_test
userdel backdoor_test
Expected output must include [HIGH-RISK ACCOUNT] UID=0 non-root: backdoor_test — the script catches the cloned-root trick attackers use. `crontab -l` should show both jobs.
FAQ
Q1: Too many false positives?
After first run, fold legitimate changes into the baseline: regenerate file hashes over `files.baseline`, refresh `ports.md5`. Grade alerts — file integrity once a day, failed-login checks more often — to cut noise.
Q2: What if an attacker tampers with or stops the script?
Tighten `/opt/idetect` to root-only (`chmod -R 700 /opt/idetect`). Offsite-backup baselines. Stronger: AIDE the script itself, and forward results via syslog to a separate log host so the attacker cannot wipe the scene.
Q3: Real-time alerts instead of once a day?
Run detect.py every 5 minutes. Wire the alert branch to WeCom/DingTalk Webhook: uncomment the sample curl and point it at the bot URL.
Summary
A drop-in Linux intrusion detection script covering four common dimensions: file-hash baseline, UID=0 account audit, failed-login counts, and port-baseline diffs. Pair with crontab and Webhook alerts for unattended inspection. In production, add ELK log centralization and AIDE integrity checks for defense in depth.