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
|
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user