Snort mailing list archives
[Snort-devel] Call for Bugs -> icmpscaner
From: Serge Droz <serge.droz () psi ch>
Date: Fri, 06 Jul 2001 16:53:04 +0200
I've appended out diff implementing out ICMP scan detector. It has been working here quite well for a while now. Maybe people find it usefull Serge -- Serge Droz Paul Scherrer Institut mailto:serge.droz () psi ch CH-5232 Villigen PSI Phone: ++41 56 310 3637 Fax: ++41 56 310 3649
--- snort.orig/snort.conf Thu Jul 5 07:17:17 2001
+++ snort/snort.conf Fri Jul 6 16:42:01 2001
@@ -228,6 +228,21 @@
#
#preprocessor portscan-ignorehosts: $DNS_SERVERS
+# icmpscan: detect icmp (ping scans)
+# __________________________________
+#
+# Format
+# preprocessor icmpscan: n t1 t2 log
+# if there are more than n icmp packets from teh same source in t1 seconds
+# the scan starts. It ends after there has been no icmp traffic for t2 seconds.
+#
+#
+
+#var ICMP_SCAN_IGNORE [129.129.110.10/32]
+#preprocessor ignore-icmpscan: $ICMP_SCAN_IGNORE
+#preprocessor icmpscan: 12 15 15 icmpscan.log
+
+
# Spade: the Statistical Packet Anomaly Detection Engine
#-------------------------------------------------------
# READ the README.Spade file before using this plugin!
--- snort.orig/spp_icmpscan.c Fri Jul 6 16:36:58 2001
+++ snort/spp_icmpscan.c Fri Jul 6 16:22:14 2001
@@ -0,0 +1,535 @@
+/*
+** Copyright (C) 2001 Serge Droz <serge.droz () psi ch>
+**
+** 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 2 of the License, 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.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*//* $Id$ */
+/* Snort IcmpScan preprocessor Plugin
+ by Serge Droz <serge.droz () psi ch>
+ Version 0.1*/
+
+/* spp_icmpscan
+ *
+ * Purpose: Detect and report icmp (ping) scans
+ *
+ *
+ * Arguments: numhosts timeinterval timeout [icmplogFile]
+ *
+ *
+ * A scans starts when there are icmp packets to more than numhosts hosts in timeinterval
+ * seconds. It stops if no packets have been seen in timeout seconds.
+ *
+ * Effect:
+ *
+ * Issue an alert if an ICMP Scan is detected. Log the targets into an optional log file.
+ *
+ */
+
+/* preprocessor header file goes here */
+#include "spp_icmpscan.h"
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "rules.h"
+#include "log.h"
+
+#define pTime(p) (p->pkth->ts.tv_sec)
+#define MaxIgnore 32 /* Maximum number of hosts/subnets which can be ignored */
+
+/* external globals from rules.c */
+
+
+extern char *file_name;
+extern int file_line;
+extern u_int32_t event_id;
+
+typedef struct _target {
+ long int time;
+ struct in_addr myaddr;
+ u_int8_t type;
+ u_int8_t code;
+ u_int8_t ttl;
+} target;
+
+/* Things we need to know about a scaner */
+
+typedef struct _scaner {
+ struct in_addr source;
+ struct _scaner *next,*prev;
+ u_int32_t event_id;
+ long int start;
+ long int last;
+ int top;
+ unsigned long count;
+ int scan_detected;
+ target targets[0];
+} scaner;
+
+/* Hosts and subnets we should ignore */
+typedef struct _ignorius {
+ unsigned long network;
+ unsigned long netmask;
+} ignorius;
+
+/* Some Prototypes */
+int IgnorePacket(Packet *p);
+
+
+/* Globals */
+static int threshTargets;
+static int threshSecs;
+static int finSecs;
+static FILE *icmplogFile = NULL;
+static scaner *FirstScaner;
+static ignorius *Ignore;
+static int NumIgnore = 0;
+
+/* Some stuff to do diagnostics. This is not needed for normal operation */
+void print_all_sources () {
+ scaner *s = FirstScaner;
+ printf("------------------------\n");
+ while ( s ) {
+ printf("Scanner: %s\n",inet_ntoa(s->source));
+ s = s->next;
+ }
+ printf("------------------------\n");
+}
+
+void print_all_targets (scaner *s) {
+int i;
+ printf("------ Targets -------\n");
+ printf("Scanner: %s\n",inet_ntoa(s->source));
+ for (i=0; i < s->top; i++) printf("target: %s\n",inet_ntoa(s->targets[i].myaddr));
+ printf("------------------------\n");
+}
+
+/*
+ * Function: SetupIcmpScan()
+ *
+ * Purpose: Registers the preprocessor keyword and initialization
+ * function into the preprocessor list. This is the function that
+ * gets called from InitPreprocessors() in plugbase.c.
+ *
+ * Arguments: None.
+ *
+ * Returns: void function
+ *
+ */
+void SetupIcmpScan()
+{
+ /* link the preprocessor keyword to the init function in
+ the preproc list */
+ RegisterPreprocessor("icmpscan", IcmpScanInit);
+
+#ifdef DEBUG
+ printf("Preprocessor: IcmpScan is setup...\n");
+#endif
+}
+
+
+/*
+ * Function: IcmpScanInit(u_char *)
+ *
+ * Purpose: Calls the argument parsing function, performs final setup on data
+ * structs, links the preproc function into the function list.
+ *
+ * Arguments: args => ptr to argument string
+ *
+ * Returns: void function
+ *
+ */
+void IcmpScanInit(u_char *args)
+{
+#ifdef DEBUG
+ printf("Preprocessor: IcmpScan Initialized\n");
+#endif
+
+ /* parse the argument list from the rules file */
+ ParseIcmpScanArgs(args);
+
+ /* Set the preprocessor function into the function list */
+ AddFuncToPreprocList(PreprocIcmpScanFunction);
+ AddFuncToCleanExitList(ExitIcmpScanFunction,NULL);
+}
+
+
+
+/*
+ * Function: ParseIcmpScanArgs(char *)
+ *
+ * Purpose: Process the preprocessor arguements from the rules file and
+ * initialize the preprocessor's data struct. This function doesn't
+ * have to exist if it makes sense to parse the args in the init
+ * function.
+ *
+ * Arguments: args => argument list
+ *
+ * Returns: void function
+ *
+ * Remark: This is shamelessly stoplen from spp_portscan.c
+ */
+void ParseIcmpScanArgs(char *args)
+{
+ char **toks;
+ int numToks;
+
+ char *icmplogFileName;
+
+
+
+ if (!args) FatalError("icmpscan" ": ERROR: %s (%d) => icmpscan configuration format: hosts seconds
seconds[icmplogFile]\n", file_name, file_line);;
+ toks = mSplit(args, " ", 4, &numToks, '\\');
+
+ if((numToks < 3) || (numToks > 4)) FatalError("icmpscan" ": ERROR: %s (%d) => icmpscan configuration format: hosts
seconds seconds[icmplogFile]\n", file_name, file_line);;
+ threshTargets = atoi(toks[0]);
+ threshSecs = atoi(toks[1]);
+ finSecs = atoi(toks[2]);
+ FirstScaner = NULL;
+ if(numToks == 4)
+ {
+ if(pv.log_dir && (*toks[3] != '/'))
+ {
+ if(*(pv.log_dir + strlen(pv.log_dir) - 1) != '/')
+ {
+ icmplogFileName = chrootdir == NULL ?
+ (char *) calloc(strlen(pv.log_dir) + strlen(toks[3]) + 1 + 1, 1) :
+ (char *) calloc(strlen(pv.log_dir) + strlen(toks[3]) + strlen(chrootdir) + 1 + 1, 1);
+
+ if(chrootdir)
+ strncpy(icmplogFileName, chrootdir, strlen(chrootdir) + 1);
+ strncat(icmplogFileName, pv.log_dir, strlen(pv.log_dir) + 1);
+ strncat(icmplogFileName, "/", 1);
+ strncat(icmplogFileName, toks[3], strlen(toks[3]));
+ }
+ else
+ {
+ icmplogFileName = chrootdir == NULL ?
+ (char *) calloc(strlen(pv.log_dir) + strlen(toks[3]) + 1, 1) :
+ (char *) calloc(strlen(pv.log_dir) + strlen(toks[3]) + strlen(chrootdir) + 1, 1);
+ if(chrootdir)
+ strncpy(icmplogFileName, chrootdir, strlen(chrootdir) + 1);
+ strncat(icmplogFileName, pv.log_dir, strlen(pv.log_dir) + 1);
+ strncat(icmplogFileName, toks[3], strlen(toks[3]));
+ }
+ }
+ else
+ {
+ icmplogFileName = chrootdir == NULL ?
+ (char *) calloc(strlen(toks[3]) + 1, 1) :
+ (char *) calloc(strlen(toks[3]) + strlen(chrootdir) + 1, 1);
+ if(chrootdir)
+ strncpy(icmplogFileName, chrootdir, strlen(chrootdir) + 1);
+ strncat(icmplogFileName, toks[3], strlen(toks[3]) + 1);
+ }
+
+#ifdef DEBUG
+ printf("icmpscan" ": icmplogFileName = %s\n", icmplogFileName);
+#endif
+
+ icmplogFile = fopen(icmplogFileName, "a+");
+ if(!icmplogFile)
+ {
+ perror("fopen");
+ FatalError("icmpscan" ": icmplogFile open error (%s)\n", icmplogFileName);
+ }
+
+ }
+ }
+/*
+ * Function RemoveScaner(scaner **)
+ *
+ * Purpose: Removes a system from the list of scanners
+ *
+ * Arguments: s pointer to the addres of the scaner struct
+ *
+ * Returns: void function
+ *
+ */
+void RemoveScaner(scaner **s) {
+ scaner *temp;
+ char AlertLine[256];
+ Event event;
+
+ temp = *s;
+
+ if ( (temp)->prev ) (temp)->prev->next = (temp)->next; else FirstScaner = (temp)->next;
+ if ( (temp)->next ) (temp)->next->prev = (temp)->prev;
+ *s = temp->next;
+ /* Log this */
+ if ( temp->scan_detected ) {
+ sprintf(AlertLine,"spp_icmpscan: End of icmpscan from %s %ld packets
recieved",inet_ntoa(temp->source),temp->count);
+ SetEvent(&event, GENERATOR_SPP_ICMPSCAN, ICMPSCAN_SCAN_END, 1, 0, 0,temp->event_id);
+ CallAlertFuncs(NULL,AlertLine,NULL,&event);
+ }
+ free(temp);
+#ifdef DEBUG
+ printf("Remove: %s\n",inet_ntoa(temp->source));
+#endif
+}
+
+/*
+ * Function NewScaner(Packet *)
+ *
+ * Purpose: Inserts a new source into the list of potential
+ * icmp scaners.
+ *
+ * Arguments: p => pointer to the current packet data struct
+ *
+ * Returns: void function
+ *
+ */
+ void NewScaner(Packet *p) {
+ scaner *news;
+ news = (scaner *)malloc(sizeof(scaner) + (threshTargets+1)*sizeof(target));
+ news->last = news->start = pTime(p);
+ news->source.s_addr = p->iph->ip_src.s_addr;
+ news->count = 1;
+ memset(news->targets+1,0,threshTargets*sizeof(target));
+ news->targets[0].time = news->start;
+ news->targets[0].myaddr = p->iph->ip_dst;
+ news->targets[0].ttl = p->iph->ip_ttl;
+ news->targets[0].type = p->icmph->type;
+ news->targets[0].code = p->icmph->code;
+ news->top=1;
+ news->scan_detected=0;
+ if ( FirstScaner ) {
+ FirstScaner->prev = news;
+ }
+ news->next = FirstScaner;
+ news->prev = NULL;
+ FirstScaner = news;
+#ifdef DEBUG
+ printf("New src: %s\n",inet_ntoa(p->iph->ip_src));
+#endif
+ }
+
+/*
+ * Function LogTarget(char *src, target *t)
+ *
+ * Purpose: Log a target into the icmplogFile
+ *
+ * Arguments: src => string containing the source, t => pointer to the target
+ *
+ * Returns: void function
+ *
+ */
+
+void LogTarget(char *src, target *t) {
+ struct tm *time;
+ char month[][4] = { "Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
+ time = localtime((time_t *) & t->time);
+
+ fprintf(icmplogFile,"%s %2d %.2d:%.2d:%.2d %s -> %s Type: %2d Code: %2d TTL: %3d\n",
+ month[time->tm_mon ], time->tm_mday,time->tm_hour, time->tm_min, time->tm_sec,
+ src,inet_ntoa(t->myaddr),t->type, t->code,t->ttl);
+}
+/*
+ * Function UpdateScaner(scaner *, Packet *)
+ *
+ * Purpose: Updates the info on a given scaner.
+ *
+ * Arguments: s => poninter to a scaner, p => pointer to the current packet data struct
+ *
+ * Returns: void function
+ * This function updates the list of recent targets.
+ * It manipulates a cronologically orderd stack of targets. If we ht the top,
+ then we deal with a scan and report.
+ *
+ */
+void UpdateScaner(scaner *s, Packet *p) {
+ char srcn[17];
+ char AlertLine[256];
+ Event event;
+ int i,j=-1;
+ /* Insert the new entry on the top */
+#ifdef DEBUG
+ print_all_targets(s);
+#endif
+ /* First remove the entries which are too old to be kept */
+ for (i=0; s->targets[i].time < pTime(p) - threshSecs && i < s->top; i++) {}
+ /* Now either update the new target (it it exists) or append on the top */
+ for (j=i ; j < s->top; j++ ) if ( s->targets[j].myaddr.s_addr == p->iph->ip_dst.s_addr ) break;
+ /* We're above the limit, so we have to throw an 'active' target out */
+ if (j == threshTargets ) {
+ s->top++;
+ if (! i ) i=1;
+ }
+ /* Now move the stuff down the stack, kicking the 'expired' stuff out */
+ memmove(s->targets,s->targets+i,(j-i)*sizeof(target));
+ if (j != s->top) memmove(s->targets+j-i,s->targets+j+1,(s->top-j-1)*sizeof(target));
+ else s->top++;
+ /* And finally add the new target, which always goes to the top */
+ s->top-=i;
+ s->targets[s->top-1].time = pTime(p);
+ s->last = pTime(p) ;
+ s->targets[s->top-1].myaddr = p->iph->ip_dst;
+ s->targets[s->top-1].ttl = p->iph->ip_ttl;
+ s->targets[s->top-1].type = p->icmph->type;
+ s->targets[s->top-1].code = p->icmph->code;
+ s->count++;
+
+ /*Logit*/
+
+ strcpy(srcn,inet_ntoa(p->iph->ip_src));
+ if ( s->scan_detected ) {
+ if ( icmplogFile ) LogTarget(srcn,&s->targets[s->top-1]);
+ if ( s->count % 100 == 0 ) {
+ sprintf(AlertLine,"spp_icmpscan: ICMPSCAN status from %s: %d packets",inet_ntoa(p->iph->ip_src),s->count);
+ SetEvent(&event, GENERATOR_SPP_ICMPSCAN, ICMPSCAN_INTER_INFO , 1, 0, 0,s->event_id);
+ CallAlertFuncs(NULL,AlertLine,NULL,&event);
+ }
+ } else if ( s->top == threshTargets ) {
+ s->scan_detected = 1 ;
+ sprintf(AlertLine,"spp_icmpscan: ICMPSCAN DETECTED from %s",inet_ntoa(p->iph->ip_src));
+ SetEvent(&event, GENERATOR_SPP_ICMPSCAN, ICMPSCAN_SCAN_DETECT, 1, 0, 0,0);
+ CallAlertFuncs(NULL,AlertLine,NULL,&event);
+ s->event_id = event_id;
+ if ( icmplogFile ) for (i=0; i< s->top; i++ ) LogTarget(srcn,&s->targets[i]);
+ }
+}
+
+/*
+ * Function: PreprocFunction(Packet *)
+ *
+ * Purpose: This is the mainloop. First check if it's an icmp packet,
+ * the if we ignore it, and if not, do the fun stuff.
+ *
+ * Arguments: p => pointer to the current packet data struct
+ *
+ * Returns: void function
+ *
+ */
+void PreprocIcmpScanFunction(Packet *p) {
+
+ scaner *currentScaner;
+ int Exists = 0;
+ /* We only do IP */
+ if ( p->iph == NULL ) {
+ return;
+ }
+
+
+ /* In fact, we only do ICMP */
+
+ if ( p->icmph == NULL ) {
+ return ;
+ }
+ if ( IgnorePacket( p ) ){
+ return ;
+ }
+ currentScaner = FirstScaner;
+
+ /* Now we loop over all scaners.
+ If we find the curent source we update.
+ Then we check if there are expired sources and delete them.
+ Finally if the scanner wasn't in the list we insert it. */
+
+ while ( currentScaner ) {
+#ifdef DEBUG
+ print_all_sources();
+#endif
+ if ( currentScaner->source.s_addr == p->iph->ip_src.s_addr ) { /* Found em */
+ UpdateScaner(currentScaner, p);
+ Exists = 1;
+ currentScaner = currentScaner->next;
+ } else if (pTime(p) > currentScaner->last + finSecs ) {
+ RemoveScaner(¤tScaner);
+ } else {
+ currentScaner = currentScaner->next;
+ }
+ }
+ if ( ! Exists ) NewScaner(p);
+}
+
+void ExitIcmpScanFunction(int signal,void *arg)
+{
+ /* clean exit code goes here */
+ scaner *s = FirstScaner;
+
+ if ( icmplogFile ) fclose(icmplogFile);
+
+ while ( s ) RemoveScaner(&s);
+
+}
+
+void RestartIcmpScanFunction(int signal)
+{
+ /* restart code goes here */
+}
+
+/*****************************************************************
+ Here the Ignore hosts stuff follows !
+ *****************************************************************/
+void SetupIgnoreIcmpScan()
+{
+ /* link the preprocessor keyword to the init function in
+ the preproc list */
+ RegisterPreprocessor("ignore-icmpscan", IgnoreIcmpScanInit);
+
+#ifdef DEBUG
+ printf("Preprocessor: IcmpScan is setup...\n");
+#endif
+}
+
+
+void IgnoreIcmpScanInit(u_char *args)
+{
+ int i;
+ char *myargs,*t;
+ char **toks;
+ unsigned long a,b,c,d,e;
+#ifdef DEBUG
+ printf("Preprocessor: IgnoreIcmpScan Initialized\n");
+#endif
+ Ignore = (ignorius *)malloc(MaxIgnore*sizeof(ignorius));
+ /* parse the argument list from the rules file */
+ myargs = strchr(args,'[');
+ if (! myargs ) FatalError("icmpscanIgnore" ": ERROR: %s (%d) => Ignore icmpscan configuration format:
[addr/mask,...]\n", file_name, file_line);;
+ t = strchr(myargs,']');
+ t = NULL;
+ toks = mSplit(myargs+1, ",", 32, &NumIgnore, '\\');
+ for (i=0; i<NumIgnore; i++) {
+ if ( 5 != sscanf(toks[i],"%lu.%lu.%lu.%lu/%lu",&a,&b,&c,&d,&e)) {
+ FatalError("icmpscanIgnore" ": ERROR: %s (%d) => Adressformat: a.b.c.d/mask\n", file_name, file_line);;
+ }
+ Ignore[i].netmask = 0xffffffff >> (32-e);
+ Ignore[i].network = ( a + (b << 8) + (c << 16) + (d << 24)) & ( Ignore[i].netmask ) ;
+ }
+ /* Set the preprocessor function into the function list */
+ AddFuncToCleanExitList(ExitIgnoreIcmpScanFunction,NULL);
+}
+
+
+
+void ExitIgnoreIcmpScanFunction(int signal,void *arg)
+{
+ /* clean exit code goes here */
+ free(Ignore);
+}
+
+/*****************************************************************
+ *
+ * Function: Igonre Packet
+ * Checks if a certain packet should be ignored.
+ * Argument: p => Pointer to pacet struct
+ * Return value: 1 if the packet should be ignored, 0 otherwise.
+ *****************************************************************/
+int IgnorePacket(Packet *p) {
+ int i;
+ for (i=0; i< NumIgnore; i++) {
+ if ( (p->iph->ip_src.s_addr & Ignore[i].netmask) == Ignore[i].network ) return 1;
+ }
+ return 0;
+}
+
--- snort.orig/spp_icmpscan.h Fri Jul 6 16:37:00 2001
+++ snort/spp_icmpscan.h Fri Jul 6 16:10:09 2001
@@ -0,0 +1,29 @@
+/* $Id: spp_template.h,v 1.2 2000/11/13 06:01:25 roesch Exp $ */
+/* Snort Preprocessor Plugin Header File Template */
+
+/* This file gets included in plugbase.h when it is integrated into the rest
+ * of the program. Sometime in The Future, I'll whip up a bad ass Perl script
+ * to handle automatically loading all the required info into the plugbase.*
+ * files.
+ */
+#include "snort.h"
+
+#ifndef __SPP_ICMPSCAN_H__
+#define __SPP_ICMPSCAN_H__
+
+/* typedef struct _TemplateData
+{
+
+} TemplateData; */
+
+/* list of function prototypes for this preprocessor */
+void SetupIcmpScan();
+void IcmpScanInit(u_char *);
+void ParseIcmpScanArgs(char *);
+void PreprocIcmpScanFunction(Packet *);
+void ExitIcmpScanFunction(int,void *);
+
+void SetupIgnoreIcmpScan();
+void ExitIgnoreIcmpScanFunction(int signal,void *arg);
+void IgnoreIcmpScanInit(u_char *args);
+#endif /* __SPP_ICMPSCAN_H__ */
--- snort.orig/plugbase.c Fri Jun 22 00:22:19 2001
+++ snort/plugbase.c Fri Jul 6 16:08:28 2001
@@ -75,6 +75,8 @@
SetupHttpDecode();
SetupPortscan();
SetupPortscanIgnoreHosts();
+ SetupIcmpScan();
+ SetupIgnoreIcmpScan();
SetupDefrag();
SetupTcpStream2();
SetupSpade();
--- snort.orig/plugbase.h Fri Jun 22 00:22:19 2001
+++ snort/plugbase.h Fri Jul 6 16:06:13 2001
@@ -52,6 +52,7 @@
#include "spp_http_decode.h"
#include "spp_portscan.h"
+#include "spp_icmpscan.h"
#include "spp_defrag.h"
#include "spp_tcp_stream2.h"
#include "spp_anomsensor.h"
--- snort.orig/generators.h Tue Jul 3 02:43:02 2001
+++ snort/generators.h Fri Jul 6 16:13:04 2001
@@ -72,5 +72,8 @@
#define STREAM4_STEALTH_NMAP_FINGERPRINT 12
#define STREAM4_STEALTH_SYN_FIN_SCAN 13
-
+#define GENERATOR_SPP_ICMPSCAN 112
+#define ICMPSCAN_SCAN_DETECT 1
+#define ICMPSCAN_INTER_INFO 2
+#define ICMPSCAN_SCAN_END 3
#endif /* __GENERATORS_H__ */
--- snort.orig/Makefile.am Tue Jun 26 04:49:30 2001
+++ snort/Makefile.am Fri Jul 6 16:07:18 2001
@@ -31,7 +31,7 @@
sp_ip_same_check.h sp_priority.c sp_priority.h sp_ip_proto.c \
sp_ip_proto.h ubi_BinTree.c ubi_BinTree.h ubi_SplayTree.c \
spo_unified.c spo_unified.h generators.h spp_stream4.h spp_stream4.c \
-ubi_SplayTree.h sys_include.h
+ubi_SplayTree.h sys_include.h spp_icmpscan.h spp_icmpscan.c
EXTRA_DIST = BUGS RULES.SAMPLE CREDITS snort.conf USAGE backdoor.rules \
info.rules smtp.rules ddos.rules local.rules telnet.rules dns.rules \
Current thread:
- [Snort-devel] Call for Bugs -> icmpscaner Serge Droz (Jul 06)
