-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
reporter.sh
222 lines (185 loc) · 7.06 KB
/
reporter.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#!/bin/bash
###
# https://github.com/sefinek/UFW-AbuseIPDB-Reporter
##
LOG_FILE="/var/log/ufw.log"
ENCODED_API_KEY_FILE="./.abuseipdb_token"
REPORTED_IPS_FILE="/tmp/ufw-abuseipdb-reporter.cache"
REPORT_INTERVAL=43200 # 12h (in seconds)
declare -A reported_ips
log() {
local level="$1"
local message="$2"
echo "[$level] $message"
}
# Check if the API key file exists and decode it
if [[ -f "$ENCODED_API_KEY_FILE" ]]; then
DECODED_API_KEY=$(openssl enc -d -base64 -in "$ENCODED_API_KEY_FILE")
if [[ -z "$DECODED_API_KEY" ]]; then
log "ERROR" "Failed to decode API key from $ENCODED_API_KEY_FILE"
exit 1
fi
else
log "ERROR" "API key file not found at $ENCODED_API_KEY_FILE"
exit 1
fi
ABUSEIPDB_API_KEY="$DECODED_API_KEY"
# Check if jq, curl, or wget packages are available
if ! command -v jq &> /dev/null; then
log "ERROR" "jq is not installed. Please install jq to run this script."
exit 1
fi
if ! command -v curl &> /dev/null && ! command -v wget &> /dev/null; then
log "ERROR" "Neither curl nor wget is available. Please install one of them to continue."
exit 1
fi
load_reported_ips() {
if [[ -f "$REPORTED_IPS_FILE" ]]; then
while IFS= read -r line; do
[[ -z "$line" ]] && continue
IFS=' ' read -r ip report_time <<< "$line"
if [[ -n "$ip" && -n "$report_time" ]]; then
reported_ips["$ip"]=$report_time
else
log "WARN" "Invalid line format: '$line'"
fi
done < "$REPORTED_IPS_FILE"
log "INFO" "Loaded ${#reported_ips[@]} IPs from $REPORTED_IPS_FILE"
else
log "INFO" "$REPORTED_IPS_FILE does not exist. No data to load."
fi
}
save_reported_ips() {
: > "$REPORTED_IPS_FILE"
for ip in "${!reported_ips[@]}"; do
echo "$ip ${reported_ips[$ip]}" >> "$REPORTED_IPS_FILE"
done
}
is_local_ip() {
local ip="$1"
[[ "$ip" =~ ^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.|fc|fd|fe80|::1) ]]
}
report_to_abuseipdb() {
local ip="$1" categories="$2" proto="$3" spt="$4" dpt="$5" ttl="$6" len="$7" tos="$8" timestamp="$9"
local comment="Blocked by UFW ($proto on $dpt)
Source port: $spt"
[[ -n "$ttl" ]] && comment+="
TTL: $ttl"
[[ -n "$len" ]] && comment+="
Packet length: $len"
[[ -n "$tos" ]] && comment+="
TOS: $tos"
comment+="
This report (for $ip) was generated by:
https://github.com/sefinek/UFW-AbuseIPDB-Reporter" # Please do not remove the URL to the repository of this script. I would be really grateful. 💙
local res
if command -v curl >/dev/null 2>&1; then
res=$(curl -s -X POST "https://api.abuseipdb.com/api/v2/report" \
--data-urlencode "ip=$ip" \
--data-urlencode "categories=$categories" \
--data-urlencode "comment=$comment" \
-H "Key: $ABUSEIPDB_API_KEY" \
-H "Accept: application/json")
elif command -v wget >/dev/null 2>&1; then
res=$(wget -qO- --post-data="ip=$ip&categories=$categories&comment=$comment" \
--header="Key: $ABUSEIPDB_API_KEY" \
--header="Accept: application/json" \
"https://api.abuseipdb.com/api/v2/report")
else
log "ERROR" "Neither curl nor wget is available to send the report."
return 1
fi
local abuse_confidence_score
abuse_confidence_score=$(echo "$res" | jq -r '.data.abuseConfidenceScore')
if [[ "$abuse_confidence_score" =~ ^[0-9]+$ ]]; then
log "INFO" "Successfully reported IP $ip to AbuseIPDB (score $abuse_confidence_score)"
return 0
else
log "ERROR" "Failed to report IP $ip to AbuseIPDB: $res"
return 1
fi
}
is_ip_reported_recently() {
local ip="$1"
local current_time
current_time=$(date +%s)
if [[ -v reported_ips["$ip"] ]]; then
local report_time=${reported_ips["$ip"]}
(( current_time - report_time < REPORT_INTERVAL )) && return 0
fi
return 1
}
mark_ip_as_reported() {
local ip="$1"
reported_ips["$ip"]=$(date +%s)
}
determine_categories() {
local proto="$1"
local dpt="$2"
# See: https://www.abuseipdb.com/categories
case "$proto" in
"TCP")
case "$dpt" in
22) echo "14,22,18" ;; # Port Scan | SSH | Brute-Force
80 | 443 | 8080) echo "14,21" ;; # Port Scan | Web App Attack
25) echo "14,11" ;; # Port Scan | Email Spam
21) echo "14,5,18" ;; # Port Scan | FTP Brute-Force | Brute-Force
53) echo "14,1,2" ;; # Port Scan | DNS Compromise | DNS Poisoning
23 | 3389) echo "14,15,18" ;; # Port Scan | Hacking | Brute-Force
3306) echo "14,16" ;; # Port Scan | SQL Injection
6666 | 6667 | 6668 | 6669) echo "14,8" ;; # Port Scan | Fraud VoIP
9999) echo "14,6" ;; # Port Scan | Ping of Death
*) echo "14" ;; # Port Scan
esac
;;
"UDP")
case "$dpt" in
53) echo "14,1,2" ;; # Port Scan | DNS Compromise | DNS Poisoning
123) echo "14,17" ;; # Port Scan | Spoofing
*) echo "14" ;; # Port Scan
esac
;;
*) echo "14" ;; # Port Scan
esac
}
process_log_line() {
local line="$1"
if [[ "$line" == *"[UFW BLOCK]"* ]]; then
local timestamp src_ip proto spt dpt ttl len tos categories
timestamp=$(echo "$line" | awk '{print $1, $2, $3}')
[[ -z "$timestamp" ]] && timestamp=$(date '+%Y-%m-%d %H:%M:%S')
src_ip=$(echo "$line" | grep -oP 'SRC=\K[^\s]+')
if is_local_ip "$src_ip"; then
log "INFO" "Ignoring local IP: $src_ip"
return
fi
proto=$(echo "$line" | grep -oP 'PROTO=\K[^\s]+')
spt=$(echo "$line" | grep -oP 'SPT=\K[^\s]+')
dpt=$(echo "$line" | grep -oP 'DPT=\K[^\s]+')
ttl=$(echo "$line" | grep -oP 'TTL=\K[^\s]+')
len=$(echo "$line" | grep -oP 'LEN=\K[^\s]+')
tos=$(echo "$line" | grep -oP 'TOS=\K[^\s]+')
# Report MUST NOT be of an attack where the source address is likely spoofed i.e. SYN floods and UDP floods.
# TCP connections can only be reported if they complete the three-way handshake. UDP connections cannot be reported.
# More: https://www.abuseipdb.com/reporting-policy
if [[ "$proto" == "UDP" ]]; then
log "INFO" "Skipping UDP traffic: SRC=$src_ip DPT=$dpt"
return
fi
if is_ip_reported_recently "$src_ip"; then
log "INFO" "IP $src_ip ($proto) was reported recently"
return
fi
categories=$(determine_categories "$proto" "$dpt")
log "INFO" "Reporting IP $src_ip ($proto $dpt) with categories $categories..."
if report_to_abuseipdb "$src_ip" "$categories" "$proto" "$spt" "$dpt" "$ttl" "$len" "$tos" "$timestamp"; then
mark_ip_as_reported "$src_ip"
save_reported_ips
fi
fi
}
load_reported_ips
log "INFO" "Starting to monitor $LOG_FILE"
tail -Fn0 "$LOG_FILE" | while read -r line; do
process_log_line "$line"
done