← News
/PLAYBOOK · 2026-08-03

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

How It Works

Core idea: diff plus behavioral signals. Four dimensions catch intrusion traces:

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.