Files
setec_cdm/setec-web/monitoring.py

360 lines
15 KiB
Python
Raw Normal View History

# IDS, log monitoring, login tracking, file integrity, process auditing, and alerting
# Each function returns a bash command string that app.py executes via ssh_run()
def auth_log_cmd(lines=100):
"""Return bash cmd to get recent auth log entries (failed/successful logins)."""
return (
"echo '=== Recent Auth Log Entries ===' && "
"if [ -f /var/log/auth.log ]; then "
f" grep -E '(Failed|Accepted|Invalid|session opened|session closed|authentication failure)' /var/log/auth.log | tail -n {lines}; "
"elif command -v journalctl >/dev/null 2>&1; then "
f" journalctl -u sshd -u ssh --no-pager -n {lines} 2>&1; "
"else "
" echo 'No auth.log found and journalctl not available'; "
"fi"
)
def login_tracker_cmd():
"""Return bash cmd to show login attempts with IP, count, and geo info."""
return (
"echo '=== Login Tracker: Top 20 IPs ===' && "
"echo '' && "
"echo '--- Failed Logins ---' && "
"if [ -f /var/log/auth.log ]; then "
" FAILED_IPS=$(grep -oP 'Failed password.*from \\K[0-9.]+' /var/log/auth.log 2>/dev/null | "
" sort | uniq -c | sort -rn | head -20); "
"else "
" FAILED_IPS=$(journalctl -u sshd -u ssh --no-pager 2>/dev/null | "
" grep -oP 'Failed password.*from \\K[0-9.]+' | "
" sort | uniq -c | sort -rn | head -20); "
"fi && "
"if [ -z \"$FAILED_IPS\" ]; then "
" echo 'No failed login attempts found'; "
"else "
" echo \"$FAILED_IPS\" | while read COUNT IP; do "
" GEO=''; "
" if command -v geoiplookup >/dev/null 2>&1; then "
" GEO=$(geoiplookup \"$IP\" 2>/dev/null | head -1 | sed 's/GeoIP Country Edition: //'); "
" elif command -v whois >/dev/null 2>&1; then "
" GEO=$(whois \"$IP\" 2>/dev/null | grep -i -m1 'country' | awk '{print $NF}'); "
" fi; "
" printf '%6s %-16s %s\\n' \"$COUNT\" \"$IP\" \"$GEO\"; "
" done; "
"fi && "
"echo '' && "
"echo '--- Accepted Logins ---' && "
"if [ -f /var/log/auth.log ]; then "
" ACCEPT_IPS=$(grep -oP 'Accepted.*from \\K[0-9.]+' /var/log/auth.log 2>/dev/null | "
" sort | uniq -c | sort -rn | head -20); "
"else "
" ACCEPT_IPS=$(journalctl -u sshd -u ssh --no-pager 2>/dev/null | "
" grep -oP 'Accepted.*from \\K[0-9.]+' | "
" sort | uniq -c | sort -rn | head -20); "
"fi && "
"if [ -z \"$ACCEPT_IPS\" ]; then "
" echo 'No accepted logins found'; "
"else "
" echo \"$ACCEPT_IPS\" | while read COUNT IP; do "
" GEO=''; "
" if command -v geoiplookup >/dev/null 2>&1; then "
" GEO=$(geoiplookup \"$IP\" 2>/dev/null | head -1 | sed 's/GeoIP Country Edition: //'); "
" elif command -v whois >/dev/null 2>&1; then "
" GEO=$(whois \"$IP\" 2>/dev/null | grep -i -m1 'country' | awk '{print $NF}'); "
" fi; "
" printf '%6s %-16s %s\\n' \"$COUNT\" \"$IP\" \"$GEO\"; "
" done; "
"fi"
)
def active_sessions_cmd():
"""Return bash cmd to show who is currently logged in."""
return (
"echo '=== Currently Logged In ===' && "
"w 2>&1 && "
"echo '' && "
"echo '=== Active Sessions (who) ===' && "
"who 2>&1 && "
"echo '' && "
"echo '=== Recent Logins (last 10) ===' && "
"last -10 2>&1"
)
def file_integrity_check_cmd(paths="/etc /usr/bin /usr/sbin /var/www"):
"""Return bash cmd to create/check file integrity baseline."""
db = "/var/lib/setec-integrity.db"
return (
f"if [ -f {db} ]; then "
f" echo '=== File Integrity Check (comparing to baseline) ===' && "
f" CURRENT=$(mktemp) && "
f" find {paths} -type f -exec md5sum {{}} + 2>/dev/null | sort -k2 > \"$CURRENT\" && "
f" BASELINE=$(sort -k2 {db}) && "
" echo '' && "
" echo '--- Modified Files ---' && "
f" diff <(echo \"$BASELINE\") \"$CURRENT\" 2>/dev/null | grep '^[<>]' | head -50 && "
" echo '' && "
" echo '--- New Files (not in baseline) ---' && "
f" comm -13 <(awk '{{print $2}}' {db} | sort) <(awk '{{print $2}}' \"$CURRENT\" | sort) | head -50 && "
" echo '' && "
" echo '--- Deleted Files (in baseline but missing) ---' && "
f" comm -23 <(awk '{{print $2}}' {db} | sort) <(awk '{{print $2}}' \"$CURRENT\" | sort) | head -50 && "
" MODIFIED=$(diff <(echo \"$BASELINE\") \"$CURRENT\" 2>/dev/null | grep -c '^[<>]' || true) && "
" echo '' && "
" echo \"Summary: $MODIFIED differences found\" && "
" rm -f \"$CURRENT\"; "
"else "
f" echo 'No baseline found at {db}. Creating initial baseline...' && "
f" find {paths} -type f -exec md5sum {{}} + 2>/dev/null | sort -k2 > {db} && "
f" COUNT=$(wc -l < {db}) && "
" echo \"Baseline created with $COUNT files\"; "
"fi"
)
def file_integrity_init_cmd(paths="/etc /usr/bin /usr/sbin /var/www"):
"""Return bash cmd to initialize/reset the integrity baseline."""
db = "/var/lib/setec-integrity.db"
return (
f"echo '=== Initializing File Integrity Baseline ===' && "
f"mkdir -p /var/lib && "
f"find {paths} -type f -exec md5sum {{}} + 2>/dev/null | sort -k2 > {db} && "
f"COUNT=$(wc -l < {db}) && "
f"echo \"Baseline created at {db} with $COUNT files\" && "
f"echo \"Monitored paths: {paths}\""
)
def process_audit_cmd():
"""Return bash cmd to find suspicious processes."""
return (
"echo '=== Process Security Audit ===' && "
"echo '' && "
"echo '--- Listening Ports with Processes ---' && "
"ss -tlnp 2>&1 && "
"echo '' && "
"echo '--- UDP Listening Ports ---' && "
"ss -ulnp 2>&1 && "
"echo '' && "
"echo '--- Processes Running as Root (non-kernel) ---' && "
"ps aux --sort=-%mem 2>/dev/null | awk '$1==\"root\" && $11!~/^\\[/' | head -30 && "
"echo '' && "
"echo '--- Processes with Deleted Binaries (suspicious) ---' && "
"ls -la /proc/*/exe 2>/dev/null | grep '(deleted)' || echo 'None found' && "
"echo '' && "
"echo '--- Recently Modified Binaries (last 7 days) ---' && "
"find /usr/bin /usr/sbin /usr/local/bin -type f -mtime -7 -ls 2>/dev/null | head -30 || echo 'None found' && "
"echo '' && "
"echo '--- Unusual SUID/SGID Binaries ---' && "
"find /usr/bin /usr/sbin /usr/local/bin /tmp /var/tmp -type f \\( -perm -4000 -o -perm -2000 \\) -ls 2>/dev/null | head -30 || echo 'None found'"
)
def security_log_cmd(lines=100):
"""Return bash cmd to get combined security-relevant logs."""
return (
"echo '=== Combined Security Logs ===' && "
"( "
" [ -f /var/log/auth.log ] && grep -E '(Failed|Accepted|Invalid|authentication failure|sudo|su:)' /var/log/auth.log 2>/dev/null; "
" [ -f /var/log/fail2ban.log ] && tail -50 /var/log/fail2ban.log 2>/dev/null; "
" [ -f /var/log/kern.log ] && grep -iE '(iptables|firewall|segfault|oom)' /var/log/kern.log 2>/dev/null; "
" [ -f /var/log/syslog ] && grep -iE '(security|attack|denied|unauthorized|blocked)' /var/log/syslog 2>/dev/null; "
" journalctl -p warning --since '24 hours ago' --no-pager 2>/dev/null | head -50; "
f") | sort -t' ' -k1,3 2>/dev/null | tail -n {lines}"
)
def alert_setup_cmd(email, webhook_url=""):
"""Return bash cmd to set up alert script for suspicious activity."""
webhook_section = ""
if webhook_url:
webhook_section = (
f" if command -v curl >/dev/null 2>&1; then\\n"
f" curl -s -X POST -H 'Content-Type: application/json' "
f"-d \\\"{{\\\\\\\"text\\\\\\\": \\\\\\\"$MSG\\\\\\\"}}\\\" "
f"'{webhook_url}' >/dev/null 2>&1\\n"
f" fi\\n"
)
script = (
"#!/bin/bash\\n"
"# Setec Labs Security Alert Script\\n"
"LOGFILE=/var/log/setec-alerts.log\\n"
"STATEFILE=/var/lib/setec-alert-state\\n"
"THRESHOLD=10\\n"
"\\n"
"mkdir -p /var/lib\\n"
"touch \\\"$STATEFILE\\\" \\\"$LOGFILE\\\"\\n"
"\\n"
"LAST_CHECK=$(cat \\\"$STATEFILE\\\" 2>/dev/null || echo 0)\\n"
"NOW=$(date +%s)\\n"
"echo \\\"$NOW\\\" > \\\"$STATEFILE\\\"\\n"
"\\n"
"# Check failed SSH logins since last check\\n"
"if [ -f /var/log/auth.log ]; then\\n"
" FAILED=$(grep 'Failed password' /var/log/auth.log 2>/dev/null | wc -l)\\n"
"else\\n"
" FAILED=$(journalctl -u sshd --since '5 minutes ago' --no-pager 2>/dev/null | grep -c 'Failed password')\\n"
"fi\\n"
"\\n"
"# Check fail2ban bans\\n"
"BANS=0\\n"
"if [ -f /var/log/fail2ban.log ]; then\\n"
" BANS=$(grep -c 'Ban' /var/log/fail2ban.log 2>/dev/null || echo 0)\\n"
"fi\\n"
"\\n"
"# Check for new SUID files\\n"
"NEWSUID=$(find /tmp /var/tmp /home -type f -perm -4000 2>/dev/null | wc -l)\\n"
"\\n"
"ALERT=0\\n"
"MSG=\\\"\\\"\\n"
"\\n"
"if [ \\\"$FAILED\\\" -gt \\\"$THRESHOLD\\\" ]; then\\n"
" MSG=\\\"ALERT: $FAILED failed SSH login attempts detected\\\"\\n"
" ALERT=1\\n"
"fi\\n"
"\\n"
"if [ \\\"$NEWSUID\\\" -gt 0 ]; then\\n"
" MSG=\\\"$MSG\\\\nALERT: $NEWSUID SUID files found in /tmp, /var/tmp, or /home\\\"\\n"
" ALERT=1\\n"
"fi\\n"
"\\n"
"if [ \\\"$ALERT\\\" -eq 1 ]; then\\n"
" HOSTNAME=$(hostname)\\n"
" MSG=\\\"[$HOSTNAME] $(date): $MSG\\\"\\n"
" echo \\\"$MSG\\\" >> \\\"$LOGFILE\\\"\\n"
f" if command -v mail >/dev/null 2>&1; then\\n"
f" echo -e \\\"$MSG\\\" | mail -s \\\"Setec Security Alert: $HOSTNAME\\\" {email} 2>/dev/null\\n"
f" fi\\n"
f"{webhook_section}"
"fi\\n"
)
return (
"echo '=== Setting Up Security Alerting ===' && "
f"echo -e '{script}' > /usr/local/bin/setec-alert.sh && "
"chmod +x /usr/local/bin/setec-alert.sh && "
"touch /var/log/setec-alerts.log && "
"(crontab -l 2>/dev/null | grep -v 'setec-alert.sh'; "
"echo '*/5 * * * * /usr/local/bin/setec-alert.sh') | crontab - && "
f"echo 'Alert script installed at /usr/local/bin/setec-alert.sh' && "
f"echo 'Cron job added: runs every 5 minutes' && "
f"echo 'Alert email: {email}'"
)
def alert_status_cmd():
"""Return bash cmd to check if alerting is configured and show recent alerts."""
return (
"echo '=== Alert System Status ===' && "
"echo '' && "
"echo '--- Script ---' && "
"if [ -f /usr/local/bin/setec-alert.sh ]; then "
" echo 'Alert script: INSTALLED'; "
" ls -la /usr/local/bin/setec-alert.sh; "
"else "
" echo 'Alert script: NOT INSTALLED'; "
"fi && "
"echo '' && "
"echo '--- Cron Job ---' && "
"if crontab -l 2>/dev/null | grep -q 'setec-alert.sh'; then "
" echo 'Cron job: ACTIVE'; "
" crontab -l 2>/dev/null | grep 'setec-alert.sh'; "
"else "
" echo 'Cron job: NOT FOUND'; "
"fi && "
"echo '' && "
"echo '--- Recent Alerts (last 20) ---' && "
"if [ -f /var/log/setec-alerts.log ]; then "
" tail -20 /var/log/setec-alerts.log; "
"else "
" echo 'No alert log found'; "
"fi"
)
def alert_remove_cmd():
"""Return bash cmd to remove alerting."""
return (
"echo '=== Removing Security Alerting ===' && "
"(crontab -l 2>/dev/null | grep -v 'setec-alert.sh') | crontab - && "
"rm -f /usr/local/bin/setec-alert.sh && "
"echo 'Alert script removed' && "
"echo 'Cron job removed' && "
"echo 'Alert log preserved at /var/log/setec-alerts.log'"
)
def suid_audit_cmd():
"""Return bash cmd to find all SUID/SGID binaries."""
return (
"echo '=== SUID/SGID Binary Audit ===' && "
"echo '' && "
"echo '--- SUID Binaries ---' && "
"find / -type f -perm -4000 -not -path '/proc/*' -not -path '/sys/*' -ls 2>/dev/null && "
"echo '' && "
"echo '--- SGID Binaries ---' && "
"find / -type f -perm -2000 -not -path '/proc/*' -not -path '/sys/*' -ls 2>/dev/null && "
"echo '' && "
"SUID_COUNT=$(find / -type f -perm -4000 -not -path '/proc/*' -not -path '/sys/*' 2>/dev/null | wc -l) && "
"SGID_COUNT=$(find / -type f -perm -2000 -not -path '/proc/*' -not -path '/sys/*' 2>/dev/null | wc -l) && "
"echo \"Total: $SUID_COUNT SUID, $SGID_COUNT SGID binaries\""
)
def world_writable_cmd():
"""Return bash cmd to find world-writable files/dirs."""
return (
"echo '=== World-Writable Files ===' && "
"find / -type f -perm -0002 "
"-not -path '/proc/*' -not -path '/sys/*' -not -path '/dev/*' "
"-ls 2>/dev/null | head -50 && "
"echo '' && "
"echo '=== World-Writable Directories (without sticky bit) ===' && "
"find / -type d -perm -0002 -not -perm -1000 "
"-not -path '/proc/*' -not -path '/sys/*' -not -path '/dev/*' "
"-ls 2>/dev/null | head -50"
)
def cron_audit_cmd():
"""Return bash cmd to audit all cron jobs on the system."""
return (
"echo '=== Cron Job Audit ===' && "
"echo '' && "
"echo '--- System Crontab (/etc/crontab) ---' && "
"cat /etc/crontab 2>/dev/null || echo 'Not found' && "
"echo '' && "
"echo '--- /etc/cron.d/ ---' && "
"for f in /etc/cron.d/*; do "
" [ -f \"$f\" ] && echo \"== $f ==\" && cat \"$f\" && echo ''; "
"done 2>/dev/null && "
"echo '' && "
"echo '--- /etc/cron.daily/ ---' && "
"ls -la /etc/cron.daily/ 2>/dev/null || echo 'Not found' && "
"echo '' && "
"echo '--- /etc/cron.hourly/ ---' && "
"ls -la /etc/cron.hourly/ 2>/dev/null || echo 'Not found' && "
"echo '' && "
"echo '--- /etc/cron.weekly/ ---' && "
"ls -la /etc/cron.weekly/ 2>/dev/null || echo 'Not found' && "
"echo '' && "
"echo '--- /etc/cron.monthly/ ---' && "
"ls -la /etc/cron.monthly/ 2>/dev/null || echo 'Not found' && "
"echo '' && "
"echo '--- User Crontabs ---' && "
"for user in $(cut -f1 -d: /etc/passwd 2>/dev/null); do "
" CRON=$(crontab -u \"$user\" -l 2>/dev/null); "
" if [ -n \"$CRON\" ]; then "
" echo \"== $user ==\"; "
" echo \"$CRON\"; "
" echo ''; "
" fi; "
"done && "
"echo '' && "
"echo '--- Systemd Timers ---' && "
"systemctl list-timers --all --no-pager 2>/dev/null || echo 'systemctl not available'"
)