diff options
Diffstat (limited to 'perl/Ipfanaly.pl')
-rw-r--r-- | perl/Ipfanaly.pl | 639 |
1 files changed, 639 insertions, 0 deletions
diff --git a/perl/Ipfanaly.pl b/perl/Ipfanaly.pl new file mode 100644 index 0000000..0fa7c17 --- /dev/null +++ b/perl/Ipfanaly.pl @@ -0,0 +1,639 @@ +#!/usr/local/bin/perl +# (C) Copyright 1998 Ivan S. Bishop (isb@notoryus.genmagic.com) +# +############### START SUBROUTINE DECLARATIONS ########### + + +sub usage { + print "\n" x 24; + print "USAGE: ipfanalyze.pl -h [-p port# or all] [-g] [-s] [-v] [-o] portnum -t [target ip address] [-f] logfilename\n"; + print "\n arguments to -p -f -o REQUIRED\n"; + print "\n -h show this help\n"; + print "\n -p limit stats/study to this port number.(eg 25 not smtp)\n"; + print " -g make graphs, one per 4 hour interval called outN.gif 1<=N<=5\n"; + print " -s make security report only (no graphical or full port info generated) \n"; + print " -o lowest port number incoming traffic can talk to and be regarded as safe\n"; + print " -v verbose report with graphs and textual AND SECURITY REPORTS with -o 1024 set\n"; + print " -t the ip address of the inerface on which you collected data!\n"; + print " -f name ipfilter log file (compatible with V 3.2.9) [ipfilter.log]\n"; + print " \nExample: ./ipfanalyze.pl -p all -g -f log1\n"; + print "Will look at traffic to/from all ports and make graphs from file log1\n"; + print " \nExample2 ./ipfanalyze.pl -p 25 -g -f log2\n"; + print "Will look at SMTP traffic and make graphs from file log2\n"; + print " \nExample3 ./ipfanalyze.pl -p all -g -f log3 -o 1024\n"; + print "Will look at all traffic,make graphs from file log3 and log security info for anthing talking inwards below port 1024\n"; + print " \nExample4 ./ipfanalyze.pl -p all -f log3 -v \n"; + print "Report the works.....when ports below 1024 are contacted highlight (like -s -o 1024)\n"; +} + + + + +sub makegifs { +local ($maxin,$maxout,$lookat,$xmax)=@_; +$YMAX=$maxin; +$XMAX=$xmax; + +if ($maxout > $maxin) + { $YMAX=$maxout;} + +($dateis,$junk)=split " " , @recs[0]; +($dayis,$monthis,$yearis)=split "/",$dateis; +$month=$months{$monthis}; +$dateis="$dayis " . "$month " . "$yearis "; +# split graphs in to 6 four hour spans for 24 hours +$numgraphs=int($XMAX/240); + +$junk=0; +$junk=$XMAX - 240*($numgraphs); +if($junk gt 0 ) +{ +$numgraphs++; +} + +$cnt1=0; +$end=0; +$loop=0; + +while ($cnt1++ < $numgraphs) +{ + $filename1="in$cnt1.dat"; + $filename2="out$cnt1.dat"; + $filename3="graph$cnt1.conf"; + open(OUTDATA,"> $filename2") || die "Couldnt open $filename2 for writing \n"; + open(INDATA,"> $filename1") || die "Couldnt open $filename1 for writing \n"; + + $loop=$end; + $end=($end + 240); + +# write all files as x time coord from 1 to 240 minutes +# set hour in graph via conf file + $arraycnt=0; + while ($loop++ < $end ) + { + $arraycnt++; + $val1=""; + $val2=""; + $val1=$inwards[$loop] [1]; + if($val1 eq "") + {$val1=0}; + $val2=$outwards[$loop] [1]; + if($val2 eq "") + {$val2=0}; + print INDATA "$arraycnt:$val1\n"; + print OUTDATA "$arraycnt:$val2\n"; + } + close INDATA; + close OUTDATA; + $gnum=($cnt1 - 1); + open(INCONFIG,"> $filename3") || die "Couldnt open ./graph.conf for writing \n"; + print INCONFIG "NUMBERYCELLGRIDSIZE:5\n"; + print INCONFIG "MAXYVALUE:$YMAX\n"; + print INCONFIG "MINYVALUE:0\n"; + print INCONFIG "XCELLGRIDSIZE:1.3\n"; + print INCONFIG "XMAX: 240\n"; + print INCONFIG "Bar:0\n"; + print INCONFIG "Average:0\n"; + print INCONFIG "Graphnum:$gnum\n"; + print INCONFIG "Title: port $lookat packets/minute to/from gatekeep on $dateis \n"; + print INCONFIG "Transparent:no\n"; + print INCONFIG "Rbgcolour:0\n"; + print INCONFIG "Gbgcolour:255\n"; + print INCONFIG "Bbgcolour:255\n"; + print INCONFIG "Rfgcolour:0\n"; + print INCONFIG "Gfgcolour:0\n"; + print INCONFIG "Bfgcolour:0\n"; + print INCONFIG "Rcolour:0\n"; + print INCONFIG "Gcolour:0\n"; + print INCONFIG "Bcolour:255\n"; + print INCONFIG "Racolour:255\n"; + print INCONFIG "Gacolour:255\n"; + print INCONFIG "Bacolour:0\n"; + print INCONFIG "Rincolour:100\n"; + print INCONFIG "Gincolour:100\n"; + print INCONFIG "Bincolour:60\n"; + print INCONFIG "Routcolour:60\n"; + print INCONFIG "Goutcolour:100\n"; + print INCONFIG "Boutcolour:100\n"; + close INCONFIG; + +} + + +$cnt1=0; +while ($cnt1++ < $numgraphs) +{ + $filename1="in$cnt1.dat"; + $out="out$cnt1.gif"; + $filename2="out$cnt1.dat"; + $filename3="graph$cnt1.conf"; + system( "cp ./$filename1 ./in.dat; + cp ./$filename2 ./out.dat; + cp ./$filename3 ./graph.conf"); + system( "./isbgraph -conf graph.conf;mv graphmaker.gif $out"); + system(" cp $out /isb/local/etc/httpd/htdocs/."); + +} + +} # end of subroutine make gifs + + + + +sub packbytime { +local ($xmax)=@_; +$XMAX=$xmax; +# pass in the dest port number or get graph for all packets +# at 1 minute intervals +# @shortrecs has form 209.24.1.217 123 192.216.16.2 123 udp len 20 76 +# @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 +# +# dont uses hashes to store how many packets per minite as they +# return random x coordinate order +@inwards=(); +@outwards=(); +$cnt=-1; +$value5=0; +$maxin=0; +$maxout=0; +$xpos=0; +while ($cnt++ <= $#recs ) + { + ($srcip,$srcport,$destip,$destport,$pro)= split " " , @shortrecs[$cnt]; + $bit=substr(@recs[$cnt],11); + ($bit,$junkit)= split " " , $bit ; + ($hour,$minute,$sec,$junk) = split ":", $bit; +# +# covert the time to decimal minutes and bucket to nearest minute +# + $xpos=($hour * 3600) + ($minute * 60) + ($sec) ; +# xpos is number of seconds since 00:00:00 on day...... + $xpos=int($xpos / 60); +# if we just want to see all packet in/out activity + if("$lookat" eq "all") + { + if("$destip" eq "$gatekeep") + { +# TO GATEKEEP port lookat +# print "to gatekeep at $xpos\n"; + $value5=$inwards[$xpos] [1]; + $value5++ ; +# $maxin = $value5 if $maxin < $value5 ; + + if($value5 > $maxin) + { + $maxin=$value5; + $timemaxin="$hour:$minute"; + } + $inwards[$xpos][1]=$value5; + } + else + { +# FROM GATEKEEP to port lookat +# print "from gatekeep at $xpos\n"; + $value4=$outwards[$xpos] [1]; + $value4++ ; +# $maxout = $value4 if $maxout < $value4 ; + if($value4 > $maxout) + { + $maxout=$value4; + $timemaxout="$hour:$minute"; + } + + $outwards[$xpos][1]=$value4; + } + } + + + + + if("$destport" eq "$lookat") + { + if("$destip" eq "$gatekeep") + { +# TO GATEKEEP port lookat +# print "to gatekeep at $xpos\n"; + $value5=$inwards[$xpos] [1]; + $value5++ ; + $maxin = $value5 if $maxin < $value5 ; + $inwards[$xpos][1]=$value5; + } + else + { +# FROM GATEKEEP to port lookat +# print "from gatekeep at $xpos\n"; + $value4=$outwards[$xpos] [1]; + $value4++ ; + $maxout = $value4 if $maxout < $value4 ; + $outwards[$xpos][1]=$value4; + } + } + } # end while + +# now call gif making stuff +if("$opt_g" eq "1") +{ + print "Making plots of in files outN.gif\n";; + makegifs($maxin,$maxout,$lookat,$#inwards); +} +if ("$timemaxin" ne "") +{print "\nTime of peak packets/minute in was $timemaxin\n";} +if ("$timemaxout" ne "") +{print "\nTime of peak packets/minute OUT was $timemaxout\n";} + +} # end of subroutine packets by time + + + + + +sub posbadones { + +$safenam=""; +@dummy=$saferports; +foreach $it (split " ",$saferports) { +if ($it eq "icmp" ) + { + $safenam = $safenam . " icmp"; + } +else + { + $safenam = $safenam . " $services{$it}" ; + } + +} +print "\n\n########################################################################\n"; +print "well known ports are 0->1023\n"; +print "Registered ports are 1024->49151\n"; +print "Dynamic/Private ports are 49152->65535\n\n"; +print "Sites that contacted gatekeep on 'less safe' ports (<$ITRUSTABOVE)\n"; + +print " 'safe' ports are $safenam \n"; +print "\n variables saferports and safehosts hardwire what/who we trust\n"; +print "########################################################################\n"; + +$loop=-1; +while ($loop++ <= $#recs ) + { + ($srcip,$srcport,$destip,$destport,$pro)= split " " , @shortrecs[$loop]; + if ("$destip" eq "$gatekeep") + { + if ($destport < $ITRUSTABOVE ) + { +# if index not found (ie < 0) then we have a low port attach to gatekeep +# that is not to a safer port (see top of this file) +# ie no ports 25 (smtp), 53 (dns) , 113 (ident), 123 (ntp), icmp + $where=index($saferports,$destport); + if ($where < 0) + { + $nameis=$services{$destport}; + if ("$nameis" eq "" ) + { + $nameis=$destport; + } + print " Warning: $srcip contacted gatekeep $nameis\n"; + } + } + } + } +print "\n\n"; +} # end of subroutine posbadones + + + + +sub toobusy_site { +$percsafe=1; +print "\n\n########################################################################\n"; +print "# Sites sending > $percsafe % of all packets to gatekeep MAY be attacking/probing\n"; +print "Trusted hosts are $safehosts\n"; +print "\nTOTAL packets were $#recs \n"; +print "########################################################################\n"; +while(($ipadd,$numpacketsent)=each %numpacks) +{ +$perc=$numpacketsent/$#recs*100; +if ($perc > $percsafe) +# dont believe safehosts are attacking! + { + $where=index($safehosts,$ipadd); +# if not found (ie < 0 then the source host IP address +# isn't in the saferhosts list, a list we trust...... + if ($where < 0 ) + { + printf "$ipadd sent %4.1f (\045) of all packets to gatekeep\n",$perc; + } + } +} + +print "\n\n"; +} # end of subroutine toobusy_site + + +############### END SUBROUTINE DECLARATIONS ########### + +use Getopt::Std; + +getopt('pfot'); + +if("$opt_t" eq "0") + {usage;print "\n---->ERROR: You must psecify the IP address of the interface that collected the data!\n"; +exit; +} + +if("$opt_h" eq "1") + {usage;exit 0}; +if("$opt_H" eq "1") + {usage;exit 0}; + +if("$opt_v" eq "1") +{ +$ITRUSTABOVE=1024; +$opt_s=1; +$opt_o=$ITRUSTABOVE; +print "\n" x 5; +print "NOTE: when the final section of the verbose report is generated\n"; +print " every host IP address that contacted $gatekeep has \n"; +print " a tally of how many times packets from a particular port on that host\n"; +print " reached $gatekeep, and WHICH source port or source portname \n"; +print " these packets originated from.\n"; +print " Many non RFC obeying boxes do not use high ports and respond to requests from\n"; +print " $gatekeep using reserved low ports... hence you'll see things like\n"; +print " #### with 207.50.191.60 as the the source for packets ####\n"; +print " 1 connections from topx to gatekeep\n\n\n\n"; + +} + +if("$opt_o" eq "") + {usage;print "\n---->ERROR: Must specify lowest safe port name for incoming trafic\n";exit 0} +else +{ +$ITRUSTABOVE=$opt_o;$opt_s=1;} + +if("$opt_f" eq "") + {usage;print "\n---->ERROR: Must specify filename with -f \n";exit 0}; +$FILENAME=$opt_f; + +if("$opt_p" eq "") + {usage;print "\n---->ERROR: Must specify port number or 'all' with -p \n";exit 0}; + +# -p arg must be all or AN INTEGER in range 1<=N<=64K +if ("$opt_p" ne "all") + { + $_=$opt_p; + unless (/^[+-]?\d+$/) + { + usage; + print "\n---->ERROR: Must specify port number (1-64K) or 'all' with -p \n"; + exit 0; + } + } + + +# if we get here then the port option is either 'all' or an integer... +# good enough..... +$lookat=$opt_p; + +# -o arg must be all or AN INTEGER in range 1<=N<=64K + $_=$opt_o; + unless (/^[+-]?\d+$/) + { + usage; + print "\n---->ERROR: Must specify port number (1-64K) with -o \n"; + exit 0; + } + + +#--------------------------------------------------------------------- + + +%danger=(); +%numpacks=(); + +$saferports="25 53 113 123 icmp"; +$gatekeep="192.216.16.2"; +#genmagic is 192.216.25.254 +$safehosts="$gatekeep 192.216.25.254"; + + + +# load hash with service numbers versus names + +# hash called $services +print "Creating hash of service names / numbers \n"; +$SERV="./services"; +open (INFILE, $SERV) || die "Cant open $SERV: $!n"; +while(<INFILE>) +{ + ($servnum,$servname,$junk)=split(/ /,$_); +# chop off null trailing..... + $servname =~ s/\n$//; + $services{$servnum}=$servname; +} +print "Create hash of month numbers as month names\n"; +%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"); + +print "Reading log file into an array\n"; +#$FILENAME="./ipfilter.log"; +open (REC, $FILENAME) || die "Cant open $FILENAME: \n"; +($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$junk)=stat REC; +print "Log file $FILENAME is $size bytes in size\n"; +#each record is an element of array rec[] now +while(<REC>) + { + @recs[$numrec++]=$_; + } + + +# get list of UNIQUE source IP addresses now, records look like +# 192.216.25.254,62910 -> 192.216.16.2,113 PR tcp len 20 40 -R +# this is slow on big log files, about 1minute for every 2.5M log file +print "Making list of unique source IP addresses (1minute for every 2M log parsed)\n"; +$loop=-1; +$where=-1; +while ($loop++ < $#recs ) + { +# get the LHS = source IP address, need fiddle as icmp rcords are logged oddly + $bit=substr(@recs[$loop],39); + $bit =~ s/,/ /g; + ($sourceip,$junkit)= split " " , $bit ; + +# NOTE the . is the string concat command NOT + .......!!!! + + $sourceip =~ split " ", $sourceip; + $where=index($allips,$sourceip); +# if not found (ie < 0, add it) + if ($where < 0 ) + { + $allips = $allips . "$sourceip " ; + } + } + +print "Put all unique ip addresses into a 1D array\n"; +@allips=split " ", $allips; + +#set loop back to -1 as first array element in recs is element 0 NOT 1 !! +print "Making compact array of logged entries\n"; +$loop=-1; +$icmp=" icmp "; +$ptr=" -> "; +$lenst=" len "; +$numpackets=0; + +while ($loop++ < $#recs ) + { +# this prints from 39 char to EOR + $a=substr(@recs[$loop],39); + ($srcip,$dummy,$destip,$dummy2,$dummy3,$dummy4,$lenicmp)= split " " , $a ; +# need to rewrite icmp ping records.... they dont have service numbers + $whereicmp=index($a,"PR icmp"); + if($whereicmp > 0 ) + { + $a = $srcip . $icmp . $ptr . $destip . $icmp . $icmp . $lenst . $lenicmp ; + } + +# dump the "->" and commas from logging + $a =~ s/->//g; + $a =~ s/PR//g; + $a =~ s/,/ /g; +# shortrec has records that look like +# 209.24.1.217 123 192.216.16.2 123 udp len 20 76 + @shortrecs[$loop]= "$a"; + +# count number packets from each IP address into hash + ($srcip,$junk) = split " ","$a"; + $numpackets=$numpacks{"$srcip"}; + $numpackets++ ; + $numpacks{"$srcip"}=$numpackets; + +} + + + +# call sub to analyse packets by time +# @shortrecs has form 209.24.1.217 123 192.216.16.2 123 udp len 20 76 +# @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 +packbytime($XMAX); + +if("$opt_s" eq "1") +{ +# call subroutine to scan for connections to ports on gatekeep +# other than those listed in saferports, connections to high +# ports are assumed OK..... +posbadones; + +# call subroutine to print out which sites had sent more than +# a defined % of packets to gatekeep +toobusy_site; +} + + +# verbose reporting? +if ("$opt_v" eq "1") +{ +$cnt=-1; +# loop over ALL unique IP source destinations +while ($cnt++ < $#allips) +{ + %tally=(); + %unknownsrcports=(); + $uniqip=@allips[$cnt]; + $loop=-1; + $value=0; + $value1=0; + $value2=0; + $value3=0; + $set="N"; + + while ($loop++ < $#recs ) + { +# get src IP num, src port number, +# destination IP num, destnation port number,protocol + ($srcip,$srcport,$destip,$destport,$pro)= split " " , @shortrecs[$loop]; +# loop over all records for the machine $uniqip +# NOTE THE STRINGS ARE COMPARED WITH eq NOT cmp and NOT = !!!! + if( "$uniqip" eq "$srcip") + { +# look up hash of service names to get key... IF ITS NOT THERE THEN WHAT??? +# its more than likely a request coming back in on a high port +# ....So... +# find out the destination port from the unknown (high) src port +# and tally these as they may be a port attack + if ("$srcport" eq "icmp") + { $srcportnam="icmp";} + else + { + $srcportnam=$services{$srcport}; + } +# try and get dest portname, if not there, leave it as the +# dest portnumber + if ("$destport" eq "icmp") + { $destportnam="icmp";} + else + { + $destportnam=$services{$destport}; + } + + if ($destportnam eq "") + { + $destportnam=$destport; + } + + if ($srcportnam eq "") + { +# increment number of times a (high)/unknown port has gone to destport + $value1=$unknownsrcports{$destportnam}; + $value1++ ; + $unknownsrcports{$destportnam}=$value1; + } + else + { +# want tally(srcport) counter to be increased by 1 + $value3=$tally{$srcportnam}; + $value3++ ; + $tally{$srcportnam}=$value3; + } + } + + + } +# end of loop over ALL IP's + +if ($set eq "N") +{ +$set="Y"; + +print "\n#### with $uniqip as the the source for packets ####\n"; +while(($key,$value)=each %tally) + { + if (not "$uniqip" eq "$gatekeep") + { + print "$value connections from $key to gatekeep\n"; + } + else + { + print "$value connections from gatekeep to $key\n"; + } + } + + + +while(($key2,$value2)=each %unknownsrcports) + { + if (not "$uniqip" eq "$gatekeep") + { + print "$value2 high port connections to $key2 on gatekeep\n"; + } + else + { + print "$value2 high port connections to $key2 from gatekeep\n"; + } + } + +} +# print if rests for UNIQIP IF flag is set to N then toggle flag + +} # end of all IPs loop +} # end of if verbose option set block + + + |