#!/usr/local/bin/perl # THIS IS ACTUALLY A C SOURCE FILE, PERL WILL BUILD IT FOR YOU... # # Just drop this file in with the snort sources after successfully building # snort. Execute this file and snortmail will be created for you. # # You might want to read the C sources below to learn how to set up snort # to send the alerts to snortmail. (Start reading after __DATA__ below) # BEGIN { open(SNORT, "<", "./snort.c") or die "You must build this in the same directory as the snort sources, exiting...\n"; print "\nMaking tmp file.\n"; open TMP, ">", "snortmail__TMP.c"; open ME, "<", $0; while() { $i? print TMP: /^__DATA__$/?$i++: next; } $i = 0; close ME; print "Stripping main() off of snort.c..."; while() { $i? print TMP: /void ProcessPacket/?$i++||print TMP: next; } print "done\n"; close TMP; close SNORT; print "Compiling a list of snort object files..."; chomp(@olibs = `ls *.o | grep -v snort`); $cc = "cc -s -o snortmail snortmail__TMP.c"; print "done\n"; print "Reading Makefile to determine the libraries that snort was linked with..."; $lnk = `grep \"LIBS =\" Makefile | sed -e \'s/LIBS =//g\'`; print "done\n"; print "Building snortmail:\n"; print "$cc @olibs $lnk\n"; system("$cc @olibs $lnk"); unlink("snortmail__TMP.c"); print "Verifying successful build...\n\n"; $success = `ls -l snortmail`; if($success =~ m/snortmail$/) { print "Here you go:\n$success\n"; } else { print "Hmmm, we hit a snag...read the sources and email me if you can't figure it out.\n", "Ian - roux\@speakeasy.org\n" } exit(0); } __DATA__ /* Snort Email Alerts - Snortmail */ #include "snort.h" /* we use snort to parse snort alerts */ /* This would really make more sense as an output plugin, but I guess if * anyone wanted that it would already be done. Done this way, it can't * slow snort down. * * Snortmail uses the unix domain socket alerting facility of snort. It also * uses snort's own files so build snort first, then place this file in the * snort directory. Build instructions are below. * * The mail is sent using MAILCMD, which must be a program which will accept * standard input via popen. /bin/mail is the default. You can choose full or * fast alerts below. Currently there is no way to provide classification or * priority data because that data is not currently passed through the socket * interface. You can get X-Refs by using the "-G" flag when you start snort. * * There is time-based rate limiting implemented as a timer which gets set * when the first alert is queued. New alerts after that will be queued until * the timer expires, at which time the alerts are flushed to mail. The next * alert will start the queue timer again. * * The queue will also flush mail when the queue capacity is reached if that * happens before the timer expires. * * See MAX_Q and Q_TIME below. * * Despite the rate limiting, you can whack your mail system if you get a * +LOT+ of alerts. -ian roux@speakeasy.org * Add "output alert_unixsock" to snort.conf and restart snort. * Edit defines to suit your wishes: */ #define UMASK 077 /* umask applied to socket creation */ #define Q_TIME 120 /* max seconds before running queue */ #define MAX_Q 20 /* max alerts before running queue */ #define VERBOSE /* remove this to get fast style alerts */ /* You can set this to any command which will accept stdin */ #define MAILCMD "/bin/mail -s \"Snort Alert\" ian" /*#define DUMP_APPLAYER */ /* decoded ascii packet dump - not advised */ /*#define USE_UTC*/ /* UTC timestamps */ /* Build the program in the snort directory (after a successful snort build). * You might need to use -DWORDS_BIGENDIAN on some architectures. ------------------------------------------------------------------------------- 1) strip main() off of snort.c and steal the rest. perl -ne'$i?print:/void ProcessPacket/?$i++||print:next;' snort.c >>snortmail.c 2) compile the program using the snort object files. ...this is all one long line below: cc -s -o snortmail snortmail.c \ `ls *.o | grep -v snort` \ `grep "LIBS =" Makefile | sed -e 's/LIBS =//g'` 4) start the program ./snortmail ------------------------------------------------------------------------------*/ typedef struct _Queue { int qsize; int flush; Alertpkt *q[MAX_Q]; } Queue; static Queue *queue; static void q_handler (int sig); /* SIGALRM handler */ static void mail (void); /* flush mail, reset queue */ static void crash (char *error); /* output perror and exit() */ int main (void) { int sockfd, read; mode_t mode; pid_t pid; struct sigaction sa; struct sockaddr_un usock; Alertpkt *alertpkt; if ((sockfd = socket (AF_UNIX, SOCK_DGRAM, 0)) == -1) crash("socket"); usock.sun_family = AF_UNIX; strcpy (usock.sun_path, UNSOCK_FILE); /* "we trust what we define" */ if (unlink (usock.sun_path) == -1) { if (errno == EACCES || errno == EPERM) /* perms */ crash("unlinking old socket file"); } /* write new socket */ mode = umask (UMASK); if (bind (sockfd, (struct sockaddr *) &usock, (sizeof (usock.sun_family) + strlen (usock.sun_path))) == -1) crash("binding new socket file"); umask (mode); /* detach */ if (pid = fork ()) { printf ("snortmail [%d]: reading /dev/snort_alert, queue timer: %d sec, capacity: %d\n", pid, Q_TIME, MAX_Q); exit (0); } setsid (); /* set up signal handler to catch queue timer: alarm(Q_TIME) */ sa.sa_handler = q_handler; sigemptyset (&sa.sa_mask); sigaddset (&sa.sa_mask, SIGALRM); sa.sa_flags = 0; sigaction (SIGALRM, &sa, 0); /* set up the read buffer and alert queue */ alertpkt = malloc (sizeof (Alertpkt)); queue = malloc (sizeof (Queue)); if (alertpkt == NULL || queue == NULL) crash("malloc"); memset (alertpkt, 0, sizeof (Alertpkt)); memset (queue, 0, sizeof (Queue)); #ifdef USE_UTC /* ripped from snort, from tcpdump */ thiszone = 0; #else thiszone = gmt2local(0); #endif /* USE_UTC */ pv.quiet_flag = 0; pv.verbose_bytedump_flag = 0; /* PrintNetData wants 1 ? */ pv.char_data_flag = 1; /* I don't think we really need the PV's here */ pv.verbose_flag = 1; pv.data_flag = 1; InitProtoNames (); /* we should be all set to parse alerts now */ for (;;) { read = recv (sockfd, alertpkt, sizeof (Alertpkt), 0); sigprocmask (SIG_BLOCK, &sa.sa_mask, 0); /* hold alarms */ if (read > 0) /* skip queue if the alarm() interrupted recv() */ { /* otherwise queue the alert */ if ((queue->q[queue->qsize] = malloc (sizeof (Alertpkt))) == NULL) crash("malloc for queue"); memcpy (queue->q[queue->qsize++], alertpkt, sizeof (Alertpkt)); if (queue->qsize == 1) alarm (Q_TIME); /* set the timer, first alert in queue */ if (queue->qsize == MAX_Q) queue->flush = 1; /* flush to mail if capacity is reached */ } if (queue->flush == 1) /* MAX_Q or timer expired (q_handler())*/ mail (); read = 0; sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0); } } static void q_handler (int sig) { if (queue->qsize > 0) queue->flush = 1; } static void mail (void) { int i; Packet *p; FILE *mail; if ((p = malloc (sizeof (Packet))) == NULL) crash("malloc"); alarm (0); /* cancel alarm if pending */ if ((mail = popen (MAILCMD, "w")) == NULL) crash("popen mail"); for (i = 0; i < queue->qsize; i++) { if (queue->q[i]->val & NOPACKET_STRUCT) /* nothing to decode. */ fprintf (mail, "%s\n", queue->q[i]->alertmsg); else { #ifdef DUMP_APPLAYER if (i == 0) fprintf (mail, "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\n"); #endif /* DUMP_APPLAYER */ /* use snort's decoder */ memset(p, 0, sizeof(Packet)); p->pkth = &queue->q[i]->pkth; /* pcap header */ p->pkt = queue->q[i]->pkt; /* the actual packet */ DecodeIP (queue->q[i]->pkt + queue->q[i]->nethdr, queue->q[i]->pkth.caplen, p); #ifdef VERBOSE AlertFull (p, queue->q[i]->alertmsg, mail); #else AlertFast (p, queue->q[i]->alertmsg, mail); #endif /* VERBOSE */ #ifdef DUMP_APPLAYER /* dump ASCII packet data */ if (p->dsize) PrintCharData (mail, p->data, p->dsize); /* PrintNetData(mail, (char *) (p->iph + (p->iph->ip_hlen << 2)), (p->iph->ip_len - (p->iph->ip_hlen << 2)));*/ fprintf (mail, "\n=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\n"); #endif /* DUMP_APPLAYER */ } } pclose (mail); /* clear out the queue */ if((queue = realloc(queue, sizeof(Queue))) == NULL) crash("realloc in mail()"); memset(queue, 0, sizeof(Queue)); free (p); } static void crash (char *error) { perror(error); exit(1); }