Intrusion Detection Script in Practice: Automatic Monitoring of Abnormal Linux Logins and Suspicious Processes
A drop-in Linux intrusion-detection script: SSH brute force, anomalous UID 0 accounts, temp-dir backdoors, and crontab changes. Cron execution plus WeCom alerts. Low-cost first line of defense.
Intrusion-detection scripts are a practical control for small and mid-size Linux servers. Without a commercial SIEM, a Shell script that periodically checks login logs, account changes, temp directories, and cron can catch anomalies in the early phase—backdoor implant and persistence. This article provides a drop-in Linux IDS script: full code, cron config, and verification.
Applicable Scenarios
- Cloud or dedicated servers with no dedicated security staff on watch
- Limited budget; Wazuh, ELK, or similar centralized monitoring is not deployed yet
- Need immediate detection of successful SSH brute force, unexpected privileged accounts, and temp-dir backdoors
- MLPS compliance requires basic security audit and alerting on servers
Prerequisites
- root or sudo
- CentOS/RHEL login log at `/var/log/secure`; Debian/Ubuntu at `/var/log/auth.log`
- cron running (`systemctl status crond` or `systemctl status cron`)
- A WeCom/DingTalk group-bot Webhook URL for alerts (optional; otherwise write a local log)
How It Works
The script checks four traces most often abused after intrusion:
- Abnormal logins: SSH brute force leaves large numbers of `Failed password` records in `/var/log/secure` (or `auth.log`); counting source-IP frequency identifies brute force. Success looks like `Accepted password` from an unusual source IP
- Accounts and privilege: after obtaining root, attackers often add a hidden UID 0 account. Counting UID 0 accounts in `/etc/passwd` is the simplest check
- Suspicious processes and backdoor files: WebShell compilers and miner malware often live in writable dirs such as `/tmp` and `/dev/shm`. Normal business does not place executables there
- Persistence (cron): scheduled tasks are the most common persistence method. Snapshot crontab content as a baseline; alert on any change
Procedure
1. Write the Detection Script
Create `/usr/local/bin/id_check.sh`:
#!/bin/bash
# Linux intrusion-detection script /usr/local/bin/id_check.sh
WEBHOOK="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=REPLACE_WITH_YOUR_KEY"
AUTHLOG=/var/log/secure # Debian/Ubuntu: change to /var/log/auth.log
notify() {
curl -s -X POST -H 'Content-Type: application/json' \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"[Intrusion Detection] $1\"}}" "$WEBHOOK" >/dev/null
echo "$(date '+%F %T') $1" >> /var/log/id_check.log
}
# 1) SSH failed-login stats: flag brute force if a single IP exceeds 50 recent failures
grep 'Failed password' "$AUTHLOG" 2>/dev/null | awk -F'from ' '{print $2}' \
| awk '{print $1}' | sort | uniq -c | sort -rn | head -10 | \
while read cnt ip; do
[ "$cnt" -gt 50 ] && notify "SSH brute-force alert: $ip failed login $cnt times"
done
# 2) Check for anomalous UID 0 accounts (none should exist besides root)
awk -F: '$3==0{print $1}' /etc/passwd | while read u; do
[ "$u" != "root" ] && notify "Anomalous UID 0 account found: $u"
done
# 3) Check for executables under /tmp and /dev/shm
EXE=$(find /tmp /dev/shm -type f -perm -0111 2>/dev/null)
[ -n "$EXE" ] && notify "Executable found in temp directory: $EXE"
# 4) Cron baseline comparison (first run creates the baseline)
BASE=/var/lib/.cron_baseline
CUR=$(cat /var/spool/cron/* /etc/crontab 2>/dev/null | md5sum | awk '{print $1}')
if [ -f "$BASE" ]; then
[ "$CUR" != "$(cat "$BASE")" ] && notify "Crontab changed; review immediately"
else
echo "$CUR" > "$BASE"
fi
Set permissions so only root can read/write, to limit tampering:
chmod 700 /usr/local/bin/id_check.sh
chattr +i /usr/local/bin/id_check.sh
2. Configure Cron
Create `/etc/cron.d/id_check`, run every 30 minutes:
*/30 * * * * root /usr/local/bin/id_check.sh
Reload cron:
# CentOS/RHEL
systemctl restart crond
# Debian/Ubuntu
systemctl restart cron
Verification
- Run the script by hand and trace output: `bash -x /usr/local/bin/id_check.sh`. Confirm no syntax errors on each check
- Simulate SSH brute force: fail password login locally 50+ times (`ssh wronguser@127.0.0.1`), then confirm an alert arrives
- Simulate a backdoor file: `touch /tmp/test.sh && chmod +x /tmp/test.sh`. The script should fire the temp-dir alert. Delete after verification
- Read the local alert log: `tail /var/log/id_check.log`
FAQ
FAQ 1: Too many alerts. How to cut false positives?
SSH brute-force alerts dominate. Priority order: ① switch to key login and disable password auth (`PasswordAuthentication no`); brute force dies; ② raise the threshold from 50 to 200; ③ hand high-frequency source IPs to `fail2ban` for auto-ban; the script only does fallback alerting.
FAQ 2: Will the script miss events after log rotate?
Yes. After `logrotate`, old failure records move into `secure-*.gz`. The script reads only the current log, so the stats window shrinks. Fix: shorten the interval to 15 minutes, or also unpack and scan the latest rotated file before counting. If the script reads an empty log, first confirm rsyslog is up: `systemctl status rsyslog`.
FAQ 3: How to stop an attacker from disabling the script itself?
Above: `chmod 700` plus `chattr +i` to lock the file. Also sync the script and baseline to a remote copy, periodically compare md5, and alert immediately on content change.
Summary
Value of an IDS script: low cost, fast to ship. It covers four high-frequency traces: login anomalies, account privilege, temp-dir backdoors, and crontab changes. It does not replace a real HIDS, but it puts a first alert line on servers with no central monitoring. Next: pair with fail2ban auto-ban and key-only SSH to keep shrinking attack surface.