diff --git a/modules/security/live-attack-monitor-v2.sh b/modules/security/live-attack-monitor-v2.sh index 676acb0..ac691a2 100755 --- a/modules/security/live-attack-monitor-v2.sh +++ b/modules/security/live-attack-monitor-v2.sh @@ -3263,34 +3263,66 @@ detect_distributed_attacks() { # Check for same attack type from 5+ different IPs (use awk for performance) for attack_type in RCE SQL_INJECTION XSS PATH_TRAVERSAL BRUTEFORCE; do - # Single AWK pass to count attacks and unique IPs - local result=$(echo "$recent" | awk -v pattern="$attack_type" ' + # Single AWK pass to extract all attacking IPs + local attacking_ips=$(echo "$recent" | awk -v pattern="$attack_type" ' $0 ~ pattern { - count++ # Extract IP (first field matching IP pattern) for(i=1; i<=NF; i++) { if($i ~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) { - ips[$i]=1 + print $i break } } } - END { - print count "|" length(ips) - } - ') - IFS='|' read -r attack_count unique_ips <<< "$result" + ' | sort -u) - if [ "${attack_count:-0}" -ge 5 ]; then + # Count unique IPs + local unique_ips=$(echo "$attacking_ips" | grep -c "^[0-9]" 2>/dev/null || echo "0") - if [ "$unique_ips" -ge 5 ]; then - # Distributed attack detected! - local time_str=$(date +"%H:%M:%S") - echo -e "${CRITICAL_COLOR}[${time_str}] DISTRIBUTED_ATTACK | ${attack_type} from ${unique_ips} IPs in last 2min | Possible botnet${NC}" >> "$TEMP_DIR/recent_events" + if [ "${unique_ips:-0}" -ge 5 ]; then + # Distributed attack detected! + local time_str=$(date +"%H:%M:%S") - # Mark in a file for Quick Actions to see - echo "${attack_type}|${unique_ips}|$(date +%s)" >> "$TEMP_DIR/distributed_attacks" + # BLOCK ALL INDIVIDUAL IPs IN THE ATTACK + local -a batch_ips=() + while IFS= read -r ip; do + [ -n "$ip" ] && batch_ips+=("$ip") + done <<< "$attacking_ips" + + if [ ${#batch_ips[@]} -gt 0 ]; then + batch_block_ips "${batch_ips[@]}" + echo -e "${CRITICAL_COLOR}[${time_str}] DISTRIBUTED_ATTACK | ${attack_type} from ${unique_ips} IPs | BLOCKED ALL${NC}" >> "$TEMP_DIR/recent_events" fi + + # Check for subnet-level coordination (25+ IPs from same /24) + declare -A subnet_counts + while IFS= read -r ip; do + [ -z "$ip" ] && continue + local subnet="${ip%.*}" # Get /24 subnet (bash built-in) + ((subnet_counts[$subnet]++)) + done <<< "$attacking_ips" + + # Block entire subnets with 25+ attacking IPs + for subnet in "${!subnet_counts[@]}"; do + local subnet_ip_count=${subnet_counts[$subnet]} + if [ "$subnet_ip_count" -ge 25 ]; then + local subnet_cidr="${subnet}.0/24" + + # Check if not already blocked + if ! grep -q "^${subnet_cidr}\$" "$TEMP_DIR/blocked_subnets" 2>/dev/null; then + echo "$subnet_cidr" >> "$TEMP_DIR/blocked_subnets" + + # Add to IPset (kernel-level blocking) + if [ "$IPSET_AVAILABLE" -eq 1 ]; then + ipset add "$IPSET_NAME" "$subnet_cidr" -exist 2>/dev/null + echo -e "${CRITICAL_COLOR}[${time_str}] SUBNET_BLOCK | $subnet_cidr | ${attack_type} from ${subnet_ip_count} IPs | BLOCKED${NC}" >> "$TEMP_DIR/recent_events" + fi + fi + fi + done + + # Mark in a file for Quick Actions to see + echo "${attack_type}|${unique_ips}|$(date +%s)" >> "$TEMP_DIR/distributed_attacks" fi done fi