Incident Response Playbook
Objective
Design and implement an automated incident response playbook for three common security scenarios: SSH brute-force attack detection and containment, web application log anomaly detection (potential SQL injection), and unauthorized privilege escalation. Each playbook combines detection logic (log parsing + threshold rules), automated containment actions (firewall block, account suspension), evidence collection, and notification. The project demonstrates core SOAR (Security Orchestration, Automation and Response) concepts using Bash scripting and Linux native tools.
Tools & Technologies
Bash— playbook automation scriptingauditd + ausearch— kernel-level event detectionjournalctl / grep / awk— log analysis and parsingiptables— automated IP blocking during containmentusermod / passwd -l— account suspension commandsss / lsof / ps— live system state collectiontar / sha256sum— evidence packaging and integritymail / curl— alert notificationsinotifywait— real-time file system event monitoring
Architecture Overview
Step-by-Step Process
The playbook monitors /var/log/auth.log for repeated failed password attempts from the same source IP. When threshold exceeded, the IP is immediately blocked with iptables and evidence is collected.
#!/usr/bin/env bash
# /usr/local/ir/playbooks/ssh_bruteforce.sh
set -euo pipefail
THRESHOLD=10
TIMEWINDOW=300 # seconds
LOGFILE="/var/log/auth.log"
EVIDENCE_DIR="/var/ir/evidence/$(date +%Y%m%d_%H%M%S)_ssh_bruteforce"
ALERT_EMAIL="[email protected]"
collect_failed_ips() {
# Count failures per IP in the last $TIMEWINDOW seconds
local cutoff=$(date -d "@$(($(date +%s) - TIMEWINDOW))" '+%b %_d %H:%M:%S')
awk -v cutoff="$cutoff" -v threshold="$THRESHOLD" '
/Failed password/ {
match($0, /from ([0-9.]+)/, arr)
count[arr[1]]++
}
END {
for (ip in count)
if (count[ip] >= threshold)
print ip
}
' "$LOGFILE"
}
block_ip() {
local ip="$1"
# Check if already blocked
iptables -C INPUT -s "$ip" -j DROP 2>/dev/null && return
iptables -I INPUT 1 -s "$ip" -j DROP
logger -t IR-PLAYBOOK "CONTAINED: Blocked $ip via iptables (SSH brute force)"
}
collect_evidence() {
local ip="$1"
mkdir -p "$EVIDENCE_DIR"
# Auth log excerpts
grep "$ip" "$LOGFILE" > "$EVIDENCE_DIR/auth_log_excerpt.txt"
# Current connections from that IP
ss -tnp | grep "$ip" > "$EVIDENCE_DIR/active_connections.txt" 2>/dev/null || true
# System state
ps aux > "$EVIDENCE_DIR/process_list.txt"
who > "$EVIDENCE_DIR/logged_in_users.txt"
# Package evidence with hash
tar czf "${EVIDENCE_DIR}.tar.gz" "$EVIDENCE_DIR"
sha256sum "${EVIDENCE_DIR}.tar.gz" > "${EVIDENCE_DIR}.sha256"
}
notify() {
local ip="$1" count="$2"
mail -s "[IR] SSH Brute Force Blocked: $ip ($count attempts)" "$ALERT_EMAIL" <
Monitors Apache access logs for SQL injection signatures using regex pattern matching. Automatically extracts the attacking IP, logs the attack signature, and adds a temporary firewall block.
#!/usr/bin/env bash
# /usr/local/ir/playbooks/web_anomaly.sh
APACHE_LOG="/var/log/apache2/access.log"
SQLI_PATTERNS='(union|select|insert|drop|exec|xp_|0x[0-9a-f]+|--|\/\*|\*\/|char\(|concat\()'
EVIDENCE_DIR="/var/ir/evidence/$(date +%Y%m%d_%H%M%S)_web_anomaly"
detect_sqli() {
grep -iE "$SQLI_PATTERNS" "$APACHE_LOG" | \
awk '{print $1}' | sort | uniq -c | sort -rn | \
awk '$1 > 5 {print $2}' # IPs with >5 suspicious requests
}
while IFS= read -r ip; do
mkdir -p "$EVIDENCE_DIR"
# Extract matching log lines
grep "$ip" "$APACHE_LOG" | grep -iE "$SQLI_PATTERNS" \
> "$EVIDENCE_DIR/sqli_attempts_${ip//./_}.txt"
# Count attack requests
count=$(wc -l < "$EVIDENCE_DIR/sqli_attempts_${ip//./_}.txt")
# Block IP
iptables -I INPUT 1 -s "$ip" -j DROP
logger -t IR-PLAYBOOK "CONTAINED: Web anomaly from $ip ($count requests) — IP blocked"
echo "Blocked $ip — $count suspicious web requests"
done < <(detect_sqli)
Uses ausearch to detect unusual sudo usage by non-privileged accounts or failed su attempts. When triggered, the account is locked and a full audit trail is extracted.
#!/usr/bin/env bash
# /usr/local/ir/playbooks/privesc.sh
EVIDENCE_DIR="/var/ir/evidence/$(date +%Y%m%d_%H%M%S)_privesc"
ADMIN_USERS=("labadmin" "ops")
detect_privesc() {
# Find users who ran sudo and are NOT in the admin list
ausearch -m USER_CMD -ts today 2>/dev/null | \
grep -oP 'acct="[^"]+"' | \
cut -d'"' -f2 | sort -u | while read -r user; do
if [[ ! " ${ADMIN_USERS[*]} " =~ " $user " ]]; then
echo "$user"
fi
done
}
respond_to_privesc() {
local user="$1"
mkdir -p "$EVIDENCE_DIR"
# Collect audit events for this user
ausearch -ua "$user" -ts today > "$EVIDENCE_DIR/audit_${user}.txt" 2>/dev/null
# Get current sessions
who | grep "^$user" > "$EVIDENCE_DIR/sessions_${user}.txt" || true
# Lock the account
usermod -L "$user"
passwd -e "$user" # force password change on next login
logger -t IR-PLAYBOOK "CONTAINED: Account $user locked (privilege escalation detected)"
# Kill active sessions
pkill -u "$user" -KILL || true
echo "Account $user locked — sessions terminated"
}
while IFS= read -r user; do
respond_to_privesc "$user"
done < <(detect_privesc)
Created a persistent watchdog service using inotifywait and a systemd service to trigger the appropriate playbook when log files change and thresholds are met.
#!/usr/bin/env bash
# /usr/local/ir/watchdog.sh — monitors logs and triggers playbooks
PLAYBOOK_DIR="/usr/local/ir/playbooks"
# Watch auth.log for SSH events
inotifywait -m -e modify /var/log/auth.log 2>/dev/null | while read -r; do
bash "$PLAYBOOK_DIR/ssh_bruteforce.sh" &
done &
# Watch apache log for web events
inotifywait -m -e modify /var/log/apache2/access.log 2>/dev/null | while read -r; do
bash "$PLAYBOOK_DIR/web_anomaly.sh" &
done &
wait
# /etc/systemd/system/ir-watchdog.service
[Unit]
Description=Incident Response Watchdog
After=network.target auditd.service
[Service]
Type=simple
ExecStart=/usr/local/ir/watchdog.sh
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Simulated each incident scenario in a controlled lab environment to verify detection, containment, and evidence collection worked correctly without false positives on legitimate traffic.
# Simulate SSH brute force (from test machine)
for i in $(seq 1 15); do
ssh -o StrictHostKeyChecking=no [email protected] 2>/dev/null || true
done
# Verify IP was blocked
sudo iptables -L INPUT -n | grep "$(hostname -I | awk '{print $1}')"
# Simulate SQLi web request
curl "http://192.168.56.20/?id=1%20UNION%20SELECT%201,2,3--"
# Trigger privilege escalation check
sudo ausearch -m USER_CMD -ts today | tail -20
# Review IR evidence directory
ls -lh /var/ir/evidence/
tar -tzvf /var/ir/evidence/*.tar.gz | head -20
Complete Workflow
Challenges & Solutions
- False positives blocking legitimate scanning tools — The SSH threshold was too low (5 attempts). During testing, a legitimate vulnerability scanner was blocked. Raised threshold to 10 and added the management subnet to an ignore list.
- Evidence collection script timing out on large logs — Grepping through a multi-GB auth.log was slow. Changed to use
journalctl --since="1 hour ago"for recent entries and tail-based log searches. - inotifywait not available by default — Required installing
inotify-tools. Added package installation as a prerequisite check in the setup script. - pkill terminating legitimate sessions — The privilege escalation playbook killed ALL sessions for the user, including legitimate ones. Added a time-window check — only kill sessions started within 10 minutes of the detected event.
Key Takeaways
- Incident response playbooks must balance speed of response against false positive risk — overly aggressive automation can lock out legitimate users and create its own service disruption.
- Evidence integrity is non-negotiable — every collected artifact must be hashed with SHA-256 immediately after collection to ensure chain of custody for forensic purposes.
- SOAR principles apply even with basic Bash scripts — detection, triage, containment, evidence, and notification are the universal phases regardless of tooling complexity.
- All IR automation must be tested against simulated incidents before production deployment — an untested playbook that fires wrong during a real incident is worse than no automation.