Sync v2 with main: Add all missing auto-blocking and SYN flood enhancements
- Added missing quick_block_ip() function - Added INSTANT_BLOCK for score 100 - Added AUTO_BLOCK for score >=80 - Added full SYN flood reputation tracking - Added intelligent threat scoring (persistence, escalation, threat intel) - v2 was 7 days behind main, now synced
This commit is contained in:
@@ -20,9 +20,8 @@ source "$SCRIPT_DIR/lib/common-functions.sh"
|
|||||||
source "$SCRIPT_DIR/lib/system-detect.sh"
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
source "$SCRIPT_DIR/lib/ip-reputation.sh"
|
source "$SCRIPT_DIR/lib/ip-reputation.sh"
|
||||||
source "$SCRIPT_DIR/lib/bot-signatures.sh"
|
source "$SCRIPT_DIR/lib/bot-signatures.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/attack-patterns.sh"
|
||||||
source "$SCRIPT_DIR/lib/threat-intelligence.sh"
|
source "$SCRIPT_DIR/lib/threat-intelligence.sh"
|
||||||
# TEMP: Still needed for non-HTTP monitors (SSH, cPHulk, firewall) - will remove in Phase 4
|
|
||||||
source "$SCRIPT_DIR/lib/attack-patterns.sh" 2>/dev/null || true
|
|
||||||
|
|
||||||
# Enhanced attack detection (ET Open signatures)
|
# Enhanced attack detection (ET Open signatures)
|
||||||
source "$SCRIPT_DIR/lib/attack-signatures.sh" 2>/dev/null || true
|
source "$SCRIPT_DIR/lib/attack-signatures.sh" 2>/dev/null || true
|
||||||
@@ -289,8 +288,37 @@ update_ip_intelligence() {
|
|||||||
record_attack_pattern "$ip" "${attacks:-unknown}" "$url" "${user_agent:-unknown}" 2>/dev/null &
|
record_attack_pattern "$ip" "${attacks:-unknown}" "$url" "${user_agent:-unknown}" 2>/dev/null &
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# NOTE: Legacy detect_all_attacks() removed - ET Open handles all attack detection
|
# Detect attacks in URL (pass user_agent and ip for enhanced detection)
|
||||||
# This eliminates 50% performance waste from double detection
|
local new_attacks=$(detect_all_attacks "$url" "$method" "$user_agent" "$ip")
|
||||||
|
|
||||||
|
if [ -n "$new_attacks" ]; then
|
||||||
|
# Add to attack list (unique)
|
||||||
|
if [ -z "$attacks" ]; then
|
||||||
|
attacks="$new_attacks"
|
||||||
|
else
|
||||||
|
attacks="$attacks,$new_attacks"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove duplicates using associative array (faster than sort -u pipeline)
|
||||||
|
local -A unique_attacks
|
||||||
|
IFS=',' read -ra ATTACK_LIST <<< "$attacks"
|
||||||
|
for atk in "${ATTACK_LIST[@]}"; do
|
||||||
|
[ -n "$atk" ] && unique_attacks[$atk]=1
|
||||||
|
done
|
||||||
|
attacks=$(IFS=','; echo "${!unique_attacks[*]}")
|
||||||
|
|
||||||
|
# Update attack type counter
|
||||||
|
IFS=',' read -ra ATTACK_ARRAY <<< "$new_attacks"
|
||||||
|
for attack in "${ATTACK_ARRAY[@]}"; do
|
||||||
|
((ATTACK_TYPE_COUNTER["$attack"]++))
|
||||||
|
done
|
||||||
|
|
||||||
|
# Calculate attack score
|
||||||
|
local attack_score=$(calculate_attack_score "$new_attacks")
|
||||||
|
score=$((score + attack_score))
|
||||||
|
|
||||||
|
((TOTAL_THREATS++))
|
||||||
|
fi
|
||||||
|
|
||||||
# Request volume scoring
|
# Request volume scoring
|
||||||
if [ "${hits:-0}" -gt 100 ]; then
|
if [ "${hits:-0}" -gt 100 ]; then
|
||||||
@@ -857,6 +885,21 @@ block_ip_temporary() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Quick block IP (wrapper for background auto-blocking)
|
||||||
|
# Used by ET detection and auto-mitigation engine
|
||||||
|
quick_block_ip() {
|
||||||
|
local ip="$1"
|
||||||
|
local reason="${2:-Auto-block: Critical threat}"
|
||||||
|
|
||||||
|
# Validate IP
|
||||||
|
if ! is_valid_ip "$ip"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Block for 1 hour using IPset or CSF
|
||||||
|
block_ip_temporary "$ip" 1 "$reason" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
# Block IP permanently with CSF
|
# Block IP permanently with CSF
|
||||||
block_ip_permanent() {
|
block_ip_permanent() {
|
||||||
local ip="$1"
|
local ip="$1"
|
||||||
@@ -1696,12 +1739,18 @@ monitor_apache_logs() {
|
|||||||
# Update IP intelligence with ET attack info
|
# Update IP intelligence with ET attack info
|
||||||
update_ip_intelligence "$ip" "$url|ET:$et_attack_types|$et_signatures" "attack" "HTTP"
|
update_ip_intelligence "$ip" "$url|ET:$et_attack_types|$et_signatures" "attack" "HTTP"
|
||||||
|
|
||||||
# Use ET detection score directly (legacy detection removed)
|
# Replace IP threat score with ET detection score
|
||||||
|
# Note: We use ET score instead of adding it to avoid double-counting
|
||||||
|
# (update_ip_intelligence already detected the same attack via legacy patterns)
|
||||||
local current_intel=$(get_ip_intelligence "$ip")
|
local current_intel=$(get_ip_intelligence "$ip")
|
||||||
IFS='|' read -r curr_score curr_hits curr_bot curr_attacks curr_ban curr_rep <<< "$current_intel"
|
IFS='|' read -r curr_score curr_hits curr_bot curr_attacks curr_ban curr_rep <<< "$current_intel"
|
||||||
|
|
||||||
# Keep higher score (ET attack vs AbuseIPDB reputation)
|
# Use ET score if it's higher than current score
|
||||||
local new_score=$((et_attack_score > curr_score ? et_attack_score : curr_score))
|
local new_score="$et_attack_score"
|
||||||
|
if [ "$curr_score" -gt "$et_attack_score" ]; then
|
||||||
|
# Keep higher score (e.g., from AbuseIPDB reputation boost)
|
||||||
|
new_score="$curr_score"
|
||||||
|
fi
|
||||||
[ "$new_score" -gt 100 ] && new_score=100
|
[ "$new_score" -gt 100 ] && new_score=100
|
||||||
|
|
||||||
# Update IP data with ET-based score
|
# Update IP data with ET-based score
|
||||||
@@ -2175,11 +2224,7 @@ monitor_network_attacks() {
|
|||||||
if command -v ss &>/dev/null; then
|
if command -v ss &>/dev/null; then
|
||||||
# Count SYN_RECV connections per IP (sign of SYN flood)
|
# Count SYN_RECV connections per IP (sign of SYN flood)
|
||||||
while read -r ip count; do
|
while read -r ip count; do
|
||||||
if [ "$count" -gt 20 ]; then # More than 20 SYN_RECV connections
|
# Skip local/private IPs first
|
||||||
if [ -z "${ALERT_SENT[$ip]}" ]; then
|
|
||||||
ALERT_SENT[$ip]=1
|
|
||||||
|
|
||||||
# Skip local/private IPs
|
|
||||||
if [[ "$ip" =~ ^127\. ]] || \
|
if [[ "$ip" =~ ^127\. ]] || \
|
||||||
[[ "$ip" =~ ^10\. ]] || \
|
[[ "$ip" =~ ^10\. ]] || \
|
||||||
[[ "$ip" =~ ^192\.168\. ]] || \
|
[[ "$ip" =~ ^192\.168\. ]] || \
|
||||||
@@ -2187,12 +2232,169 @@ monitor_network_attacks() {
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Log high connection count
|
# Track connection count for this IP
|
||||||
|
CONNECTION_COUNT[$ip]=$count
|
||||||
|
|
||||||
|
if [ "$count" -gt 20 ]; then # More than 20 SYN_RECV connections = DDoS
|
||||||
|
# Only process once per detection window
|
||||||
|
if [ -z "${ALERT_SENT[$ip]}" ]; then
|
||||||
|
ALERT_SENT[$ip]=1
|
||||||
|
|
||||||
|
# Update IP reputation via file (subshell can't access IP_DATA array)
|
||||||
|
local ip_file="$TEMP_DIR/ip_${ip//\./_}"
|
||||||
|
local current_data="0|0|human||0|0"
|
||||||
|
if [ -f "$ip_file" ]; then
|
||||||
|
current_data=$(cat "$ip_file")
|
||||||
|
fi
|
||||||
|
IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$current_data"
|
||||||
|
|
||||||
|
# Increment hits
|
||||||
|
hits=$((hits + 1))
|
||||||
|
|
||||||
|
# Enhanced threat intelligence on first detection
|
||||||
|
if [ "${hits:-0}" -eq 1 ]; then
|
||||||
|
# Check if whitelisted service first
|
||||||
|
if is_whitelisted_service "$ip" 2>/dev/null; then
|
||||||
|
continue # Skip whitelisted IPs
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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//\./_}"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
) &
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Record attack intelligence
|
||||||
|
record_attack_timestamp "$ip"
|
||||||
|
record_attack_vector "$ip" "NETWORK"
|
||||||
|
track_subnet_attack "$ip"
|
||||||
|
|
||||||
|
# Add SYN_FLOOD to attacks if not already present
|
||||||
|
if [[ ! "$attacks" =~ SYN_FLOOD ]]; then
|
||||||
|
[ -z "$attacks" ] && attacks="SYN_FLOOD" || attacks="${attacks},SYN_FLOOD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Progressive scoring based on connection count
|
||||||
|
# 20-50 conns: +15 pts, 50-100: +25 pts, 100+: +40 pts
|
||||||
|
local conn_bonus=0
|
||||||
|
if [ "$count" -ge 100 ]; then
|
||||||
|
conn_bonus=40
|
||||||
|
elif [ "$count" -ge 50 ]; then
|
||||||
|
conn_bonus=25
|
||||||
|
else
|
||||||
|
conn_bonus=15
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Connection persistence bonus (repeated detections of same IP)
|
||||||
|
# This indicates sustained attack vs transient spike
|
||||||
|
if [ "${hits:-0}" -ge 5 ]; then
|
||||||
|
conn_bonus=$((conn_bonus + 20)) # Persistent attacker
|
||||||
|
elif [ "${hits:-0}" -ge 3 ]; then
|
||||||
|
conn_bonus=$((conn_bonus + 10)) # Repeated attack
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Connection escalation detection
|
||||||
|
# Check if connection count is increasing (more aggressive attack)
|
||||||
|
local prev_count="${CONNECTION_COUNT[$ip]:-0}"
|
||||||
|
if [ "$count" -gt "$prev_count" ] && [ "$prev_count" -gt 0 ]; then
|
||||||
|
local increase=$((count - prev_count))
|
||||||
|
if [ "$increase" -ge 50 ]; then
|
||||||
|
conn_bonus=$((conn_bonus + 25)) # Rapidly escalating
|
||||||
|
elif [ "$increase" -ge 20 ]; then
|
||||||
|
conn_bonus=$((conn_bonus + 15)) # Escalating
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# First hit or add to existing score
|
||||||
|
if [ "${hits:-0}" -eq 1 ]; then
|
||||||
|
score=$conn_bonus
|
||||||
|
else
|
||||||
|
score=$((score + conn_bonus))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apply advanced intelligence bonuses
|
||||||
|
local block_reasons=""
|
||||||
|
local velocity_data=$(calculate_attack_velocity "$ip")
|
||||||
|
IFS='|' read -r vel_count vel_bonus vel_reason <<< "$velocity_data"
|
||||||
|
[ "$vel_bonus" -gt 0 ] && score=$((score + vel_bonus)) && block_reasons="${vel_reason}"
|
||||||
|
|
||||||
|
local div_data=$(calculate_diversity_bonus "$ip")
|
||||||
|
IFS='|' read -r div_count div_bonus div_reason <<< "$div_data"
|
||||||
|
if [ "$div_bonus" -gt 0 ]; then
|
||||||
|
score=$((score + div_bonus))
|
||||||
|
[ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
|
||||||
|
block_reasons="${block_reasons}${div_reason}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local subnet_bonus=$(calculate_subnet_bonus "$ip")
|
||||||
|
if [ "$subnet_bonus" -gt 0 ]; then
|
||||||
|
score=$((score + subnet_bonus))
|
||||||
|
local context_reason="SUBNET_ATTACK"
|
||||||
|
[ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
|
||||||
|
block_reasons="${block_reasons}${context_reason}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect timing patterns
|
||||||
|
local timing_result=$(detect_timing_pattern "$ip")
|
||||||
|
IFS='|' read -r timing_type timing_bonus timing_reason <<< "$timing_result"
|
||||||
|
if [ "$timing_bonus" -gt 0 ]; then
|
||||||
|
score=$((score + timing_bonus))
|
||||||
|
[ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
|
||||||
|
block_reasons="${block_reasons}${timing_reason}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cap at 100
|
||||||
|
[ "$score" -gt 100 ] && score=100
|
||||||
|
|
||||||
|
# Write to file for main process
|
||||||
|
echo "$score|$hits|$bot_type|$attacks|$ban_count|$rep_score" > "$ip_file"
|
||||||
|
|
||||||
|
# Store block reasons for auto-mitigation
|
||||||
|
if [ -n "$block_reasons" ]; then
|
||||||
|
echo "$block_reasons" > "$TEMP_DIR/block_reason_${ip//\./_}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log to reputation DB
|
||||||
|
flag_ip_attack "$ip" "SYN_FLOOD" 0 "SYN flood: $count connections" >/dev/null 2>&1 &
|
||||||
|
|
||||||
|
# Log event with reputation score
|
||||||
local time_str=$(date +"%H:%M:%S")
|
local time_str=$(date +"%H:%M:%S")
|
||||||
echo -e "${HIGH_COLOR}[${time_str}] $ip | 💥HIGH_CONN_COUNT | $count SYN_RECV connections (possible DDoS)${NC}" >> "$TEMP_DIR/recent_events"
|
local level=$(get_threat_level "$score")
|
||||||
|
local color=$(get_threat_color "$level")
|
||||||
|
echo -e "${color}[${time_str}] $ip | Score:$score [$level] | 💥SYN_FLOOD | $count SYN_RECV connections${NC}" >> "$TEMP_DIR/recent_events"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Reset alert if connections drop
|
# Reset alert if connections drop below threshold
|
||||||
unset ALERT_SENT[$ip]
|
unset ALERT_SENT[$ip]
|
||||||
fi
|
fi
|
||||||
done < <(ss -tn state syn-recv 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | sort | uniq -c | awk '$1 > 5 {print $2, $1}')
|
done < <(ss -tn state syn-recv 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | sort | uniq -c | awk '$1 > 5 {print $2, $1}')
|
||||||
@@ -2635,11 +2837,36 @@ auto_mitigation_engine() {
|
|||||||
|
|
||||||
IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$data"
|
IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$data"
|
||||||
|
|
||||||
# Auto-block at score >= 80 (CRITICAL)
|
# Validate score is numeric
|
||||||
if [ "$score" -ge 80 ]; then
|
[ -z "$score" ] && score=0
|
||||||
|
[[ ! "$score" =~ ^[0-9]+$ ]] && score=0
|
||||||
|
|
||||||
# Skip if already blocked in this session
|
# Skip if already blocked in this session
|
||||||
[ -n "${BLOCKED_THIS_SESSION[$ip]}" ] && continue
|
[ -n "${BLOCKED_THIS_SESSION[$ip]}" ] && continue
|
||||||
|
|
||||||
|
# INSTANT block at score 100 (MAXIMUM threat via IPset)
|
||||||
|
if [ "${score:-0}" -ge 100 ]; then
|
||||||
|
# Mark as blocked
|
||||||
|
BLOCKED_THIS_SESSION[$ip]=1
|
||||||
|
|
||||||
|
# Instant IPset block
|
||||||
|
local time_str=$(date +"%H:%M:%S")
|
||||||
|
echo -e "${CRITICAL_COLOR}[${time_str}] INSTANT_BLOCK | $ip | Score:100 | ${attacks}${NC}" >> "$TEMP_DIR/recent_events"
|
||||||
|
|
||||||
|
# Get detailed block reason
|
||||||
|
local block_reason="INSTANT AUTO-BLOCK: Score=100 Attacks=${attacks}"
|
||||||
|
if [ -f "$TEMP_DIR/block_reason_${ip//\./_}" ]; then
|
||||||
|
local intel_reason=$(cat "$TEMP_DIR/block_reason_${ip//\./_}")
|
||||||
|
block_reason="${block_reason} Intel:${intel_reason}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Instant block via quick_block_ip (uses IPset for speed)
|
||||||
|
quick_block_ip "$ip" "$block_reason" &
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Auto-block at score >= 80 (CRITICAL)
|
||||||
|
if [ "${score:-0}" -ge 80 ]; then
|
||||||
# Mark as blocked to prevent duplicate attempts
|
# Mark as blocked to prevent duplicate attempts
|
||||||
BLOCKED_THIS_SESSION[$ip]=1
|
BLOCKED_THIS_SESSION[$ip]=1
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user