/* * Scandetd is daemon which tries to recognize port scanning. * If it happens daemon sends e-mail to root@localhost (by default) * with following informations: * * time * host * how many connetctions was made * port of first connection and port of last connection * * compile: gcc scandetd.c -o scandetd * * author: Michal Suszycki mike@wizard.ae.krakow.pl * * You can change few define's and variables below this comment to tune * scandetd to your needs. * * If you have some problems with compiling try to * change 2 lines: * #include to #include * #include to #include * * This code was based on IpLogger Package by Mike Edulla (medulla@infosoc.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include //#include #include //#include #include #include #include #include extern int errno; /* how many hosts should I remember. If your server is heavily loaded it's good idea to increase this number a little bit */ #define HOW_MANY 6 /* how many connections should I recognize as scanning? */ #define SCAN 25 /* uncomment this to enable printing to log files scan warnings (using syslogd) */ //#define DOSYSLOG /* uncoment this if you want to log every connection attempt (using syslogd) */ //#defind LOGCON /* here you can define special port you want to ignore: if 'scanning' started and ended on the same port and this port is equal to NOPORT then 'scanning' will be ignored. If you notice for example that your server get a lot of fast connections (from one host) to www port you can define NOPORT to 80 so there will be no false warnings */ #define NOPORT 80 /* If next connection arrived right after the previous one we have to count it. Default time is 1 second. */ #define SEC 1 /* We use this port for sending mail */ #define MAILPORT 25 /* we send mail to : */ char *mail_to = ""; /* IP of the machine which sends our mail */ char *mail_host = "127.0.0.1"; /* mail will be send from host: */ char *from_host = "localhost"; /* ----------- end of user's configuration ---------------- */ #ifndef NOFILE #define NOFILE 1024 #endif char *hostlookup(int i) { static char buff[256]; struct in_addr ia; struct hostent *he; ia.s_addr = i; if (!(he = gethostbyaddr((char *)&ia, sizeof ia, AF_INET))) strncpy(buff,inet_ntoa(ia),sizeof buff); else strncpy(buff,he->h_name,sizeof buff); return buff; } char *servlookup(unsigned short port) { struct servent *se; static char buff[256]; se=getservbyport(port, "tcp"); if(se == NULL) sprintf(buff, "port %d", ntohs(port)); else sprintf(buff, "%s", se->s_name); return buff; } struct ippkt{ struct iphdr ip; struct tcphdr tcp; } pkt; struct host{ unsigned int from; time_t t; time_t start; unsigned short low_port; unsigned short hi_port; int count; } hosts[HOW_MANY]; void demonize() { int fd, f; if (getppid() != 1){ signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); signal(SIGTSTP,SIG_IGN); f = fork(); if (f < 0) exit(-1); if (f > 0) exit (0); /* child process */ setpgrp(); for (fd = 0 ; fd < NOFILE; fd++) close(fd); chdir("/"); umask(0); return; } } void init() { int i; time_t now; now = time(NULL); for (i = 0; i < HOW_MANY; i++) hosts[i].t = now; } int allocate(int *p, unsigned int addr) { int i, v = 0; time_t tmp = hosts[0].t; for( i = 0; i < HOW_MANY; i++){ if (hosts[i].t <= tmp) { tmp = hosts[i].t; v = i; } if (hosts[i].from == addr){ *p = 1; return i; } } *p = 0; return v; } // only for debugging void show(int a) { int i; for (i = 0; i < HOW_MANY; i++){ printf("Host %s, time %ld, count=%d, l=%d,", hostlookup(hosts[i].from),hosts[i].t, hosts[i].count, ntohs(hosts[i].low_port)); printf("hi = %d\n",ntohs(hosts[i].hi_port)); } exit (0); } void no_zombie(int i) { wait(NULL); } int send_mail(struct host *bad) { static struct sockaddr_in sa; int s, i, low, high; char buf[1024], combuf[256]; char *comm[] = { "HELO ", from_host, "MAIL FROM: SCANDETD@", from_host, "RCPT TO:" , mail_to, "DATA" , " " }; i = fork(); if (i < 0) return -1; if (!i) return 0; low = ntohs(bad->low_port); high = ntohs(bad->hi_port); sprintf(buf,"%sPossible port scanning from %s,\n" "I counted %d connections.\nFirst connection was made on %d port and the last one on %d port.\r\n.\r\n", ctime(&bad->start),hostlookup(bad->from),bad->count, low, high); sa.sin_port = htons(MAILPORT); sa.sin_family = AF_INET; if ((sa.sin_addr.s_addr = inet_addr(mail_host)) == -1) exit (-1); bzero(&sa.sin_zero, 8); if ((s = socket(AF_INET,SOCK_STREAM,0)) < 0) exit (-1); if (connect(s,(struct sockaddr *) &sa, sizeof (struct sockaddr)) < 0) exit (-1); for (i = 0; i < 8 ; i += 2){ sprintf(combuf,"%s%s\n",comm[i],comm[i+1]); if (write(s,combuf,strlen(combuf)) < 0 ){ close(s); exit(-1); } sleep(1); } if (write(s,buf,strlen(buf)) < 0) exit(-1); sleep(1); if (write(s,"QUIT\n",5) < 0) exit (-1); close(s); exit(0); } void main(int argc, char **argv) { int s, index, was; time_t now; demonize(); init(); s = socket(AF_INET, SOCK_RAW, 6); #ifdef DOSYSLOG openlog("scandetd", 0, LOG_LOCAL2); syslog(LOG_NOTICE,"scandetd started and ready"); #endif // signal(SIGINT,show); /* to avoid zombies */ signal(SIGCHLD,no_zombie); while(1){ read(s, (struct ippkt*) &pkt, sizeof(pkt)); now = time(NULL); if (pkt.tcp.syn == 1 && pkt.tcp.ack == 0){ #ifdef LOGCON syslog(LOG_NOTICE,"%s connecion attempt from %s", servlookup(pkt.tcp.dest), hostlookup(pkt.ip.saddr) ); #endif index = allocate(&was,pkt.ip.saddr); if (!was){ if (hosts[index].count >= SCAN #ifdef NOPORT && hosts[index].low_port != htons(NOPORT) && hosts[index].hi_port != htons(NOPORT) #endif ){ send_mail(&hosts[index]); #ifdef DOSYSLOG syslog(LOG_NOTICE,"Possible port scanning from %s", hostlookup(hosts[index].from)); #endif } hosts[index].from = pkt.ip.saddr; hosts[index].low_port = pkt.tcp.dest; hosts[index].hi_port = pkt.tcp.dest; hosts[index].count = 1; hosts[index].t = now; hosts[index].start = now; continue; } /* if this connection was right after previous we must count it */ else if (now - SEC <= hosts[index].t){ hosts[index].count++; hosts[index].hi_port = pkt.tcp.dest; } hosts[index].t = now; } } }