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:
@@ -26,6 +26,9 @@ source "$SCRIPT_DIR/lib/http-attack-analyzer.sh" 2>/dev/null || {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Try to source IP reputation library (optional)
|
||||
source "$SCRIPT_DIR/lib/ip-reputation.sh" 2>/dev/null
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
@@ -148,6 +151,9 @@ MEDIUM_ATTACKS=0
|
||||
declare -A ATTACK_TYPES
|
||||
declare -A TOP_ATTACKERS
|
||||
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
|
||||
show_progress() {
|
||||
@@ -231,22 +237,36 @@ uri="${temp#*||}"
|
||||
ATTACK_TYPES["$type"]=$((${ATTACK_TYPES[$type]:-0} + 1))
|
||||
done
|
||||
|
||||
# Track top attackers
|
||||
# Track top attackers (cumulative 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
|
||||
IFS=',' read -ra sigs <<< "$signatures"
|
||||
for sig in "${sigs[@]}"; do
|
||||
SIGNATURE_HITS["$sig"]=$((${SIGNATURE_HITS[$sig]:-0} + 1))
|
||||
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
|
||||
done < <($CAT_CMD "$log_file" 2>/dev/null)
|
||||
|
||||
@@ -254,6 +274,83 @@ uri="${temp#*||}"
|
||||
done
|
||||
|
||||
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 "================================================================================
|
||||
" >> "$OUTPUT_FILE"
|
||||
echo "SUMMARY STATISTICS" >> "$OUTPUT_FILE"
|
||||
@@ -262,6 +359,9 @@ uri="${temp#*||}"
|
||||
echo "" >> "$OUTPUT_FILE"
|
||||
echo "Total lines processed: $TOTAL_LINES" >> "$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 " - High (70-84): $HIGH_ATTACKS" >> "$OUTPUT_FILE"
|
||||
echo " - Medium (50-69): $MEDIUM_ATTACKS" >> "$OUTPUT_FILE"
|
||||
@@ -271,29 +371,11 @@ uri="${temp#*||}"
|
||||
echo "Top Attack Types:" >> "$OUTPUT_FILE"
|
||||
for type in "${!ATTACK_TYPES[@]}"; do
|
||||
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"
|
||||
done
|
||||
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 "================================================================================
|
||||
" >> "$OUTPUT_FILE"
|
||||
echo "END OF REPORT" >> "$OUTPUT_FILE"
|
||||
|
||||
Reference in New Issue
Block a user