Scandetd is a port scan detection daemon that waits for incoming tcp connections and tries to recognize port scans. If tripped, scandetd sends email to root@127.0.0.1 with the time, attacking host, number of connections made, port of the first and last connections. Easy on system resources; for Linux; initial release. 6k.
305219b79f012a5152846430f4c566386f23c156aa8d040e810ccddc4f3c7a6d
/*
* 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 <netinet/ip.h> to #include <linux/ip.h>
* #include <netinet/tcp.h> to #include <linux/tcp.h>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
//#include <netinet/ip.h>
#include <linux/ip.h>
//#include <netinet/tcp.h>
#include <linux/tcp.h>
#include <time.h>
#include <signal.h>
#include <string.h>
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 <user@host>: */
char *mail_to = "<root@localhost>";
/* 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;
}
}
}