#!/bin/bash # # Suspicious Login Monitor - Integrated Security Analysis & Compromise Detection # Detects suspicious login patterns, correlates with web attack activity, # and checks for actual system compromise indicators # Supports: cPanel, Plesk, InterWorx, Standalone # # Get script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TOOLKIT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" # Configuration SUSPICIOUS_LOGIN_AUTO_BLOCK="${SUSPICIOUS_LOGIN_AUTO_BLOCK:-yes}" SUSPICIOUS_LOGIN_AUTO_SCAN="${SUSPICIOUS_LOGIN_AUTO_SCAN:-yes}" SUSPICIOUS_LOGIN_EMAIL_ALERTS="${SUSPICIOUS_LOGIN_EMAIL_ALERTS:-no}" # Risk thresholds RISK_CRITICAL=85 RISK_HIGH=70 RISK_MEDIUM=50 # Integration paths BOT_ANALYZER="$TOOLKIT_ROOT/modules/security/bot-analyzer.sh" MALWARE_SCANNER="$TOOLKIT_ROOT/modules/security/malware-scanner.sh" IP_REPUTATION_LIB="$TOOLKIT_ROOT/lib/ip-reputation.sh" THREAT_INTEL_LIB="$TOOLKIT_ROOT/lib/threat-intelligence.sh" # Temp files TMP_DIR="/tmp" REPORT_FILE="$TMP_DIR/suspicious_login_report_$(date +%Y%m%d_%H%M%S).txt" SSH_EVENTS="$TMP_DIR/ssh_events_$$.txt" PANEL_EVENTS="$TMP_DIR/panel_events_$$.txt" SUDO_EVENTS="$TMP_DIR/sudo_events_$$.txt" SUSPICIOUS_IPS="$TMP_DIR/suspicious_ips_$$.txt" # Analysis period (default: last 24 hours) HOURS="${1:-24}" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # Cleanup on exit trap cleanup EXIT cleanup() { rm -f "$SSH_EVENTS" "$PANEL_EVENTS" "$SUDO_EVENTS" "$SUSPICIOUS_IPS" 2>/dev/null } # Detect control panel detect_panel() { if [ -d /usr/local/cpanel ]; then echo "cpanel" elif [ -d /usr/local/psa ]; then echo "plesk" elif [ -d /home/interworx ]; then echo "interworx" else echo "standalone" fi } # Get server IPs to exclude get_server_ips() { { echo "127.0.0.1" echo "::1" ip addr | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v "^127\." ip addr | grep -oP '(?<=inet6\s)[0-9a-f:]+' | grep -v "^::1" } | sort -u } # # SSH LOG PARSING # parse_ssh_logins() { local hours=$1 local secure_log="/var/log/secure" # Use auth.log on Debian-based systems if [ ! -f "$secure_log" ] && [ -f "/var/log/auth.log" ]; then secure_log="/var/log/auth.log" fi if [ ! -f "$secure_log" ]; then return 0 fi echo " Parsing SSH login attempts..." >&2 # Parse successful SSH logins awk -v hours="$hours" ' BEGIN { # Calculate time threshold cmd = "date -d \"" hours " hours ago\" +%s" cmd | getline threshold close(cmd) } /sshd.*Accepted/ { # Parse timestamp timestamp = $1 " " $2 " " $3 cmd = "date -d \"" timestamp "\" +%s 2>/dev/null" cmd | getline epoch close(cmd) if (epoch < threshold) next # Extract details user = "" ip = "" method = "" # Accepted password for user from ip if (match($0, /Accepted (password|publickey) for ([^ ]+) from ([^ ]+)/)) { method = (index($0, "publickey") > 0) ? "key" : "password" for (i=1; i<=NF; i++) { if ($i == "for") user = $(i+1) if ($i == "from") ip = $(i+1) } } if (user != "" && ip != "") { print timestamp "|" user "|" ip "|ssh|" method "|success" } } /sshd.*Failed/ { # Parse timestamp timestamp = $1 " " $2 " " $3 cmd = "date -d \"" timestamp "\" +%s 2>/dev/null" cmd | getline epoch close(cmd) if (epoch < threshold) next # Extract details user = "" ip = "" # Failed password for user from ip if (match($0, /Failed password for/)) { for (i=1; i<=NF; i++) { if ($i == "for") user = $(i+1) if ($i == "from") ip = $(i+1) } } if (user != "" && ip != "") { print timestamp "|" user "|" ip "|ssh|password|failed" } } ' "$secure_log" > "$SSH_EVENTS" } # # WTMP LOG PARSING (Universal - All Panels) # parse_wtmp_logins() { local hours=$1 if [ ! -f /var/log/wtmp ]; then return 0 fi echo " Parsing session history (wtmp)..." >&2 # Calculate cutoff date for last command local cutoff_date=$(date -d "$hours hours ago" "+%Y-%m-%d %H:%M:%S") # Use last command to parse binary wtmp log # Format: user tty ip datetime - datetime (duration) last -F 2>/dev/null | awk -v cutoff="$cutoff_date" ' BEGIN { # Convert cutoff to epoch cmd = "date -d \"" cutoff "\" +%s" cmd | getline cutoff_epoch close(cmd) } /^[a-zA-Z0-9]/ && !/^reboot/ && !/^wtmp/ { # Skip header lines if ($1 == "USER" || $1 == "wtmp") next user = $1 tty = $2 ip = $3 # Parse login time: "Mon Feb 2 18:59:03 2026" month = $4 day = $5 time = $6 year = $7 # Check if still logged in or logged out if ($9 == "still") { status = "active" } else { status = "success" } # Build timestamp for comparison timestamp = month " " day " " time " " year cmd = "date -d \"" timestamp "\" +%s 2>/dev/null" cmd | getline login_epoch close(cmd) # Only include logins within time window if (login_epoch >= cutoff_epoch) { # Normalize timestamp format simple_timestamp = month " " day " " time # Determine auth method (if from pts/pty, assume key; if no IP, skip) method = "key" if (ip == "" || ip == "-" || ip == ":0" || ip == "localhost") { next # Skip local logins } print simple_timestamp "|" user "|" ip "|ssh|" method "|" status } } ' >> "$SSH_EVENTS" } # # BTMP LOG PARSING (Universal - All Panels) # parse_btmp_logins() { local hours=$1 if [ ! -f /var/log/btmp ]; then return 0 fi echo " Parsing failed logins (btmp)..." >&2 # Calculate cutoff date local cutoff_date=$(date -d "$hours hours ago" "+%Y-%m-%d %H:%M:%S") # Use lastb command to parse binary btmp log lastb -F 2>/dev/null | awk -v cutoff="$cutoff_date" ' BEGIN { cmd = "date -d \"" cutoff "\" +%s" cmd | getline cutoff_epoch close(cmd) } /^[a-zA-Z0-9]/ && !/^btmp/ { # Skip header if ($1 == "USERNAME" || $1 == "btmp") next user = $1 tty = $2 ip = $3 # Parse timestamp: "Mon Feb 2 19:18:10 2026" month = $4 day = $5 time = $6 year = $7 timestamp = month " " day " " time " " year cmd = "date -d \"" timestamp "\" +%s 2>/dev/null" cmd | getline login_epoch close(cmd) # Only include failed attempts within time window if (login_epoch >= cutoff_epoch) { simple_timestamp = month " " day " " time # Skip if no IP if (ip == "" || ip == "-") next print simple_timestamp "|" user "|" ip "|ssh|password|failed" } } ' >> "$SSH_EVENTS" } # # SUDO/PRIVILEGE ESCALATION DETECTION (Universal - All Panels) # parse_sudo_escalation() { local hours=$1 local secure_log="/var/log/secure" # Use auth.log on Debian-based systems if [ ! -f "$secure_log" ] && [ -f "/var/log/auth.log" ]; then secure_log="/var/log/auth.log" fi if [ ! -f "$secure_log" ]; then return 0 fi echo " Detecting sudo/privilege escalation..." >&2 # Parse sudo commands from secure log awk -v hours="$hours" ' BEGIN { cmd = "date -d \"" hours " hours ago\" +%s" cmd | getline threshold close(cmd) } /sudo:/ { # Parse timestamp timestamp = $1 " " $2 " " $3 cmd = "date -d \"" timestamp "\" +%s 2>/dev/null" cmd | getline epoch close(cmd) if (epoch < threshold) next # Extract user and command # Format: "user : TTY=pts/0 ; PWD=/root ; USER=root ; COMMAND=/bin/bash" user = "" target_user = "" sudo_cmd = "" # Find the username before the colon for (i=1; i<=NF; i++) { if ($i == "sudo:") { user = $(i+1) break } } # Extract TARGET user (USER=root) if (match($0, /USER=([^ ;]+)/)) { target_user_match = substr($0, RSTART+5) split(target_user_match, arr, " ") target_user = arr[1] gsub(/;/, "", target_user) } # Extract COMMAND if (match($0, /COMMAND=(.+)$/)) { sudo_cmd = substr($0, RSTART+8) } # Determine if this is a privilege escalation to root if (target_user == "root" && user != "root") { # Non-root user executing sudo to root print timestamp "|" user "|localhost|sudo|escalation|" target_user "|" sudo_cmd } } ' "$secure_log" > "$TMP_DIR/sudo_events_$$.txt" } # # CPANEL LOG PARSING # parse_cpanel_logins() { local hours=$1 local whm_log="/usr/local/cpanel/logs/access_log" local cpanel_log="/usr/local/cpanel/logs/login_log" echo " Parsing cPanel/WHM logins..." >&2 # WHM access log if [ -f "$whm_log" ]; then awk -v hours="$hours" ' BEGIN { cmd = "date -d \"" hours " hours ago\" +%s" cmd | getline threshold close(cmd) } { # Parse timestamp [01/Feb/2026:19:30:00] if (match($4, /\[([0-9]{2})\/([A-Za-z]{3})\/([0-9]{4}):([0-9:]+)\]/)) { timestamp_str = substr($4, 2) cmd = "date -d \"" timestamp_str "\" +\"%b %d %H:%M:%S\" 2>/dev/null" cmd | getline timestamp close(cmd) cmd = "date -d \"" timestamp_str "\" +%s 2>/dev/null" cmd | getline epoch close(cmd) if (epoch < threshold) next # Extract IP (first field) ip = $1 # Check for WHM login if (match($0, /POST \/login\//) || match($0, /GET \/cpsess/)) { # Extract user from URL if possible user = "root" # WHM is root access status = "success" if ($9 ~ /^(40|50)/) status = "failed" print timestamp "|" user "|" ip "|whm|web|" status } } } ' "$whm_log" >> "$PANEL_EVENTS" fi # cPanel login log if [ -f "$cpanel_log" ]; then awk -v hours="$hours" ' BEGIN { cmd = "date -d \"" hours " hours ago\" +%s" cmd | getline threshold close(cmd) } { # Parse timestamp timestamp = $1 " " $2 " " $3 cmd = "date -d \"" timestamp "\" +%s 2>/dev/null" cmd | getline epoch close(cmd) if (epoch < threshold) next # Extract user and IP if (match($0, /user: ([^ ]+).*ip: ([^ ]+)/)) { for (i=1; i<=NF; i++) { if ($i == "user:") user = $(i+1) if ($i == "ip:") ip = $(i+1) } status = (match($0, /FAILED/) || match($0, /failed/)) ? "failed" : "success" if (user != "" && ip != "") { print timestamp "|" user "|" ip "|cpanel|web|" status } } } ' "$cpanel_log" >> "$PANEL_EVENTS" fi # cPanel session log (WHM Terminal access) local session_log="/usr/local/cpanel/logs/session_log" if [ -f "$session_log" ]; then awk -v hours="$hours" ' BEGIN { cmd = "date -d \"" hours " hours ago\" +%s" cmd | getline threshold close(cmd) } /NEW/ && /whostmgrd/ { # Parse timestamp: [2026-01-05 19:40:56 -0500] if (match($0, /\[([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})/)) { timestamp_str = substr($0, RSTART+1, RLENGTH-1) # Convert to epoch for comparison cmd = "date -d \"" timestamp_str "\" +%s 2>/dev/null" cmd | getline epoch close(cmd) if (epoch < threshold) next # Convert to simple format for output cmd = "date -d \"" timestamp_str "\" \"+%b %d %H:%M:%S\" 2>/dev/null" cmd | getline timestamp close(cmd) # Extract IP from "address=IP" ip = "" if (match($0, /address=([0-9.]+)/)) { ip_match = substr($0, RSTART+8) split(ip_match, arr, ",") ip = arr[1] } # Extract user (usually root for WHM terminal) user = "root" if (match($0, /creator=([^ ,]+)/)) { user_match = substr($0, RSTART+8) split(user_match, arr, ",") user = arr[1] } if (ip != "" && ip != "internal") { print timestamp "|" user "|" ip "|whm-terminal|web|success" } } } ' "$session_log" >> "$PANEL_EVENTS" fi } # # PLESK LOG PARSING # parse_plesk_logins() { local hours=$1 local plesk_log="/var/log/plesk/panel.log" # Try alternative location if [ ! -f "$plesk_log" ]; then plesk_log="/usr/local/psa/var/log/panel.log" fi if [ ! -f "$plesk_log" ]; then return 0 fi echo " Parsing Plesk panel logins..." >&2 awk -v hours="$hours" ' BEGIN { cmd = "date -d \"" hours " hours ago\" +%s" cmd | getline threshold close(cmd) } /login/ { # Parse timestamp (ISO format: 2026-02-02 19:30:00) timestamp = $1 " " $2 cmd = "date -d \"" timestamp "\" +%s 2>/dev/null" cmd | getline epoch close(cmd) if (epoch < threshold) next # Extract user and IP user = "" ip = "" status = "success" if (match($0, /user=([^ ]+)/)) { for (i=1; i<=NF; i++) { if (match($i, /user=/)) { gsub(/user=/, "", $i) user = $i } if (match($i, /ip=/)) { gsub(/ip=/, "", $i) ip = $i } } } if (match($0, /failed/)) status = "failed" if (user != "" && ip != "") { print timestamp "|" user "|" ip "|plesk|web|" status } } ' "$plesk_log" >> "$PANEL_EVENTS" } # # INTERWORX LOG PARSING # parse_interworx_logins() { local hours=$1 local iworx_log="/home/interworx/var/log/iworx.log" local siteworx_log="/home/interworx/var/log/siteworx.log" if [ ! -f "$iworx_log" ]; then return 0 fi echo " Parsing InterWorx logins..." >&2 # Parse NodeWorx (admin) logins awk -v hours="$hours" ' BEGIN { cmd = "date -d \"" hours " hours ago\" +%s" cmd | getline threshold close(cmd) } /login/ { timestamp = $1 " " $2 cmd = "date -d \"" timestamp "\" +%s 2>/dev/null" cmd | getline epoch close(cmd) if (epoch < threshold) next # Extract details user = "" ip = "" status = "success" for (i=1; i<=NF; i++) { if (match($i, /user=/)) { gsub(/user=/, "", $i) user = $i } if (match($i, /ip=/)) { gsub(/ip=/, "", $i) ip = $i } } if (match($0, /failed/)) status = "failed" if (user != "" && ip != "") { print timestamp "|" user "|" ip "|interworx|web|" status } } ' "$iworx_log" >> "$PANEL_EVENTS" # Parse SiteWorx (user/site owner) logins if [ -f "$siteworx_log" ]; then awk -v hours="$hours" ' BEGIN { cmd = "date -d \"" hours " hours ago\" +%s" cmd | getline threshold close(cmd) } /login/ { timestamp = $1 " " $2 cmd = "date -d \"" timestamp "\" +%s 2>/dev/null" cmd | getline epoch close(cmd) if (epoch < threshold) next # Extract details user = "" ip = "" status = "success" for (i=1; i<=NF; i++) { if (match($i, /user=/)) { gsub(/user=/, "", $i) user = $i } if (match($i, /ip=/)) { gsub(/ip=/, "", $i) ip = $i } } if (match($0, /failed/)) status = "failed" if (user != "" && ip != "") { print timestamp "|" user "|" ip "|siteworx|web|" status } } ' "$siteworx_log" >> "$PANEL_EVENTS" fi } # # ANOMALY DETECTION # detect_anomalies() { echo " Analyzing login patterns..." >&2 # Combine all events (including sudo) cat "$SSH_EVENTS" "$PANEL_EVENTS" "$SUDO_EVENTS" 2>/dev/null | \ awk -F'|' -v server_ips="$(get_server_ips | tr '\n' '|')" ' { timestamp = $1 user = $2 ip = $3 service = $4 method = $5 status = $6 # Skip server IPs (but not localhost for sudo events) if (service != "sudo" && index(server_ips, ip "|") > 0) next # Skip cPanel internal IPs if (match(ip, /^(208\.74\.123\.|184\.94\.197\.)/)) next # Track all events by IP (use "local" for sudo events) event_key = (ip == "localhost") ? "LOCAL_SUDO" : ip ip_events[event_key]++ ip_users[event_key] = ip_users[event_key] (ip_users[event_key] ? "," : "") user # Track sudo escalation events (method == "escalation") if (method == "escalation") { sudo_escalations[event_key]++ sudo_users[event_key] = sudo_users[event_key] (sudo_users[event_key] ? "," : "") user # status field contains target_user for sudo sudo_targets[event_key] = sudo_targets[event_key] (sudo_targets[event_key] ? "," : "") status } # Track failed attempts if (status == "failed") { failed[event_key]++ failed_users[event_key] = failed_users[event_key] (failed_users[event_key] ? "," : "") user } # Track successful logins if (status == "success" || status == "active") { successful[event_key]++ successful_users[event_key] = successful_users[event_key] (successful_users[event_key] ? "," : "") user successful_services[event_key] = successful_services[event_key] (successful_services[event_key] ? "," : "") service # Track root access if (user == "root") { root_logins[event_key]++ root_methods[event_key] = root_methods[event_key] (root_methods[event_key] ? "," : "") method } # Track password vs key auth if (method == "password") { password_auth[event_key]++ } else if (method == "key") { key_auth[event_key]++ } } # Store first and last seen if (first_seen[event_key] == "") { first_seen[event_key] = timestamp } last_seen[event_key] = timestamp } END { for (ip in ip_events) { risk = 0 reasons = "" # Root access risk if (root_logins[ip] > 0) { risk += 20 reasons = reasons "Root-Access " # Password auth for root (higher risk) if (index(root_methods[ip], "password") > 0) { risk += 10 reasons = reasons "Root-Password-Auth " } } # Failed login risk if (failed[ip] > 0) { fail_score = failed[ip] * 5 if (fail_score > 25) fail_score = 25 risk += fail_score reasons = reasons "Failed-Attempts(" failed[ip] ") " # Failed root attempts (very suspicious) if (index(failed_users[ip], "root") > 0) { risk += 15 reasons = reasons "Failed-Root " } } # Brute force detection if (failed[ip] >= 5) { risk += 20 reasons = reasons "Brute-Force " } # Multiple users from same IP (potential attack) split(ip_users[ip], user_arr, ",") unique_users = 0 for (u in user_arr) unique_users++ if (unique_users > 3) { risk += 15 reasons = reasons "Multiple-Users(" unique_users ") " } # Password auth risk (should use keys) if (password_auth[ip] > 0 && key_auth[ip] == 0) { risk += 5 reasons = reasons "Password-Only " } # Sudo escalation risk if (sudo_escalations[ip] > 0) { risk += 15 reasons = reasons "Sudo-Escalation(" sudo_escalations[ip] ") " } # Cap at 100 if (risk > 100) risk = 100 # Only report suspicious IPs (risk >= 30) if (risk >= 30) { print ip "|" risk "|" reasons "|" successful[ip] "|" failed[ip] "|" root_logins[ip] "|" ip_users[ip] "|" successful_services[ip] } } } ' | sort -t'|' -k2 -nr > "$SUSPICIOUS_IPS" } # # BOT ANALYZER INTEGRATION # correlate_with_access_logs() { local ip=$1 local risk_score=$2 # Find most recent bot analyzer report local latest_report=$(ls -t "$TOOLKIT_ROOT"/tmp/bot_analysis_report_*.txt /tmp/bot_analysis_report_*.txt 2>/dev/null | head -n1) if [ -z "$latest_report" ]; then echo "0|No bot analyzer data available" return 0 fi # Check if this IP appears in bot analyzer results local ip_data=$(grep -w "$ip" "$latest_report" 2>/dev/null || echo "") if [ -z "$ip_data" ]; then echo "0|No access log activity" return 0 fi # Extract attack vectors from bot analyzer report local attack_vectors="" local additional_risk=0 # Look for attack patterns in the report around this IP local context=$(grep -w -A 5 -B 5 "$ip" "$latest_report" 2>/dev/null) # Check for specific attack types if echo "$context" | grep -qi "RCE/Upload"; then attack_vectors="$attack_vectors RCE/Upload" additional_risk=$((additional_risk + 25)) fi if echo "$context" | grep -qi "SQLi\|SQL injection"; then attack_vectors="$attack_vectors SQLi" additional_risk=$((additional_risk + 20)) fi if echo "$context" | grep -qi "Admin.*prob\|wp-login\|admin login"; then attack_vectors="$attack_vectors Admin-Probe" additional_risk=$((additional_risk + 15)) fi if echo "$context" | grep -qi "Login.*Brute\|brute.*force"; then attack_vectors="$attack_vectors Login-Bruteforce" additional_risk=$((additional_risk + 15)) fi if echo "$context" | grep -qi "Info.*Disclosure\|info disclosure"; then attack_vectors="$attack_vectors Info-Disclosure" additional_risk=$((additional_risk + 10)) fi if echo "$context" | grep -qi "XSS"; then attack_vectors="$attack_vectors XSS" additional_risk=$((additional_risk + 10)) fi # Cap at 100 local new_risk=$((risk_score + additional_risk)) [ $new_risk -gt 100 ] && new_risk=100 echo "$additional_risk|$attack_vectors" } # # IP REPUTATION CHECK # check_ip_reputation() { local ip=$1 local risk_score=$2 # Source IP reputation library if available if [ ! -f "$IP_REPUTATION_LIB" ]; then echo "0|IP reputation library not available" return 0 fi source "$IP_REPUTATION_LIB" local additional_risk=0 local notes="" # Check whitelist if is_whitelisted "$ip" 2>/dev/null; then echo "-30|Whitelisted IP" return 0 fi # Check blacklist if is_blacklisted "$ip" 2>/dev/null; then additional_risk=$((additional_risk + 20)) notes="$notes Previously-Blacklisted" fi # Check reputation score local reputation=$(get_ip_reputation "$ip" 2>/dev/null) if [ -n "$reputation" ] && [ "$reputation" -lt 40 ]; then additional_risk=$((additional_risk + 15)) notes="$notes Poor-Reputation($reputation)" fi echo "$additional_risk|$notes" } # # THREAT INTELLIGENCE CORRELATION # correlate_with_threat_intel() { local ip=$1 # Source threat intelligence library if available if [ ! -f "$THREAT_INTEL_LIB" ]; then echo "0|Threat intelligence not available" return 0 fi source "$THREAT_INTEL_LIB" local additional_risk=0 local notes="" # Check known botnets if is_known_botnet "$ip" 2>/dev/null; then additional_risk=$((additional_risk + 30)) notes="$notes Known-Botnet" fi # GeoIP check (if available) if command -v geoiplookup &>/dev/null; then local country=$(geoiplookup "$ip" 2>/dev/null | awk -F': ' '{print $2}' | head -n1) if [ -n "$country" ]; then notes="$notes Country:$country" # High-risk countries case "$country" in *China*|*Russia*|*North\ Korea*) additional_risk=$((additional_risk + 10)) notes="$notes High-Risk-Geo" ;; esac fi fi echo "$additional_risk|$notes" } # # COMPROMISE DETECTION - Check for actual root compromise indicators # check_backdoor_accounts() { echo " Checking for backdoor user accounts..." >&2 local findings="" local risk=0 # Check for multiple UID 0 accounts (besides root) local uid0_accounts=$(awk -F: '$3 == 0 && $1 != "root" {print $1}' /etc/passwd) if [ -n "$uid0_accounts" ]; then findings="${findings}Unauthorized-UID-0-Accounts:$(echo $uid0_accounts | tr '\n' ',') " risk=$((risk + 50)) fi # Check for accounts with no password local no_pass=$(awk -F: '$2 == "" {print $1}' /etc/shadow 2>/dev/null) if [ -n "$no_pass" ]; then findings="${findings}No-Password-Accounts:$(echo $no_pass | tr '\n' ',') " risk=$((risk + 30)) fi # Check for recently added users (last 7 days) local recent_users=$(awk -F: -v cutoff=$(date -d '7 days ago' +%s) ' $3 >= 1000 { cmd = "stat -c %Y /home/" $1 " 2>/dev/null" cmd | getline created close(cmd) if (created > cutoff) print $1 } ' /etc/passwd) if [ -n "$recent_users" ]; then findings="${findings}Recently-Added-Users:$(echo $recent_users | tr '\n' ',') " risk=$((risk + 20)) fi # Check for suspicious usernames (common backdoor names) local suspicious_users=$(awk -F: ' $1 ~ /^(test|temp|backup|hacker|admin|ftpuser|nobody2|bin2|daemon2)$/ && $3 >= 1000 { print $1 } ' /etc/passwd) if [ -n "$suspicious_users" ]; then findings="${findings}Suspicious-Usernames:$(echo $suspicious_users | tr '\n' ',') " risk=$((risk + 25)) fi echo "$risk|$findings" } check_unauthorized_ssh_keys() { echo " Checking for unauthorized SSH keys..." >&2 local findings="" local risk=0 # Check root's authorized_keys if [ -f /root/.ssh/authorized_keys ]; then local key_count=$(grep -v "^#" /root/.ssh/authorized_keys 2>/dev/null | grep -c "ssh-") if [ "$key_count" -gt 5 ]; then findings="${findings}Excessive-Root-SSH-Keys:$key_count " risk=$((risk + 20)) fi # Check for keys with suspicious comments local suspicious_keys=$(grep -v "^#" /root/.ssh/authorized_keys 2>/dev/null | grep -i "hacker\|backdoor\|pwned\|rooted") if [ -n "$suspicious_keys" ]; then findings="${findings}Suspicious-SSH-Key-Comments " risk=$((risk + 40)) fi # Check file permissions (should be 600) local perms=$(stat -c %a /root/.ssh/authorized_keys 2>/dev/null) if [ "$perms" != "600" ] && [ "$perms" != "400" ]; then findings="${findings}Incorrect-SSH-Key-Permissions:$perms " risk=$((risk + 15)) fi fi # Check for authorized_keys in unusual locations local unusual_keys=$(find /tmp /var/tmp /dev/shm -name "authorized_keys" 2>/dev/null) if [ -n "$unusual_keys" ]; then findings="${findings}SSH-Keys-In-Unusual-Locations " risk=$((risk + 35)) fi echo "$risk|$findings" } check_system_file_tampering() { echo " Checking for system file tampering..." >&2 local findings="" local risk=0 # Check /etc/passwd modification time (recent changes suspicious) local passwd_age=$(($(date +%s) - $(stat -c %Y /etc/passwd 2>/dev/null))) if [ "$passwd_age" -lt 86400 ]; then # Modified in last 24 hours findings="${findings}/etc/passwd-Modified-Recently " risk=$((risk + 25)) fi # Check /etc/shadow modification time local shadow_age=$(($(date +%s) - $(stat -c %Y /etc/shadow 2>/dev/null))) if [ "$shadow_age" -lt 86400 ]; then findings="${findings}/etc/shadow-Modified-Recently " risk=$((risk + 25)) fi # Check for suspicious entries in /etc/passwd (exclude system accounts) # Look for non-standard shells on user accounts (UID >= 1000) local backdoor_shells=$(awk -F: '$3 >= 1000 && $7 != "" { shell = $7 # Standard shells if (shell ~ /\/bash$/ || shell ~ /\/sh$/ || shell ~ /nologin$/ || shell ~ /false$/ || shell ~ /true$/) next # System accounts if ($1 == "sync" || $1 == "shutdown" || $1 == "halt" || $1 == "operator") next # cPanel shells if (shell ~ /\/noshell$/) next # If we get here, shell is suspicious print $1":"shell }' /etc/passwd 2>/dev/null) if [ -n "$backdoor_shells" ]; then findings="${findings}Suspicious-Login-Shells:$backdoor_shells " risk=$((risk + 30)) fi # Check sudoers for unauthorized entries if [ -f /etc/sudoers ]; then local suspicious_sudo=$(grep -v "^#" /etc/sudoers 2>/dev/null | grep "NOPASSWD" | grep -v "root") if [ -n "$suspicious_sudo" ]; then findings="${findings}Suspicious-Sudoers-Entries " risk=$((risk + 35)) fi fi echo "$risk|$findings" } check_suspicious_processes() { echo " Checking for suspicious processes..." >&2 local findings="" local risk=0 # Check for processes with suspicious names local suspicious_procs=$(ps aux | grep -E "nc -l|ncat -l|/dev/tcp|bash -i|perl.*socket|python.*socket" | grep -v grep) if [ -n "$suspicious_procs" ]; then findings="${findings}Reverse-Shell-Processes " risk=$((risk + 50)) fi # Check for hidden processes (spaces in name) local hidden_procs=$(ps aux | awk '$11 ~ /^[ ]+$/ {print $2}') if [ -n "$hidden_procs" ]; then findings="${findings}Hidden-Processes " risk=$((risk + 40)) fi # Check for processes running from /tmp or /dev/shm local tmp_procs=$(lsof -p $(ps aux | awk '$11 ~ /\/(tmp|dev\/shm)/ {print $2}' | tr '\n' ',') 2>/dev/null | grep -c "^COMMAND") if [ "$tmp_procs" -gt 0 ]; then findings="${findings}Processes-From-Tmp:$tmp_procs " risk=$((risk + 30)) fi # Check for unusual network connections local suspicious_conns=$(netstat -antp 2>/dev/null | grep ESTABLISHED | awk '{print $5}' | cut -d: -f1 | sort -u | wc -l) if [ "$suspicious_conns" -gt 50 ]; then findings="${findings}Excessive-Network-Connections:$suspicious_conns " risk=$((risk + 20)) fi echo "$risk|$findings" } check_backdoor_cron_jobs() { echo " Checking for backdoor cron jobs..." >&2 local findings="" local risk=0 # Check root crontab local suspicious_cron=$(crontab -l 2>/dev/null | grep -v "^#" | grep -E "wget|curl|nc |bash -i|/tmp/|/dev/shm|base64") if [ -n "$suspicious_cron" ]; then findings="${findings}Suspicious-Root-Cron-Jobs " risk=$((risk + 45)) fi # Check /etc/cron.d for suspicious entries (exclude false positives) local suspicious_cron_d=$(grep -rE "wget.*\||curl.*\||bash -i|/dev/tcp" /etc/cron.d/ 2>/dev/null | grep -v "^#" | grep -v "cpanel\|license_sync") if [ -n "$suspicious_cron_d" ]; then findings="${findings}Suspicious-Cron.d-Entries " risk=$((risk + 45)) fi # Check for cron jobs in unusual locations local unusual_crons=$(find /tmp /var/tmp /dev/shm -name "cron*" -o -name "*.cron" 2>/dev/null) if [ -n "$unusual_crons" ]; then findings="${findings}Cron-Jobs-In-Unusual-Locations " risk=$((risk + 40)) fi echo "$risk|$findings" } check_bash_history_malicious_commands() { echo " Analyzing bash history for malicious commands..." >&2 local findings="" local risk=0 if [ -f /root/.bash_history ]; then # Check for common attack commands (exclude legitimate installers) local malicious_cmds=$(grep -E "wget.*\/tmp\/.*sh|curl.*(base64|eval)|nc -l|bash -i.*\/dev\/tcp|chmod \+s \/|chattr \+i" /root/.bash_history 2>/dev/null | grep -v "claude.ai\|github.com\|githubusercontent") if [ -n "$malicious_cmds" ]; then findings="${findings}Malicious-Commands-In-History " risk=$((risk + 40)) fi # Check for history clearing attempts local history_tampering=$(grep -E "history -c|rm.*bash_history|unset HISTFILE" /root/.bash_history 2>/dev/null) if [ -n "$history_tampering" ]; then findings="${findings}History-Tampering-Detected " risk=$((risk + 35)) fi # Check for password hash modifications local passwd_mods=$(grep -E "echo.*\/etc\/passwd|echo.*\/etc\/shadow|vipw|usermod.*-p" /root/.bash_history 2>/dev/null) if [ -n "$passwd_mods" ]; then findings="${findings}Password-File-Manipulation " risk=$((risk + 45)) fi fi # Check if history is disabled if ! grep -q "^HISTFILE=" /root/.bashrc 2>/dev/null && [ ! -f /root/.bash_history ]; then findings="${findings}Bash-History-Disabled " risk=$((risk + 25)) fi echo "$risk|$findings" } check_web_shells() { echo " Scanning for web shells..." >&2 local findings="" local risk=0 local web_roots="" # Determine web roots based on panel if [ -d /home ]; then web_roots="/home/*/public_html /var/www/html /usr/local/apache/htdocs" fi # Scan for common web shell patterns (limit to recent files for performance) local suspicious_files=$(find $web_roots -type f -name "*.php" -mtime -7 2>/dev/null | head -50 | xargs grep -l "eval(\|base64_decode(\|system(\|exec(\|passthru(\|shell_exec(" 2>/dev/null | head -10) if [ -n "$suspicious_files" ]; then local file_count=$(echo "$suspicious_files" | wc -l) findings="${findings}Potential-Web-Shells:$file_count " risk=$((risk + 35)) fi # Check for suspicious PHP files in unusual locations local unusual_php=$(find /tmp /var/tmp /dev/shm -name "*.php" 2>/dev/null) if [ -n "$unusual_php" ]; then findings="${findings}PHP-Files-In-Tmp " risk=$((risk + 40)) fi echo "$risk|$findings" } check_rootkit_indicators() { echo " Checking for rootkit indicators..." >&2 local findings="" local risk=0 # Check for common rootkit files local rootkit_files="/dev/.hid /usr/bin/.sniffer /usr/local/hide /lib/libproc.a" for file in $rootkit_files; do if [ -e "$file" ]; then findings="${findings}Rootkit-File:$file " risk=$((risk + 50)) fi done # Check for suspicious kernel modules local suspicious_modules=$(lsmod | grep -E "^(diamond|phalanx|beastkit)") if [ -n "$suspicious_modules" ]; then findings="${findings}Suspicious-Kernel-Modules " risk=$((risk + 50)) fi # Check for modified binaries (ls, ps, netstat) - look for backdoor/rootkit strings only for cmd in /bin/ls /bin/ps /bin/netstat; do if [ -f "$cmd" ]; then local strings_check=$(strings "$cmd" 2>/dev/null | grep -iE "backdoor|rootkit|\bhidden\b.*process|\bhide\b.*file") if [ -n "$strings_check" ]; then findings="${findings}Modified-Binary:$cmd " risk=$((risk + 50)) fi fi done # Check for hidden directories (... or . or . ) local hidden_dirs=$(find / -maxdepth 3 -type d -name "..." -o -name ". " -o -name ". " 2>/dev/null) if [ -n "$hidden_dirs" ]; then findings="${findings}Hidden-Directories " risk=$((risk + 35)) fi echo "$risk|$findings" } check_suspicious_network_activity() { echo " Analyzing network connections..." >&2 local findings="" local risk=0 # Check for connections to unusual ports (reverse shells often use 4444, 5555, 6666, 1337) local suspicious_ports=$(netstat -antp 2>/dev/null | grep ESTABLISHED | awk '{print $4}' | cut -d: -f2 | grep -E "^(4444|5555|6666|1337|31337)$") if [ -n "$suspicious_ports" ]; then findings="${findings}Suspicious-Port-Connections " risk=$((risk + 45)) fi # Check for IRC connections (common in botnets) local irc_conns=$(netstat -antp 2>/dev/null | grep ESTABLISHED | grep ":6667\|:6666\|:7000") if [ -n "$irc_conns" ]; then findings="${findings}IRC-Connections-Detected " risk=$((risk + 30)) fi # Check for excessive outbound connections from web server if command -v pgrep &>/dev/null; then local httpd_conns=$(lsof -p $(pgrep -d, httpd 2>/dev/null) 2>/dev/null | grep -c ESTABLISHED) if [ "$httpd_conns" -gt 100 ]; then findings="${findings}Excessive-Web-Server-Connections:$httpd_conns " risk=$((risk + 25)) fi fi echo "$risk|$findings" } perform_compromise_detection() { local ip=$1 echo " ${YELLOW}Running comprehensive compromise detection...${NC}" >&2 echo "" >&2 local total_risk=0 local all_findings="" # Run all compromise checks local result=$(check_backdoor_accounts) local check_risk=$(echo "$result" | cut -d'|' -f1) local check_findings=$(echo "$result" | cut -d'|' -f2-) total_risk=$((total_risk + check_risk)) [ -n "$check_findings" ] && all_findings="${all_findings}${check_findings}" result=$(check_unauthorized_ssh_keys) check_risk=$(echo "$result" | cut -d'|' -f1) check_findings=$(echo "$result" | cut -d'|' -f2-) total_risk=$((total_risk + check_risk)) [ -n "$check_findings" ] && all_findings="${all_findings}${check_findings}" result=$(check_system_file_tampering) check_risk=$(echo "$result" | cut -d'|' -f1) check_findings=$(echo "$result" | cut -d'|' -f2-) total_risk=$((total_risk + check_risk)) [ -n "$check_findings" ] && all_findings="${all_findings}${check_findings}" result=$(check_suspicious_processes) check_risk=$(echo "$result" | cut -d'|' -f1) check_findings=$(echo "$result" | cut -d'|' -f2-) total_risk=$((total_risk + check_risk)) [ -n "$check_findings" ] && all_findings="${all_findings}${check_findings}" result=$(check_backdoor_cron_jobs) check_risk=$(echo "$result" | cut -d'|' -f1) check_findings=$(echo "$result" | cut -d'|' -f2-) total_risk=$((total_risk + check_risk)) [ -n "$check_findings" ] && all_findings="${all_findings}${check_findings}" result=$(check_bash_history_malicious_commands) check_risk=$(echo "$result" | cut -d'|' -f1) check_findings=$(echo "$result" | cut -d'|' -f2-) total_risk=$((total_risk + check_risk)) [ -n "$check_findings" ] && all_findings="${all_findings}${check_findings}" result=$(check_web_shells) check_risk=$(echo "$result" | cut -d'|' -f1) check_findings=$(echo "$result" | cut -d'|' -f2-) total_risk=$((total_risk + check_risk)) [ -n "$check_findings" ] && all_findings="${all_findings}${check_findings}" result=$(check_rootkit_indicators) check_risk=$(echo "$result" | cut -d'|' -f1) check_findings=$(echo "$result" | cut -d'|' -f2-) total_risk=$((total_risk + check_risk)) [ -n "$check_findings" ] && all_findings="${all_findings}${check_findings}" result=$(check_suspicious_network_activity) check_risk=$(echo "$result" | cut -d'|' -f1) check_findings=$(echo "$result" | cut -d'|' -f2-) total_risk=$((total_risk + check_risk)) [ -n "$check_findings" ] && all_findings="${all_findings}${check_findings}" # Cap at 100 [ $total_risk -gt 100 ] && total_risk=100 echo "$total_risk|$all_findings" } # # AUTOMATED RESPONSE # trigger_automated_response() { local ip=$1 local risk_score=$2 local username=$3 local panel=$4 # CRITICAL: 85-100 if [ $risk_score -ge $RISK_CRITICAL ] && [ "$SUSPICIOUS_LOGIN_AUTO_BLOCK" = "yes" ]; then echo -e "\n${RED}🚨 CRITICAL RISK: Triggering automated response${NC}" # 1. Block IP if command -v csf &>/dev/null; then echo " [1/3] Blocking IP via CSF..." if csf -d "$ip" "Suspicious login (risk: $risk_score)" 2>/dev/null; then echo -e " ${GREEN}✓ IP blocked via CSF${NC}" else echo -e " ${RED}✗ CSF block failed${NC}" fi else echo " [1/3] Blocking IP via iptables..." if iptables -I INPUT -s "$ip" -j DROP 2>/dev/null; then echo -e " ${GREEN}✓ IP blocked via iptables${NC}" else echo -e " ${RED}✗ iptables block failed${NC}" fi fi # 2. Trigger rkhunter scan if [ "$SUSPICIOUS_LOGIN_AUTO_SCAN" = "yes" ] && [ -f "$MALWARE_SCANNER" ]; then echo " [2/3] Triggering rootkit scan..." # Run in background with timeout timeout 300 bash "$MALWARE_SCANNER" --scanner rkhunter --quick &>/dev/null & local scan_pid=$! echo -e " ${GREEN}✓ Rootkit scan initiated (PID: $scan_pid)${NC}" else echo " [2/3] Rootkit scan (skipped - not configured)" fi # 3. CSI recommendation (cPanel only) if [ "$panel" = "cpanel" ]; then echo " [3/3] CSI scan recommended" echo "" echo " Run comprehensive security scan:" echo " wget https://raw.githubusercontent.com/CpanelInc/tech-CSI/master/csi.pl" echo " perl csi.pl --full" fi # HIGH: 70-84 elif [ $risk_score -ge $RISK_HIGH ]; then echo -e "\n${YELLOW}âš ī¸ HIGH RISK: Manual review recommended${NC}" if [ "$SUSPICIOUS_LOGIN_AUTO_BLOCK" = "yes" ] && command -v csf &>/dev/null; then echo " [1/2] Adding temporary rate limit..." if csf -tr "$ip" 300 "Rate limit: suspicious login" 2>/dev/null; then echo -e " ${GREEN}✓ Rate limited for 5 minutes${NC}" fi fi echo " [2/2] Schedule security scan for review" # MEDIUM: 50-69 elif [ $risk_score -ge $RISK_MEDIUM ]; then echo -e "\n${BLUE}â„šī¸ MEDIUM RISK: Monitoring recommended${NC}" # LOW: <50 else echo -e "\n${GREEN}✓ LOW RISK: Logged for analysis${NC}" fi } # # REPORTING # generate_report() { local panel=$1 echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" echo -e "${CYAN} SUSPICIOUS LOGIN MONITOR - Integrated Security Report${NC}" echo -e "${CYAN} Generated: $(date '+%Y-%m-%d %H:%M:%S')${NC}" echo -e "${CYAN} Scanning: Last $HOURS hours${NC}" echo -e "${CYAN} Panel: $panel${NC}" echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" echo "" # Count total events local total_events=$(cat "$SSH_EVENTS" "$PANEL_EVENTS" 2>/dev/null | wc -l) local successful=$(cat "$SSH_EVENTS" "$PANEL_EVENTS" 2>/dev/null | grep -c "|success$") local failed=$(cat "$SSH_EVENTS" "$PANEL_EVENTS" 2>/dev/null | grep -c "|failed$") local root_count=$(cat "$SSH_EVENTS" "$PANEL_EVENTS" 2>/dev/null | grep -c "|root|") if [ ! -s "$SUSPICIOUS_IPS" ]; then echo -e "${GREEN}✓ No suspicious login activity detected${NC}" echo "" echo "SUMMARY:" echo " Total Login Events: $total_events" echo " Successful: $successful" echo " Failed: $failed" echo " Root Logins: $root_count" echo "" return 0 fi local critical_count=$(awk -F'|' -v thresh=$RISK_CRITICAL '$2 >= thresh' "$SUSPICIOUS_IPS" | wc -l) local high_count=$(awk -F'|' -v crit=$RISK_CRITICAL -v high=$RISK_HIGH '$2 >= high && $2 < crit' "$SUSPICIOUS_IPS" | wc -l) if [ $critical_count -gt 0 ]; then echo -e "${RED}🚨 CRITICAL ALERTS ($critical_count):${NC}" echo "" awk -F'|' -v thresh=$RISK_CRITICAL '$2 >= thresh' "$SUSPICIOUS_IPS" | head -n 5 | while IFS='|' read -r ip risk reasons successful failed root users services; do echo -e " ${RED}[CRITICAL] $ip - Risk: $risk/100${NC}" echo " ┌─────────────────────────────────────────────────────────" echo " │ LOGIN EVENT" echo " ├─────────────────────────────────────────────────────────" echo " │ IP: $ip" echo " │ Successful logins: ${successful:-0}" echo " │ Failed attempts: ${failed:-0}" echo " │ Root logins: ${root:-0}" echo " │ Users: $users" echo " │ Services: $services" echo " │ Initial Risk Factors: $reasons" echo " │ Initial Risk: $risk/100" echo " │" # Cross-reference with bot analyzer echo " ├─────────────────────────────────────────────────────────" echo " │ ACCESS LOG CORRELATION (bot-analyzer.sh)" echo " ├─────────────────────────────────────────────────────────" local correlation=$(correlate_with_access_logs "$ip" "$risk") local corr_risk=$(echo "$correlation" | cut -d'|' -f1) local corr_attacks=$(echo "$correlation" | cut -d'|' -f2-) if [ "$corr_risk" != "0" ] && [ -n "$corr_attacks" ]; then echo " │ âš ī¸ Web attack activity detected:" for attack in $corr_attacks; do echo " │ - $attack" done risk=$((risk + corr_risk)) [ $risk -gt 100 ] && risk=100 else echo " │ $corr_attacks" fi echo " │" # Check IP reputation echo " ├─────────────────────────────────────────────────────────" echo " │ IP REPUTATION" echo " ├─────────────────────────────────────────────────────────" local rep_result=$(check_ip_reputation "$ip" "$risk") local rep_risk=$(echo "$rep_result" | cut -d'|' -f1) local rep_notes=$(echo "$rep_result" | cut -d'|' -f2-) if [ "$rep_risk" != "0" ]; then echo " │ $rep_notes" risk=$((risk + rep_risk)) [ $risk -gt 100 ] && risk=100 else echo " │ $rep_notes" fi echo " │" # Threat intelligence echo " ├─────────────────────────────────────────────────────────" echo " │ THREAT INTELLIGENCE" echo " ├─────────────────────────────────────────────────────────" local threat_result=$(correlate_with_threat_intel "$ip") local threat_risk=$(echo "$threat_result" | cut -d'|' -f1) local threat_notes=$(echo "$threat_result" | cut -d'|' -f2-) if [ "$threat_risk" != "0" ]; then echo " │ âš ī¸ $threat_notes" risk=$((risk + threat_risk)) [ $risk -gt 100 ] && risk=100 else echo " │ $threat_notes" fi echo " │" # COMPROMISE DETECTION - Check if server is actually rooted echo " ├─────────────────────────────────────────────────────────" echo -e " │ ${RED}COMPROMISE DETECTION - System Integrity Check${NC}" echo " ├─────────────────────────────────────────────────────────" local compromise_result=$(perform_compromise_detection "$ip") local compromise_risk=$(echo "$compromise_result" | cut -d'|' -f1) local compromise_findings=$(echo "$compromise_result" | cut -d'|' -f2-) if [ "$compromise_risk" -ge 50 ]; then echo -e " │ ${RED}🚨 COMPROMISE CONFIRMED - $compromise_risk risk points${NC}" echo " │" echo " │ Indicators of compromise found:" # Parse and display findings for finding in $(echo "$compromise_findings" | tr ' ' '\n'); do echo " │ â€ĸ $finding" done risk=$((risk + compromise_risk)) [ $risk -gt 100 ] && risk=100 elif [ "$compromise_risk" -gt 0 ]; then echo -e " │ ${YELLOW}âš ī¸ Suspicious indicators found - $compromise_risk risk points${NC}" echo " │" for finding in $(echo "$compromise_findings" | tr ' ' '\n'); do echo " │ â€ĸ $finding" done risk=$((risk + compromise_risk)) [ $risk -gt 100 ] && risk=100 else echo -e " │ ${GREEN}✓ No compromise indicators detected${NC}" echo " │ System integrity checks passed" fi echo " │" echo " ├─────────────────────────────────────────────────────────" if [ "$compromise_risk" -ge 50 ]; then echo -e " │ ${RED}FINAL RISK SCORE: $risk/100 - SERVER LIKELY COMPROMISED${NC}" else echo -e " │ ${RED}FINAL RISK SCORE: $risk/100 - CRITICAL${NC}" fi echo " └─────────────────────────────────────────────────────────" # Trigger automated response trigger_automated_response "$ip" "$risk" "$(echo "$users" | cut -d',' -f1)" "$panel" echo "" done fi if [ $high_count -gt 0 ]; then echo -e "${YELLOW}âš ī¸ HIGH ALERTS ($high_count):${NC}" echo "" awk -F'|' -v crit=$RISK_CRITICAL -v high=$RISK_HIGH '$2 >= high && $2 < crit' "$SUSPICIOUS_IPS" | head -n 5 | while IFS='|' read -r ip risk reasons successful failed root users services; do echo -e " ${YELLOW}[HIGH] $ip - Risk: $risk/100${NC}" echo " Successful: ${successful:-0} | Failed: ${failed:-0} | Root: ${root:-0}" echo " Reasons: $reasons" # Quick correlation local correlation=$(correlate_with_access_logs "$ip" "$risk") local corr_attacks=$(echo "$correlation" | cut -d'|' -f2-) if [ -n "$corr_attacks" ] && [ "$corr_attacks" != "No access log activity" ]; then echo " Web attacks: $corr_attacks" fi echo "" done fi echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" echo "SUMMARY:" echo "" echo " Total Login Events: $total_events" echo " Successful: $successful" echo " Failed: $failed" echo " Root Logins: $root_count" echo "" echo " Suspicious IPs: $(wc -l < "$SUSPICIOUS_IPS")" echo " Critical Risk: $critical_count" echo " High Risk: $high_count" echo "" # Integration status echo " Integration Status:" local bot_report=$(ls -t "$TOOLKIT_ROOT"/tmp/bot_analysis_report_*.txt /tmp/bot_analysis_report_*.txt 2>/dev/null | head -n1) [ -n "$bot_report" ] && echo " ✓ Bot Analyzer: Available" || echo " ✗ Bot Analyzer: No recent data" [ -f "$IP_REPUTATION_LIB" ] && echo " ✓ IP Reputation: Available" || echo " ✗ IP Reputation: Not available" [ -f "$THREAT_INTEL_LIB" ] && echo " ✓ Threat Intelligence: Available" || echo " ✗ Threat Intelligence: Not available" echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" } # # MAIN # main() { echo -e "${CYAN}Starting Suspicious Login Monitor...${NC}" echo "" # Detect panel local panel=$(detect_panel) echo "Detected panel: $panel" echo "" # Parse logs (universal parsers first) parse_ssh_logins "$HOURS" parse_wtmp_logins "$HOURS" parse_btmp_logins "$HOURS" parse_sudo_escalation "$HOURS" # Parse panel-specific logs case "$panel" in cpanel) parse_cpanel_logins "$HOURS" ;; plesk) parse_plesk_logins "$HOURS" ;; interworx) parse_interworx_logins "$HOURS" ;; esac # Analyze detect_anomalies # Generate report generate_report "$panel" | tee "$REPORT_FILE" echo "" echo "Report saved to: $REPORT_FILE" # ALWAYS run system-wide compromise detection (regardless of login activity) echo "" echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" echo -e "${CYAN} SYSTEM COMPROMISE DETECTION - Integrity Check${NC}" echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" echo "" local compromise_result=$(perform_compromise_detection "system-wide") local compromise_risk=$(echo "$compromise_result" | cut -d'|' -f1) local compromise_findings=$(echo "$compromise_result" | cut -d'|' -f2-) if [ "$compromise_risk" -ge 100 ]; then echo -e "${RED}🚨 CRITICAL: Server shows strong indicators of compromise${NC}" echo -e "${RED} Risk Score: $compromise_risk/100${NC}" echo "" echo "Indicators found:" for finding in $(echo "$compromise_findings" | tr ' ' '\n'); do echo -e " ${RED}â€ĸ${NC} $(echo $finding | tr '-' ' ')" done echo "" echo -e "${RED}RECOMMENDED ACTIONS:${NC}" echo " 1. Investigate all findings immediately" echo " 2. Run full rootkit scan: rkhunter --check" if [ "$panel" = "cpanel" ]; then echo " 3. Run cPanel CSI: perl csi.pl --full" fi echo " 4. Consider full system reinstall if compromised" elif [ "$compromise_risk" -ge 50 ]; then echo -e "${RED}âš ī¸ WARNING: Suspicious indicators detected${NC}" echo -e "${YELLOW} Risk Score: $compromise_risk/100${NC}" echo "" echo "Indicators found:" for finding in $(echo "$compromise_findings" | tr ' ' '\n'); do echo -e " ${YELLOW}â€ĸ${NC} $(echo $finding | tr '-' ' ')" done echo "" echo -e "${YELLOW}RECOMMENDED ACTIONS:${NC}" echo " 1. Review all findings carefully" echo " 2. Run rootkit scan: rkhunter --check" echo " 3. Investigate recent account/file changes" elif [ "$compromise_risk" -gt 0 ]; then echo -e "${BLUE}â„šī¸ NOTICE: Minor security concerns detected${NC}" echo -e "${BLUE} Risk Score: $compromise_risk/100${NC}" echo "" echo "Issues found:" for finding in $(echo "$compromise_findings" | tr ' ' '\n'); do echo -e " ${BLUE}â€ĸ${NC} $(echo $finding | tr '-' ' ')" done echo "" echo "Review these items when convenient." else echo -e "${GREEN}✓ No compromise indicators detected${NC}" echo "" echo "System integrity checks:" echo " ✓ No unauthorized UID 0 accounts" echo " ✓ No suspicious SSH keys" echo " ✓ No system file tampering detected" echo " ✓ No suspicious processes found" echo " ✓ No backdoor cron jobs" echo " ✓ No malicious commands in bash history" echo " ✓ No web shells detected" echo " ✓ No rootkit indicators" echo " ✓ No suspicious network activity" fi echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" } # Run main function main exit 0