2 # (C) Copyright 1998 Ivan S. Bishop (isb@notoryus.genmagic.com)
4 ############### START SUBROUTINE DECLARATIONS ###########
9 print "USAGE: ipfanalyze.pl -h [-p port# or all] [-g] [-s] [-v] [-o] portnum -t [target ip address] [-f] logfilename\n";
10 print "\n arguments to -p -f -o REQUIRED\n";
11 print "\n -h show this help\n";
12 print "\n -p limit stats/study to this port number.(eg 25 not smtp)\n";
13 print " -g make graphs, one per 4 hour interval called outN.gif 1<=N<=5\n";
14 print " -s make security report only (no graphical or full port info generated) \n";
15 print " -o lowest port number incoming traffic can talk to and be regarded as safe\n";
16 print " -v verbose report with graphs and textual AND SECURITY REPORTS with -o 1024 set\n";
17 print " -t the ip address of the inerface on which you collected data!\n";
18 print " -f name ipfilter log file (compatible with V 3.2.9) [ipfilter.log]\n";
19 print " \nExample: ./ipfanalyze.pl -p all -g -f log1\n";
20 print "Will look at traffic to/from all ports and make graphs from file log1\n";
21 print " \nExample2 ./ipfanalyze.pl -p 25 -g -f log2\n";
22 print "Will look at SMTP traffic and make graphs from file log2\n";
23 print " \nExample3 ./ipfanalyze.pl -p all -g -f log3 -o 1024\n";
24 print "Will look at all traffic,make graphs from file log3 and log security info for anthing talking inwards below port 1024\n";
25 print " \nExample4 ./ipfanalyze.pl -p all -f log3 -v \n";
26 print "Report the works.....when ports below 1024 are contacted highlight (like -s -o 1024)\n";
33 local ($maxin,$maxout,$lookat,$xmax)=@_;
40 ($dateis,$junk)=split " " , @recs[0];
41 ($dayis,$monthis,$yearis)=split "/",$dateis;
42 $month=$months{$monthis};
43 $dateis="$dayis " . "$month " . "$yearis ";
44 # split graphs in to 6 four hour spans for 24 hours
45 $numgraphs=int($XMAX/240);
48 $junk=$XMAX - 240*($numgraphs);
58 while ($cnt1++ < $numgraphs)
60 $filename1="in$cnt1.dat";
61 $filename2="out$cnt1.dat";
62 $filename3="graph$cnt1.conf";
63 open(OUTDATA
,"> $filename2") || die "Couldnt open $filename2 for writing \n";
64 open(INDATA
,"> $filename1") || die "Couldnt open $filename1 for writing \n";
69 # write all files as x time coord from 1 to 240 minutes
70 # set hour in graph via conf file
72 while ($loop++ < $end )
77 $val1=$inwards[$loop] [1];
80 $val2=$outwards[$loop] [1];
83 print INDATA
"$arraycnt:$val1\n";
84 print OUTDATA
"$arraycnt:$val2\n";
89 open(INCONFIG
,"> $filename3") || die "Couldnt open ./graph.conf for writing \n";
90 print INCONFIG
"NUMBERYCELLGRIDSIZE:5\n";
91 print INCONFIG
"MAXYVALUE:$YMAX\n";
92 print INCONFIG
"MINYVALUE:0\n";
93 print INCONFIG
"XCELLGRIDSIZE:1.3\n";
94 print INCONFIG
"XMAX: 240\n";
95 print INCONFIG
"Bar:0\n";
96 print INCONFIG
"Average:0\n";
97 print INCONFIG
"Graphnum:$gnum\n";
98 print INCONFIG
"Title: port $lookat packets/minute to/from gatekeep on $dateis \n";
99 print INCONFIG
"Transparent:no\n";
100 print INCONFIG
"Rbgcolour:0\n";
101 print INCONFIG
"Gbgcolour:255\n";
102 print INCONFIG
"Bbgcolour:255\n";
103 print INCONFIG
"Rfgcolour:0\n";
104 print INCONFIG
"Gfgcolour:0\n";
105 print INCONFIG
"Bfgcolour:0\n";
106 print INCONFIG
"Rcolour:0\n";
107 print INCONFIG
"Gcolour:0\n";
108 print INCONFIG
"Bcolour:255\n";
109 print INCONFIG
"Racolour:255\n";
110 print INCONFIG
"Gacolour:255\n";
111 print INCONFIG
"Bacolour:0\n";
112 print INCONFIG
"Rincolour:100\n";
113 print INCONFIG
"Gincolour:100\n";
114 print INCONFIG
"Bincolour:60\n";
115 print INCONFIG
"Routcolour:60\n";
116 print INCONFIG
"Goutcolour:100\n";
117 print INCONFIG
"Boutcolour:100\n";
124 while ($cnt1++ < $numgraphs)
126 $filename1="in$cnt1.dat";
128 $filename2="out$cnt1.dat";
129 $filename3="graph$cnt1.conf";
130 system( "cp ./$filename1 ./in.dat;
131 cp ./$filename2 ./out.dat;
132 cp ./$filename3 ./graph.conf");
133 system( "./isbgraph -conf graph.conf;mv graphmaker.gif $out");
134 system(" cp $out /isb/local/etc/httpd/htdocs/.");
138 } # end of subroutine make gifs
146 # pass in the dest port number or get graph for all packets
147 # at 1 minute intervals
148 # @shortrecs has form 209.24.1.217 123 192.216.16.2 123 udp len 20 76
149 # @recs has form 27/07/1998 00:01:05.216596 le0 @0:2 L 192.216.21.16,2733 -> 192.216.16.2,53 PR udp len 20 62
151 # dont uses hashes to store how many packets per minite as they
152 # return random x coordinate order
160 while ($cnt++ <= $#recs )
162 ($srcip,$srcport,$destip,$destport,$pro)= split " " , @shortrecs[$cnt];
163 $bit=substr(@recs[$cnt],11);
164 ($bit,$junkit)= split " " , $bit ;
165 ($hour,$minute,$sec,$junk) = split ":", $bit;
167 # covert the time to decimal minutes and bucket to nearest minute
169 $xpos=($hour * 3600) + ($minute * 60) + ($sec) ;
170 # xpos is number of seconds since 00:00:00 on day......
171 $xpos=int($xpos / 60);
172 # if we just want to see all packet in/out activity
173 if("$lookat" eq "all")
175 if("$destip" eq "$gatekeep")
177 # TO GATEKEEP port lookat
178 # print "to gatekeep at $xpos\n";
179 $value5=$inwards[$xpos] [1];
181 # $maxin = $value5 if $maxin < $value5 ;
186 $timemaxin="$hour:$minute";
188 $inwards[$xpos][1]=$value5;
192 # FROM GATEKEEP to port lookat
193 # print "from gatekeep at $xpos\n";
194 $value4=$outwards[$xpos] [1];
196 # $maxout = $value4 if $maxout < $value4 ;
197 if($value4 > $maxout)
200 $timemaxout="$hour:$minute";
203 $outwards[$xpos][1]=$value4;
210 if("$destport" eq "$lookat")
212 if("$destip" eq "$gatekeep")
214 # TO GATEKEEP port lookat
215 # print "to gatekeep at $xpos\n";
216 $value5=$inwards[$xpos] [1];
218 $maxin = $value5 if $maxin < $value5 ;
219 $inwards[$xpos][1]=$value5;
223 # FROM GATEKEEP to port lookat
224 # print "from gatekeep at $xpos\n";
225 $value4=$outwards[$xpos] [1];
227 $maxout = $value4 if $maxout < $value4 ;
228 $outwards[$xpos][1]=$value4;
233 # now call gif making stuff
236 print "Making plots of in files outN.gif\n";;
237 makegifs
($maxin,$maxout,$lookat,$#inwards);
239 if ("$timemaxin" ne "")
240 {print "\nTime of peak packets/minute in was $timemaxin\n";}
241 if ("$timemaxout" ne "")
242 {print "\nTime of peak packets/minute OUT was $timemaxout\n";}
244 } # end of subroutine packets by time
254 foreach $it (split " ",$saferports) {
257 $safenam = $safenam . " icmp";
261 $safenam = $safenam . " $services{$it}" ;
265 print "\n\n########################################################################\n";
266 print "well known ports are 0->1023\n";
267 print "Registered ports are 1024->49151\n";
268 print "Dynamic/Private ports are 49152->65535\n\n";
269 print "Sites that contacted gatekeep on 'less safe' ports (<$ITRUSTABOVE)\n";
271 print " 'safe' ports are $safenam \n";
272 print "\n variables saferports and safehosts hardwire what/who we trust\n";
273 print "########################################################################\n";
276 while ($loop++ <= $#recs )
278 ($srcip,$srcport,$destip,$destport,$pro)= split " " , @shortrecs[$loop];
279 if ("$destip" eq "$gatekeep")
281 if ($destport < $ITRUSTABOVE )
283 # if index not found (ie < 0) then we have a low port attach to gatekeep
284 # that is not to a safer port (see top of this file)
285 # ie no ports 25 (smtp), 53 (dns) , 113 (ident), 123 (ntp), icmp
286 $where=index($saferports,$destport);
289 $nameis=$services{$destport};
290 if ("$nameis" eq "" )
294 print " Warning: $srcip contacted gatekeep $nameis\n";
300 } # end of subroutine posbadones
307 print "\n\n########################################################################\n";
308 print "# Sites sending > $percsafe % of all packets to gatekeep MAY be attacking/probing\n";
309 print "Trusted hosts are $safehosts\n";
310 print "\nTOTAL packets were $#recs \n";
311 print "########################################################################\n";
312 while(($ipadd,$numpacketsent)=each %numpacks)
314 $perc=$numpacketsent/$#recs*100;
315 if ($perc > $percsafe)
316 # dont believe safehosts are attacking!
318 $where=index($safehosts,$ipadd);
319 # if not found (ie < 0 then the source host IP address
320 # isn't in the saferhosts list, a list we trust......
323 printf "$ipadd sent %4.1f (\045) of all packets to gatekeep\n",$perc;
329 } # end of subroutine toobusy_site
332 ############### END SUBROUTINE DECLARATIONS ###########
339 {usage
;print "\n---->ERROR: You must psecify the IP address of the interface that collected the data!\n";
354 print "NOTE: when the final section of the verbose report is generated\n";
355 print " every host IP address that contacted $gatekeep has \n";
356 print " a tally of how many times packets from a particular port on that host\n";
357 print " reached $gatekeep, and WHICH source port or source portname \n";
358 print " these packets originated from.\n";
359 print " Many non RFC obeying boxes do not use high ports and respond to requests from\n";
360 print " $gatekeep using reserved low ports... hence you'll see things like\n";
361 print " #### with 207.50.191.60 as the the source for packets ####\n";
362 print " 1 connections from topx to gatekeep\n\n\n\n";
367 {usage
;print "\n---->ERROR: Must specify lowest safe port name for incoming trafic\n";exit 0}
370 $ITRUSTABOVE=$opt_o;$opt_s=1;}
373 {usage
;print "\n---->ERROR: Must specify filename with -f \n";exit 0};
377 {usage
;print "\n---->ERROR: Must specify port number or 'all' with -p \n";exit 0};
379 # -p arg must be all or AN INTEGER in range 1<=N<=64K
380 if ("$opt_p" ne "all")
383 unless (/^[+-]?\d+$/)
386 print "\n---->ERROR: Must specify port number (1-64K) or 'all' with -p \n";
392 # if we get here then the port option is either 'all' or an integer...
396 # -o arg must be all or AN INTEGER in range 1<=N<=64K
398 unless (/^[+-]?\d+$/)
401 print "\n---->ERROR: Must specify port number (1-64K) with -o \n";
406 #---------------------------------------------------------------------
412 $saferports="25 53 113 123 icmp";
413 $gatekeep="192.216.16.2";
414 #genmagic is 192.216.25.254
415 $safehosts="$gatekeep 192.216.25.254";
419 # load hash with service numbers versus names
421 # hash called $services
422 print "Creating hash of service names / numbers \n";
424 open (INFILE
, $SERV) || die "Cant open $SERV: $!n";
427 ($servnum,$servname,$junk)=split(/ /,$_);
428 # chop off null trailing.....
429 $servname =~ s/\n$//;
430 $services{$servnum}=$servname;
432 print "Create hash of month numbers as month names\n";
433 %months=("01","January","02","February","03","March","04","April","05","May","06","June","07","July","08","August","09","September","10","October","11","November","12","December");
435 print "Reading log file into an array\n";
436 #$FILENAME="./ipfilter.log";
437 open (REC
, $FILENAME) || die "Cant open $FILENAME: \n";
438 ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$junk)=stat REC
;
439 print "Log file $FILENAME is $size bytes in size\n";
440 #each record is an element of array rec[] now
447 # get list of UNIQUE source IP addresses now, records look like
448 # 192.216.25.254,62910 -> 192.216.16.2,113 PR tcp len 20 40 -R
449 # this is slow on big log files, about 1minute for every 2.5M log file
450 print "Making list of unique source IP addresses (1minute for every 2M log parsed)\n";
453 while ($loop++ < $#recs )
455 # get the LHS = source IP address, need fiddle as icmp rcords are logged oddly
456 $bit=substr(@recs[$loop],39);
458 ($sourceip,$junkit)= split " " , $bit ;
460 # NOTE the . is the string concat command NOT + .......!!!!
462 $sourceip =~ split " ", $sourceip;
463 $where=index($allips,$sourceip);
464 # if not found (ie < 0, add it)
467 $allips = $allips . "$sourceip " ;
471 print "Put all unique ip addresses into a 1D array\n";
472 @allips=split " ", $allips;
474 #set loop back to -1 as first array element in recs is element 0 NOT 1 !!
475 print "Making compact array of logged entries\n";
482 while ($loop++ < $#recs )
484 # this prints from 39 char to EOR
485 $a=substr(@recs[$loop],39);
486 ($srcip,$dummy,$destip,$dummy2,$dummy3,$dummy4,$lenicmp)= split " " , $a ;
487 # need to rewrite icmp ping records.... they dont have service numbers
488 $whereicmp=index($a,"PR icmp");
491 $a = $srcip . $icmp . $ptr . $destip . $icmp . $icmp . $lenst . $lenicmp ;
494 # dump the "->" and commas from logging
498 # shortrec has records that look like
499 # 209.24.1.217 123 192.216.16.2 123 udp len 20 76
500 @shortrecs[$loop]= "$a";
502 # count number packets from each IP address into hash
503 ($srcip,$junk) = split " ","$a";
504 $numpackets=$numpacks{"$srcip"};
506 $numpacks{"$srcip"}=$numpackets;
512 # call sub to analyse packets by time
513 # @shortrecs has form 209.24.1.217 123 192.216.16.2 123 udp len 20 76
514 # @recs has form 27/07/1998 00:01:05.216596 le0 @0:2 L 192.216.21.16,2733 -> 192.216.16.2,53 PR udp len 20 62
519 # call subroutine to scan for connections to ports on gatekeep
520 # other than those listed in saferports, connections to high
521 # ports are assumed OK.....
524 # call subroutine to print out which sites had sent more than
525 # a defined % of packets to gatekeep
534 # loop over ALL unique IP source destinations
535 while ($cnt++ < $#allips)
539 $uniqip=@allips[$cnt];
547 while ($loop++ < $#recs )
549 # get src IP num, src port number,
550 # destination IP num, destnation port number,protocol
551 ($srcip,$srcport,$destip,$destport,$pro)= split " " , @shortrecs[$loop];
552 # loop over all records for the machine $uniqip
553 # NOTE THE STRINGS ARE COMPARED WITH eq NOT cmp and NOT = !!!!
554 if( "$uniqip" eq "$srcip")
556 # look up hash of service names to get key... IF ITS NOT THERE THEN WHAT???
557 # its more than likely a request coming back in on a high port
559 # find out the destination port from the unknown (high) src port
560 # and tally these as they may be a port attack
561 if ("$srcport" eq "icmp")
562 { $srcportnam="icmp";}
565 $srcportnam=$services{$srcport};
567 # try and get dest portname, if not there, leave it as the
569 if ("$destport" eq "icmp")
570 { $destportnam="icmp";}
573 $destportnam=$services{$destport};
576 if ($destportnam eq "")
578 $destportnam=$destport;
581 if ($srcportnam eq "")
583 # increment number of times a (high)/unknown port has gone to destport
584 $value1=$unknownsrcports{$destportnam};
586 $unknownsrcports{$destportnam}=$value1;
590 # want tally(srcport) counter to be increased by 1
591 $value3=$tally{$srcportnam};
593 $tally{$srcportnam}=$value3;
599 # end of loop over ALL IP's
605 print "\n#### with $uniqip as the the source for packets ####\n";
606 while(($key,$value)=each %tally)
608 if (not "$uniqip" eq "$gatekeep")
610 print "$value connections from $key to gatekeep\n";
614 print "$value connections from gatekeep to $key\n";
620 while(($key2,$value2)=each %unknownsrcports)
622 if (not "$uniqip" eq "$gatekeep")
624 print "$value2 high port connections to $key2 on gatekeep\n";
628 print "$value2 high port connections to $key2 from gatekeep\n";
633 # print if rests for UNIQIP IF flag is set to N then toggle flag
635 } # end of all IPs loop
636 } # end of if verbose option set block