diff --git a/modules/security/live-attack-monitor-v2.sh b/modules/security/live-attack-monitor-v2.sh index 7f8eb03..1ecdcab 100755 --- a/modules/security/live-attack-monitor-v2.sh +++ b/modules/security/live-attack-monitor-v2.sh @@ -2672,7 +2672,11 @@ monitor_network_attacks() { fi # Check if whitelisted service - if [ "$skip_scoring" -eq 0 ] && [ "${hits:-0}" -eq 1 ]; then + # CRITICAL FIX: Changed hits check from -eq 1 to -eq 0 + # Bug: hits=0 means NEW IP (first detection), hits=1 means repeat detection + # Whitelist should only be checked on FIRST detection (hits=0), not repeat + # Previous: only checked on 2nd+ detection, causing false alerts on initial detection + if [ "$skip_scoring" -eq 0 ] && [ "${hits:-0}" -eq 0 ]; then # Only check whitelist on first detection, and only if not already skipped if is_whitelisted_service "$ip" 2>/dev/null; then skip_scoring=1 # Skip scoring but STILL write/track @@ -2680,67 +2684,58 @@ monitor_network_attacks() { fi # Enhanced threat intelligence on first detection - if [ "$skip_scoring" -eq 0 ] && [ "${hits:-0}" -eq 1 ]; then + # CRITICAL FIX: Changed hits check from -eq 1 to -eq 0 + # Only query threat intelligence on FIRST detection to avoid redundant API calls + # CRITICAL FIX #2: Moved reputation bonus calculation OUT of background subshell + # Bug: Bonuses were calculated in background and written to $ip_file, but never added to final score + # Fix: Calculate bonuses synchronously and add directly to $score variable + local threat_intel_bonus=0 + if [ "$skip_scoring" -eq 0 ] && [ "${hits:-0}" -eq 0 ]; then - # Get threat intelligence in background to avoid slowdown + local threat_intel=$(get_threat_intelligence "$ip" 2>/dev/null) + IFS='|' read -r abuse_conf abuse_rpts country isp geo timing whitelisted <<< "$threat_intel" + + # Store enrichment for later use + echo "$threat_intel" > "$TEMP_DIR/threat_enrich_${ip//\./_}" + + # Geographic clustering detection (still in background to avoid blocking) ( - local threat_intel=$(get_threat_intelligence "$ip" 2>/dev/null) - IFS='|' read -r abuse_conf abuse_rpts country isp geo timing whitelisted <<< "$threat_intel" - - # Store enrichment for later use - echo "$threat_intel" > "$TEMP_DIR/threat_enrich_${ip//\./_}" - - # Geographic clustering detection + # Check country/ASN clustering if [ -n "$geo" ] && [ "$geo" != "XX" ]; then echo "$geo" >> "$TEMP_DIR/attack_countries" - # Check if this country has 5+ attacking IPs local country_count=$(grep -c "^${geo}$" "$TEMP_DIR/attack_countries" 2>/dev/null || echo "0") if [ "$country_count" -ge 5 ]; then - # Coordinated attack from same country - boost all IPs from there echo "$geo" >> "$TEMP_DIR/hostile_countries" fi fi # ASN clustering detection if [ -n "$isp" ]; then - # Extract ASN number from ISP string local asn=$(echo "$isp" | grep -oP 'AS\K\d+' 2>/dev/null | head -1 2>/dev/null || echo "") if [ -n "$asn" ]; then echo "$asn" >> "$TEMP_DIR/attack_asns" local asn_count=$(grep -c "^${asn}$" "$TEMP_DIR/attack_asns" 2>/dev/null || echo "0") if [ "$asn_count" -ge 3 ]; then - # Same ASN/hosting provider used by 3+ attackers echo "$asn" >> "$TEMP_DIR/hostile_asns" fi fi fi - - # Apply reputation boosts based on AbuseIPDB - if [ "${abuse_conf:-0}" -ge 75 ]; then - # High confidence malicious - add 30 points - local curr_data=$(cat "$ip_file" 2>/dev/null || echo "0|0|human||0|0") - IFS='|' read -r old_score old_hits old_bot old_attacks old_ban old_rep <<< "$curr_data" - local new_score=$((old_score + 30)) - [ "$new_score" -gt 100 ] && new_score=100 - echo "$new_score|$old_hits|$old_bot|$old_attacks|$old_ban|$old_rep" > "$ip_file" - elif [ "${abuse_conf:-0}" -ge 50 ]; then - # Medium confidence - add 15 points - local curr_data=$(cat "$ip_file" 2>/dev/null || echo "0|0|human||0|0") - IFS='|' read -r old_score old_hits old_bot old_attacks old_ban old_rep <<< "$curr_data" - local new_score=$((old_score + 15)) - [ "$new_score" -gt 100 ] && new_score=100 - echo "$new_score|$old_hits|$old_bot|$old_attacks|$old_ban|$old_rep" > "$ip_file" - fi - - # High-risk country adds 5 points - if is_high_risk_country "${geo:-XX}" 2>/dev/null; then - local curr_data=$(cat "$ip_file" 2>/dev/null || echo "0|0|human||0|0") - IFS='|' read -r old_score old_hits old_bot old_attacks old_ban old_rep <<< "$curr_data" - local new_score=$((old_score + 5)) - [ "$new_score" -gt 100 ] && new_score=100 - echo "$new_score|$old_hits|$old_bot|$old_attacks|$old_ban|$old_rep" > "$ip_file" - fi ) & + + # Calculate reputation bonuses NOW (synchronously) so they get added to score + # Apply reputation boosts based on AbuseIPDB + if [ "${abuse_conf:-0}" -ge 75 ]; then + # High confidence malicious - add 30 points + threat_intel_bonus=30 + elif [ "${abuse_conf:-0}" -ge 50 ]; then + # Medium confidence - add 15 points + threat_intel_bonus=15 + fi + + # High-risk country adds 5 points + if is_high_risk_country "${geo:-XX}" 2>/dev/null; then + threat_intel_bonus=$((threat_intel_bonus + 5)) + fi fi # Reputation pre-boost: IPs with existing HTTP attacks get higher SYN scoring @@ -2848,7 +2843,11 @@ monitor_network_attacks() { # This indicates sophisticated attacker (SYN flood + application layer) local multi_vector=0 if [ -f "$TEMP_DIR/ip_${ip//\./_}" ]; then - local existing_attacks=$(grep -oP 'attacks=\K[^|]+' "$TEMP_DIR/ip_${ip//\./_}" 2>/dev/null || echo "") + # CRITICAL FIX: Parse pipe-delimited format correctly + # File format: score|hits|bot_type|attacks|ban_count|rep_score + # Bug: was trying to parse 'attacks=' key which doesn't exist + # Fixed: Use cut to extract 4th field (attacks) + local existing_attacks=$(cut -d'|' -f4 "$TEMP_DIR/ip_${ip//\./_}" 2>/dev/null || echo "") if [[ "$existing_attacks" =~ (SQLI|XSS|RCE|LFI|RFI|WEBSHELL) ]]; then multi_vector=1 conn_bonus=$((conn_bonus + 30)) # Multi-vector = very dangerous @@ -2901,10 +2900,14 @@ monitor_network_attacks() { conn_bonus=$((conn_bonus + geo_bonus)) # First hit or add to existing score - if [ "${hits:-0}" -eq 1 ]; then - score=$conn_bonus + # CRITICAL FIX: Reversed the condition - repeat detections should ADD, not RESET + # Bug: hits=0 means NEW IP (initialize score), hits=1+ means REPEAT (accumulate) + # Previous: reset score on repeat detection, losing threat history + # Now: initialize only on first detection, accumulate on repeats + if [ "${hits:-0}" -eq 0 ]; then + score=$conn_bonus # First detection: initialize to connection bonus else - score=$((score + conn_bonus)) + score=$((score + conn_bonus)) # Repeat detection: ADD to accumulated score fi # Apply advanced intelligence bonuses @@ -2913,6 +2916,13 @@ monitor_network_attacks() { IFS='|' read -r vel_count vel_bonus vel_reason <<< "$velocity_data" [ "$vel_bonus" -gt 0 ] && score=$((score + vel_bonus)) && block_reasons="${vel_reason}" + # Apply threat intelligence bonuses (AbuseIPDB, geolocation) + if [ "$threat_intel_bonus" -gt 0 ]; then + score=$((score + threat_intel_bonus)) + [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons="" + block_reasons="${block_reasons}THREAT_INTEL(+${threat_intel_bonus})" + fi + local div_data=$(calculate_diversity_bonus "$ip") IFS='|' read -r div_count div_bonus div_reason <<< "$div_data" if [ "$div_bonus" -gt 0 ]; then