diff --git a/modules/security/suspicious-login-monitor.sh b/modules/security/suspicious-login-monitor.sh new file mode 100755 index 0000000..252e5fd --- /dev/null +++ b/modules/security/suspicious-login-monitor.sh @@ -0,0 +1,919 @@ +#!/bin/bash + +# +# Suspicious Login Monitor - Integrated Security Analysis +# Detects suspicious login patterns and correlates with web attack activity +# 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" +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" "$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" +} + +# +# 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 +} + +# +# 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" +} + +# +# ANOMALY DETECTION +# + +detect_anomalies() { + echo " Analyzing login patterns..." >&2 + + # Combine all events + cat "$SSH_EVENTS" "$PANEL_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 + if (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 + ip_events[ip]++ + ip_users[ip] = ip_users[ip] (ip_users[ip] ? "," : "") user + + # Track failed attempts + if (status == "failed") { + failed[ip]++ + failed_users[ip] = failed_users[ip] (failed_users[ip] ? "," : "") user + } + + # Track successful logins + if (status == "success") { + successful[ip]++ + successful_users[ip] = successful_users[ip] (successful_users[ip] ? "," : "") user + successful_services[ip] = successful_services[ip] (successful_services[ip] ? "," : "") service + + # Track root access + if (user == "root") { + root_logins[ip]++ + root_methods[ip] = root_methods[ip] (root_methods[ip] ? "," : "") method + } + + # Track password vs key auth + if (method == "password") { + password_auth[ip]++ + } else if (method == "key") { + key_auth[ip]++ + } + } + + # Store first and last seen + if (first_seen[ip] == "") { + first_seen[ip] = timestamp + } + last_seen[ip] = 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 " + } + + # 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 -F "$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 -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" +} + +# +# 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_count:-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 " │" + echo " ├─────────────────────────────────────────────────────────" + echo -e " │ ${RED}FINAL RISK SCORE: $risk/100 - CRITICAL${NC}" + 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 + parse_ssh_logins "$HOURS" + + 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" +} + +# Run main function +main + +exit 0