Rewrite historical attack analyzer to show per-IP summaries

Issue:
User reported: "it seems to just list all possible hits"
- Old format listed every individual attack hit
- No grouping or organization by IP
- Hard to understand what each IP actually did
- No reputation context

User Request:
"show an IP, saying what it did, saying how many times it did it,
and what its reputation is"

Solution:
Completely rewrote output format to group by IP with summaries:

New Output Format:
================================================================================
ATTACKING IPs - DETAILED BREAKDOWN
================================================================================

[1] 192.168.1.100
    Attacks: 15 | Avg Score: 87 | Threat Level: CRITICAL
    Attack Types: WEBSHELL(8), SQLI(5), XSS(2)
    Reputation: AbuseIPDB 85% confidence (142 reports) | China
    Sample Targets:
      - /wp-admin/alfa-rex.php
      - /admin.php?id=1' union select...
      - /upload.php?file=../../../../etc/passwd

[2] 45.83.66.23
    Attacks: 8 | Avg Score: 92 | Threat Level: CRITICAL
    Attack Types: CMD(5), TRAVERSAL(3)
    Sample Targets:
      - /cgi-bin/admin.cgi?cmd=cat%20/etc/passwd
      - /../../../etc/shadow

Changes Made:

1. Added IP-level tracking (lines 151-153):
   - IP_ATTACK_DETAILS: Store all attack types per IP
   - IP_ATTACK_COUNT: Count total attacks per IP
   - IP_SAMPLE_URLS: Store first 3 sample URLs per IP

2. Track data during scan (lines 240-260):
   - Aggregate attack types per IP
   - Keep sample URLs for context
   - Count occurrences of each attack type

3. New output section (lines 284-352):
   - Sort IPs by cumulative threat score (worst first)
   - Calculate average score per IP
   - Count attack type occurrences: "SQLI(5), XSS(2)"
   - Show reputation from AbuseIPDB (if available)
   - Display sample target URLs for context
   - Limit to top 50 attacking IPs

4. Improved summary stats (lines 360-381):
   - Added "Unique attacking IPs" count
   - Condensed attack type summary to top 10
   - Removed redundant "Top Signatures" section

5. Source IP reputation library (line 30):
   - Optional: loads get_threat_intelligence() if available
   - Gracefully skips reputation if not available

Benefits:
 Clean per-IP summary (not a flood of individual hits)
 Shows what each IP did and how many times
 Includes reputation context from AbuseIPDB
 Sample URLs provide attack pattern examples
 Sorted by threat level (worst attackers first)
 Much easier to understand and act on
This commit is contained in:
cschantz
2025-12-13 02:40:15 -05:00
parent 7895657049
commit dd643b7d0e
+110 -28
View File
@@ -26,6 +26,9 @@ source "$SCRIPT_DIR/lib/http-attack-analyzer.sh" 2>/dev/null || {
exit 1 exit 1
} }
# Try to source IP reputation library (optional)
source "$SCRIPT_DIR/lib/ip-reputation.sh" 2>/dev/null
# Colors # Colors
RED='\033[0;31m' RED='\033[0;31m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
@@ -148,6 +151,9 @@ MEDIUM_ATTACKS=0
declare -A ATTACK_TYPES declare -A ATTACK_TYPES
declare -A TOP_ATTACKERS declare -A TOP_ATTACKERS
declare -A SIGNATURE_HITS declare -A SIGNATURE_HITS
declare -A IP_ATTACK_DETAILS # Store detailed attack info per IP
declare -A IP_ATTACK_COUNT # Count attacks per IP
declare -A IP_SAMPLE_URLS # Sample URLs per IP
# Progress indicator # Progress indicator
show_progress() { show_progress() {
@@ -231,22 +237,36 @@ uri="${temp#*||}"
ATTACK_TYPES["$type"]=$((${ATTACK_TYPES[$type]:-0} + 1)) ATTACK_TYPES["$type"]=$((${ATTACK_TYPES[$type]:-0} + 1))
done done
# Track top attackers # Track top attackers (cumulative score)
TOP_ATTACKERS["$ip"]=$((${TOP_ATTACKERS[$ip]:-0} + threat_score)) TOP_ATTACKERS["$ip"]=$((${TOP_ATTACKERS[$ip]:-0} + threat_score))
# Track attack count per IP
IP_ATTACK_COUNT["$ip"]=$((${IP_ATTACK_COUNT[$ip]:-0} + 1))
# Store attack type details per IP
current_types="${IP_ATTACK_DETAILS[$ip]}"
if [ -z "$current_types" ]; then
IP_ATTACK_DETAILS["$ip"]="$attack_types"
else
IP_ATTACK_DETAILS["$ip"]="$current_types,$attack_types"
fi
# Store sample URL (keep first 3)
current_urls="${IP_SAMPLE_URLS[$ip]}"
url_count=$(echo "$current_urls" | grep -o "||" | wc -l)
if [ "$url_count" -lt 3 ]; then
if [ -z "$current_urls" ]; then
IP_SAMPLE_URLS["$ip"]="${uri:0:100}"
else
IP_SAMPLE_URLS["$ip"]="$current_urls||${uri:0:100}"
fi
fi
# Track signatures # Track signatures
IFS=',' read -ra sigs <<< "$signatures" IFS=',' read -ra sigs <<< "$signatures"
for sig in "${sigs[@]}"; do for sig in "${sigs[@]}"; do
SIGNATURE_HITS["$sig"]=$((${SIGNATURE_HITS[$sig]:-0} + 1)) SIGNATURE_HITS["$sig"]=$((${SIGNATURE_HITS[$sig]:-0} + 1))
done done
# Log if verbose or critical
if [ "$VERBOSE" -eq 1 ] || [ "$threat_score" -ge 85 ]; then
echo "" >> "$OUTPUT_FILE"
echo "[Score: $threat_score] $ip$attack_types" >> "$OUTPUT_FILE"
echo " URI: ${uri:0:150}" >> "$OUTPUT_FILE"
echo " Signatures: $signatures" >> "$OUTPUT_FILE"
fi
fi fi
done < <($CAT_CMD "$log_file" 2>/dev/null) done < <($CAT_CMD "$log_file" 2>/dev/null)
@@ -254,6 +274,83 @@ uri="${temp#*||}"
done done
echo "" >> "$OUTPUT_FILE" echo "" >> "$OUTPUT_FILE"
echo "================================================================================
" >> "$OUTPUT_FILE"
echo "ATTACKING IPs - DETAILED BREAKDOWN" >> "$OUTPUT_FILE"
echo "================================================================================
" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
# Sort IPs by cumulative threat score and display
# Create sorted list first to avoid subshell issues
sorted_ips=$(for ip in "${!TOP_ATTACKERS[@]}"; do
echo "${TOP_ATTACKERS[$ip]}:$ip"
done | sort -t: -k1 -nr | head -50)
ip_count=0
while IFS=: read -r cumulative_score ip; do
ip_count=$((ip_count + 1))
attack_count="${IP_ATTACK_COUNT[$ip]:-0}"
all_attack_types="${IP_ATTACK_DETAILS[$ip]}"
sample_urls="${IP_SAMPLE_URLS[$ip]}"
# Count occurrences of each attack type
declare -A type_counts
IFS=',' read -ra attacks <<< "$all_attack_types"
for attack in "${attacks[@]}"; do
[ -n "$attack" ] && type_counts["$attack"]=$((${type_counts[$attack]:-0} + 1))
done
# Format attack summary
attack_summary=""
for type in "${!type_counts[@]}"; do
if [ -z "$attack_summary" ]; then
attack_summary="$type(${type_counts[$type]})"
else
attack_summary="$attack_summary, $type(${type_counts[$type]})"
fi
done
unset type_counts
# Determine threat level
avg_score=$((cumulative_score / attack_count))
if [ "$avg_score" -ge 85 ]; then
level="CRITICAL"
elif [ "$avg_score" -ge 70 ]; then
level="HIGH"
else
level="MEDIUM"
fi
# Print IP summary
echo "[$ip_count] $ip" >> "$OUTPUT_FILE"
printf " Attacks: %d | Avg Score: %d | Threat Level: %s\n" "$attack_count" "$avg_score" "$level" >> "$OUTPUT_FILE"
echo " Attack Types: $attack_summary" >> "$OUTPUT_FILE"
# Get reputation (if available)
if type get_threat_intelligence &>/dev/null; then
threat_intel=$(get_threat_intelligence "$ip" 2>/dev/null)
if [ -n "$threat_intel" ]; then
IFS='|' read -r abuse_conf abuse_rpts country isp geo timing whitelisted <<< "$threat_intel"
if [ "${abuse_conf:-0}" -gt 0 ]; then
printf " Reputation: AbuseIPDB %d%% confidence (%d reports) | %s\n" "${abuse_conf:-0}" "${abuse_rpts:-0}" "${country:-Unknown}" >> "$OUTPUT_FILE"
fi
fi
fi
# Show sample URLs
if [ -n "$sample_urls" ]; then
echo " Sample Targets:" >> "$OUTPUT_FILE"
IFS='||' read -ra urls <<< "$sample_urls"
for url in "${urls[@]}"; do
echo " - $url" >> "$OUTPUT_FILE"
done
fi
echo "" >> "$OUTPUT_FILE"
done <<< "$sorted_ips"
echo "================================================================================ echo "================================================================================
" >> "$OUTPUT_FILE" " >> "$OUTPUT_FILE"
echo "SUMMARY STATISTICS" >> "$OUTPUT_FILE" echo "SUMMARY STATISTICS" >> "$OUTPUT_FILE"
@@ -262,6 +359,9 @@ uri="${temp#*||}"
echo "" >> "$OUTPUT_FILE" echo "" >> "$OUTPUT_FILE"
echo "Total lines processed: $TOTAL_LINES" >> "$OUTPUT_FILE" echo "Total lines processed: $TOTAL_LINES" >> "$OUTPUT_FILE"
echo "Total attacks detected: $TOTAL_ATTACKS" >> "$OUTPUT_FILE" echo "Total attacks detected: $TOTAL_ATTACKS" >> "$OUTPUT_FILE"
echo "Unique attacking IPs: ${#TOP_ATTACKERS[@]}" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "Attack Severity:" >> "$OUTPUT_FILE"
echo " - Critical (≥85): $CRITICAL_ATTACKS" >> "$OUTPUT_FILE" echo " - Critical (≥85): $CRITICAL_ATTACKS" >> "$OUTPUT_FILE"
echo " - High (70-84): $HIGH_ATTACKS" >> "$OUTPUT_FILE" echo " - High (70-84): $HIGH_ATTACKS" >> "$OUTPUT_FILE"
echo " - Medium (50-69): $MEDIUM_ATTACKS" >> "$OUTPUT_FILE" echo " - Medium (50-69): $MEDIUM_ATTACKS" >> "$OUTPUT_FILE"
@@ -271,29 +371,11 @@ uri="${temp#*||}"
echo "Top Attack Types:" >> "$OUTPUT_FILE" echo "Top Attack Types:" >> "$OUTPUT_FILE"
for type in "${!ATTACK_TYPES[@]}"; do for type in "${!ATTACK_TYPES[@]}"; do
echo "$type:${ATTACK_TYPES[$type]}" echo "$type:${ATTACK_TYPES[$type]}"
done | sort -t: -k2 -nr | head -15 | while IFS=: read -r type count; do done | sort -t: -k2 -nr | head -10 | while IFS=: read -r type count; do
printf " %-20s %5d attacks\n" "$type" "$count" >> "$OUTPUT_FILE" printf " %-20s %5d attacks\n" "$type" "$count" >> "$OUTPUT_FILE"
done done
echo "" >> "$OUTPUT_FILE" echo "" >> "$OUTPUT_FILE"
# Top Attackers
echo "Top 20 Attacking IPs (by cumulative threat score):" >> "$OUTPUT_FILE"
for ip in "${!TOP_ATTACKERS[@]}"; do
echo "$ip:${TOP_ATTACKERS[$ip]}"
done | sort -t: -k2 -nr | head -20 | while IFS=: read -r ip score; do
printf " %-15s Score: %5d\n" "$ip" "$score" >> "$OUTPUT_FILE"
done
echo "" >> "$OUTPUT_FILE"
# Top Signatures
echo "Top 20 Triggered Signatures:" >> "$OUTPUT_FILE"
for sig in "${!SIGNATURE_HITS[@]}"; do
echo "$sig:${SIGNATURE_HITS[$sig]}"
done | sort -t: -k2 -nr | head -20 | while IFS=: read -r sig count; do
printf " %-30s %5d hits\n" "$sig" "$count" >> "$OUTPUT_FILE"
done
echo "" >> "$OUTPUT_FILE"
echo "================================================================================ echo "================================================================================
" >> "$OUTPUT_FILE" " >> "$OUTPUT_FILE"
echo "END OF REPORT" >> "$OUTPUT_FILE" echo "END OF REPORT" >> "$OUTPUT_FILE"