Agelopen weekend heb ik me verdiept in mogelijkheden om na hackpogingen die afkomstig zijn van een bepaald IP een geautomatiseerde waarschuwingsmail te sturen naar de ISP of het hostingbedrijf waar dat IP onder valt. Hoewel er geen database of iets dergelijks lijkt te bestaan waarin de abuse email adressen staan (zie mijn vraag in dit topic), blijkt het in de praktijk veelal toch mogelijk om iets te doen via reverse mapping. Bijvoorbeeld mijn eigen ip resolvt naar ... .adsl.xs4all.nl. abuse@xs4all.nl is het abuse email adres van xs4all, en na een kleine check blijkt dat dit heel vaak zo werkt.
Ik ben nu dan ook begonnen met het maken van een script dat na een aantal hack/brute-force pogingen geheel geautomatiseerd een waarschuwing mailt naar de provider. Het bevindt zich nog in een beginfase, dus ik ben zeer geïnteresseerd in opmerkingen/toevoegingen. Andere mensen zijn er uiteraard vrij in om de onderstaande code zelf te gebruiken.
Script
De basis van mijn script is fail2ban, dat weer gebaseerd is op iptables. Fail2ban detecteerd een aantal soorten inbraakpogingen, en voegt gedurende één uur in iptables een firewall regel toe die het verkeer van een bepaalde combi van ip en netwerkprotocol blokkeerd. Via crontab voer ik elke nacht een shell script uit, dat een aantal stappen uitvoerd.
(1) Kijk of alle gebande ip's nog in iptables staan (na rebooten wordt iptables gereset).
(2) Indien ip's ontbreken, voeg ze weer toe.
(3) Controleer in /var/log/fail2ban.log of er nog niet geblokkeerde ip's zijn die minstens 2x geblokkeerd zijn door fail2ban
(4) Als dat het geval is: controleer of het ip adres uit Nederland komt (anders geen blokkade).
(5) Controleer in /var/log/auth.log of vanaf het ip ooit succesvol is ingelogd (anders geen blokkade).
(6) Zend indien mogelijk een waarschuwingsmail.
(7) Voeg het ip toe aan de lijst met permanent geblokkeerde ip's, en aan iptables.
Commentaar op het script is uiteraard zeer welkom.
Het script ziet er als volgt uit:
Zoals te zien is maakt het shell script gebruik van twee externe programma's om het land op te zoeken waar het ip vandaan komt, en om de waarschuwingsmail te versturen. Deze zal ik nu kort bespreken.
Herkomstland ip
Maxmind's Geo IP database is een gratis beschikbare database waarin per ip range het herkomstland staat. Deze database wordt regelmatig geupdate. Hoewel je deze database bijvoorbeeld in een MySQL database kunt importeren, maak ik voor dit doel gebruik van een eenvoudig programma dat ik zelf heb geschreven. Ik gebruik dit programma overigens ook voor mijn website, waar ik via PHP een Nederlandstalige welkomsttekst aanbied aan bezoekers uit Nederland, en een engelstalige tekst aan mensen uit het buitenland, en waarbij sommige functionaliteiten (zoals de zichtbaarheid van mijn email adres) alleen beschikbaar zijn voor mensen uit westerse landen. Als jullie bugs of kwetsbaarheden vinden hoor ik het graag.
De broncode is als volgt. Aangezien deze geen externe libraries nodig heeft kan er eenvoudig een executable van worden gemaakt.
Versturen waarschuwings mail
Ook het versturen van de waarschuwingsmail verloopt zoals gezegd via een simpel programmatje. Dat zit itt. het bovenstaande programma echter extreem ad-hoc inelkaar. De versie die hieronder staat is een test versie, die de waarschuwingsmail naar mijn eigen email adres stuurt. Door deze regels te vervangen door de uitgeschakelde versies die zich baseren op argv[1], argv[2] en argv[3] gaat de mail naar het juiste adres.
Ik ben nu dan ook begonnen met het maken van een script dat na een aantal hack/brute-force pogingen geheel geautomatiseerd een waarschuwing mailt naar de provider. Het bevindt zich nog in een beginfase, dus ik ben zeer geïnteresseerd in opmerkingen/toevoegingen. Andere mensen zijn er uiteraard vrij in om de onderstaande code zelf te gebruiken.
Script
De basis van mijn script is fail2ban, dat weer gebaseerd is op iptables. Fail2ban detecteerd een aantal soorten inbraakpogingen, en voegt gedurende één uur in iptables een firewall regel toe die het verkeer van een bepaalde combi van ip en netwerkprotocol blokkeerd. Via crontab voer ik elke nacht een shell script uit, dat een aantal stappen uitvoerd.
(1) Kijk of alle gebande ip's nog in iptables staan (na rebooten wordt iptables gereset).
(2) Indien ip's ontbreken, voeg ze weer toe.
(3) Controleer in /var/log/fail2ban.log of er nog niet geblokkeerde ip's zijn die minstens 2x geblokkeerd zijn door fail2ban
(4) Als dat het geval is: controleer of het ip adres uit Nederland komt (anders geen blokkade).
(5) Controleer in /var/log/auth.log of vanaf het ip ooit succesvol is ingelogd (anders geen blokkade).
(6) Zend indien mogelijk een waarschuwingsmail.
(7) Voeg het ip toe aan de lijst met permanent geblokkeerde ip's, en aan iptables.
Commentaar op het script is uiteraard zeer welkom.
Het script ziet er als volgt uit:
code:
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
| #!/bin/sh #Check whether all the IP's from ban_list.txt are still banned in iptables RESULT=`cat ban_list.txt | grep "ALL: " | cut -d " " -f2` for IP in $RESULT; do IPTABLES=`iptables -n -L | grep "DROP all -- $IP"` if [ -z "$IPTABLES" ] then iptables -A INPUT -s "$IP" -j DROP fi done #Then we check whether new IP's have to be banned RESULT=`cat /var/log/fail2ban.log* | grep "Ban" | cut -d " " -f7` for IP in $RESULT; do #First we check whether we have alreadly processed a potentially duplicate IP CHECK=`echo "$FINISHED" | grep "$IP"` if [ ! -z "$CHECK" ] then continue fi #Count how many bans a user has had, if it's more than two he gets banned if he has never logged in COUNT=`echo "$RESULT" | grep "$IP" | wc -l` if [ ! $COUNT -le 1 ]; then #Check if user is already banned BANNED=`cat ban_list.txt | grep "$IP"` if [ -z "$BANNED" ] then #Check country COUNTRY=`./country "$IP"` if [ "$COUNTRY" = "NL" ] then continue fi #Check if user has ever succesfully loged on to the system SUCCESS=`cat /var/log/auth.log | grep "Accepted password" | grep "$IP"` if [ -z "$SUCCESS" ] then #Send a warning mail HOST=`host "$IP"` RES=`echo "$HOST" | grep "not found"` if [ -z "$RES" ] then MAIL1=`echo "$HOST" | rev | cut -d "." -f3 | rev` MAIL2=`echo "$HOST" | rev | cut -d "." -f2 | rev` MAIL="abuse@$MAIL1.$MAIL2" MAILIP=`host "$MAIL1.$MAIL2" | grep "mail is handled by" | rev | cut -d " " -f1 | tail -n 1 | rev` if [ ! -z "$MAILIP" ] then MAILIP=`host "$MAILIP" | grep "has address" | rev | cut -d " " -f1 | rev` if [ ! -z "$MAILIP" ] then LOGS=`cat /var/log/auth.log* | grep "$IP"` ./abusemail "$MAILIP" "$MAIL1.$MAIL2" "$MAIL" "$IP" "$LOGS" fi fi fi # Ban the user echo "ALL: $IP" >> ban_list.txt iptables -A INPUT -s "$IP" -j DROP echo "IP $IP is banned" fi fi fi FINISHED="$FINISHED\n$IP" done exit |
Zoals te zien is maakt het shell script gebruik van twee externe programma's om het land op te zoeken waar het ip vandaan komt, en om de waarschuwingsmail te versturen. Deze zal ik nu kort bespreken.
Herkomstland ip
Maxmind's Geo IP database is een gratis beschikbare database waarin per ip range het herkomstland staat. Deze database wordt regelmatig geupdate. Hoewel je deze database bijvoorbeeld in een MySQL database kunt importeren, maak ik voor dit doel gebruik van een eenvoudig programma dat ik zelf heb geschreven. Ik gebruik dit programma overigens ook voor mijn website, waar ik via PHP een Nederlandstalige welkomsttekst aanbied aan bezoekers uit Nederland, en een engelstalige tekst aan mensen uit het buitenland, en waarbij sommige functionaliteiten (zoals de zichtbaarheid van mijn email adres) alleen beschikbaar zijn voor mensen uit westerse landen. Als jullie bugs of kwetsbaarheden vinden hoor ik het graag.
De broncode is als volgt. Aangezien deze geen externe libraries nodig heeft kan er eenvoudig een executable van worden gemaakt.
code:
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
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <arpa/inet.h> #define BUFSIZE 1024 FILE *fildes; unsigned char data[BUFSIZE+128]; int search(int pos, uint32_t addr) { int i = 0, j; uint32_t lower_addr = 0; char buf[128]; fseek(fildes, pos, SEEK_SET); fread(data, 1, BUFSIZE, fildes); // Eerste adres while (data[i++] != '\n'); i++; j = i; while (data[j++] != '"'); strncpy(buf, &data[i], j - i - 1); buf[j - i - 1] = '\0'; lower_addr = inet_addr(buf); lower_addr = htonl(lower_addr); if (lower_addr > addr) return 1; return 0; } int checkout(int pos, uint32_t addr) { int i = 0, j; uint32_t lower_addr = 0xffffffff, upper_addr = 0; char buf[128]; fseek(fildes, pos, SEEK_SET); fread(data, 1, BUFSIZE, fildes); while (i < BUFSIZE & addr > upper_addr) { // Eerste adres while (i < BUFSIZE & i < BUFSIZE & data[i++] != '\n'); i++; j = i; while (j < BUFSIZE & data[j++] != '"'); strncpy(buf, &data[i], j - i - 1); buf[j - i - 1] = '\0'; lower_addr = inet_addr(buf); lower_addr = htonl(lower_addr); while (i < BUFSIZE & data[i++] != ','); // Tweede adres while (i < BUFSIZE & data[i++] != '"'); j = i; while (j < BUFSIZE & data[j++] != '"'); strncpy(buf, &data[i], j - i - 1); buf[j - i - 1] = '\0'; upper_addr = inet_addr(buf); upper_addr = htonl(upper_addr); } while (i < BUFSIZE & data[i++] != ','); while (i < BUFSIZE & data[i++] != ','); while (i < BUFSIZE & data[i++] != ','); i++; buf[0] = data[i++]; buf[1] = data[i++]; buf[2] = '\0'; printf("%s", buf); return 0; } int main(int argc, char *argv[]) { int pos = 0, searchsize = 1000000; uint32_t addr = 0; if (argc != 2) { printf("Error"); return 0; } addr = inet_addr(argv[1]); if (addr == INADDR_NONE | addr == EAFNOSUPPORT | addr == ENOSPC) { printf("Error"); return 0; } addr = htonl(addr); fildes = fopen("GeoIPCountryWhois.csv", "r"); if(!fildes) { printf("Error"); return 0; } fseek(fildes, 0L, SEEK_END); int len = ftell(fildes); while (pos < len) { if (search(pos, addr) == 1) break; pos += searchsize; } pos -= searchsize; searchsize /= 10; pos += searchsize; while (pos < len) { if (search(pos, addr) == 1) break; pos += searchsize; } pos -= searchsize; searchsize /= 10; pos += searchsize; while (pos < len) { if (search(pos, addr) == 1) break; pos += searchsize; } pos -= searchsize; searchsize /= 10; pos += searchsize; while (pos < len) { if (search(pos, addr) == 1) break; pos += searchsize; } pos -= searchsize; checkout(pos, addr); fclose(fildes); return 0; } |
Versturen waarschuwings mail
Ook het versturen van de waarschuwingsmail verloopt zoals gezegd via een simpel programmatje. Dat zit itt. het bovenstaande programma echter extreem ad-hoc inelkaar. De versie die hieronder staat is een test versie, die de waarschuwingsmail naar mijn eigen email adres stuurt. Door deze regels te vervangen door de uitgeschakelde versies die zich baseren op argv[1], argv[2] en argv[3] gaat de mail naar het juiste adres.
code:
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
| #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #define PORT_NR 25 #define ERROR -1 #define NOT_FOUND -1 int sockfd; int main(int argc, char *argv[]) { if (argc < 6) return -1; int sockfd, len, result; struct sockaddr_in address; time_t t; struct tm *tstruct; // Create a socket for the client sockfd = socket(AF_INET, SOCK_STREAM, 0); // Set a timeout interval struct timeval tv; tv.tv_sec = 30; /* 30 Secs Timeout */ setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,(struct timeval *)&tv,sizeof(struct timeval)); // Name the socket address.sin_family = AF_INET; // address.sin_addr.s_addr = inet_addr(argv[1]); address.sin_addr.s_addr = inet_addr("192.168.1.11"); address.sin_port = htons(PORT_NR); len = sizeof(address); if (strlen(argv[4]) > 3000) return -1; // Connect your socket to the server’s socket result = connect(sockfd, (struct sockaddr *)&address, len); if(result == -1) { printf("Could not connect!\n"); return 0; } char *helo, *rcptto, *message, *from; helo = malloc(32 + strlen(argv[2])); rcptto = malloc(32 + strlen(argv[3])); from = malloc(1024); message = malloc(4096); // sprintf(helo, "HELO %s\n", argv[2]); sprintf(helo, "HELO %s\n", "eigen_domein.net"); // sprintf(rcptto, "RCPT TO: %s\n", argv[3]); sprintf(rcptto, "RCPT TO: %s\n", "mail@eigen_domein.net"); sprintf(from, "From: noreply@noreply.net\nSubject: Abuse from ip %s\n\n", argv[4]); sprintf(message, "THIS MESSAGE WAS AUTOMATICALLY GENERATED\n\nDear Mr./Mrs.,\n\nI have noticed several attacks on my system, originating from the following ip-address:\n\n%s\n\nThis ip-address seems to be owned by your company. Relevant log entries have been included at the bottom of this mail.\n\nI would like to kindly ask you to undertake the necessary steps to stop this.\n\nThank you in advance.\n\nBest regards,\nRaspberry Pi\n\n%s\n.\n", argv[4], argv[5]); send(sockfd, helo, strlen(helo),0); send(sockfd,"MAIL FROM: noreply@noreply.net\n",strlen("MAIL FROM: noreply@noreply.net\n"),0); send(sockfd, rcptto,strlen(rcptto),0); send(sockfd,"DATA\n",strlen("DATA\n"),0); send(sockfd, from,strlen(from),0); send(sockfd, message,strlen(message),0); close(sockfd); return 0; } |