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)