#!/usr/bin/perl -s $VERSION='ipf_04072001'; # ipf parse routine by Ken McKinlay based on material by: # Dirk-Willem van Gulik ##!/usr/bin/perl -s # # DShield Client Framework # # Parameters: # # -config - Name of config file (default /etc/dshield.cnf ) # -from - e-mail 'from' information # -to - e-mail 'to' information (default reports@dshield.org ) # -userid - dshield numerid userid (default 0) # -tz - timezone (default: date -z) # -log - location of log file (default: STDIN) # -verbose - turn on extra output # -sendmail - location of mail software # (default: '/usr/sbin/sendmail -oi -t') # -rotate - Y: copy to 'log.old' # N: do nothing, but keep line count file # A: append to log.old # -linecnt - name of line count file (if rotate=N). # Default: /var/log/dshield.cnt # this file save the date now, not the number of lines. # -tmp - specify a temp file name # -obfus - Y: hide target IP (replace first byte with 10.) # - N: don't (default). # -target_exclude - name of file with excluded target IPs. # (default: none) # -source_exclude - name of file with excluded source IPs. # (default: none) # # The idea of this framework is to establish a list of common # features and command line paramters accross all clients. # # The script uses no perl modules to provide for maximum # compatibility. # %Months=( Jan => 1, Feb => 2, Mar => 3, Apr => 4, May => 5, Jun =>6, Jul => 7, Aug => 8, Sep => 9, Oct => 10, Nov => 11, Dec => 12 ); $config='/etc/dshield.cnf' unless $config; print "Config File: $config\n" if $verbose; $ENV{"PATH"}="/usr/bin"; $ENV{"BASH_ENV"}=""; if ( -f $config ) { open (CONF,"$config") || die ("Can't open $config for reading\n"); foreach () { if ( ! /^#/ ) { chomp; ($name,$value)=split("="); $name=lc($name); print "name: $name value: $value\n" if $verbose; $$name=$value; } } } print "$log\n" if ( $verbose eq 'Y'); if ( $target_exclude ) { if ( -f $target_exclude ) { open (FILE,$target_exclude); foreach () { chomp; unless ( /^#/ ) { if ( /^[\d\.]+$/ ) { $excluded_targets .= "|$_|"; } else { print "Bad IP address ($_) in $target_exclude\n"; } } } } else { die ("Can't find the target exclude file ($target_exclude)\n"); } } if ( $source_exclude ) { if ( -f $source_exclude ) { open (FILE,$source_exclude); foreach () { chomp; unless ( /^#/ ) { if ( /^[\d\.]+$/ ) { $excluded_sources .= "|$_|"; } else { print "Bad IP address ($_) in $source_exclude\n"; } } } } else { die ("Can't find the source exclude file ($source_exclude)\n"); } } $from='nobody@nowhere.com' unless $from; $to='report@dshield.org' unless $to; $userid=0 unless $userid; $tz=`date +%z` unless $tz; @the_date=localtime(time()); $cur_year=$the_date[5]+1900; $cur_month=$the_date[4]+1; $log='-' unless $log; $verbose='N' unless $verbose; $sendmail='/usr/sbin/sendmail -oi -t' unless $sendmail; $rotate='Y' unless $rotate; $linecnt='/var/log/dshield.cnt' unless $linecnt; $obfus='N' unless $obfus; chomp $tz; $tmp="dshield.$$.tmp" unless $tmp; $excluded_sources="="; $excluded_targets="="; if ( $log ne '-') { die ("Can't find log file at $log\n") unless ( -f $log ); } if ($rotate eq 'N') { print "Opening Line count file $linecnt\n" if ($verbose eq 'Y'); if ( -f $linecnt) { open (CNT,$linecnt) || die "Can't open line count file ($linecnt)\n"; $line_count=; chomp ($line_count); close CNT; } else { $line_count='2000-01-01'; } } print "Opening Log: $log\n" if ( $verbose eq 'Y' ); open (LOG,$log) || die ("Can't open $log for reading\n"); open (TMP,"> $tmp") || die ("Can't open temp file ($tmp) for writing\n"); foreach $line () { # # @dshield_array: # # 0 - time/date/timezone 1 - author 2 - count # 3 - sourceip, 4 - sourceport, # 5 - targetip, 6 - targetport, # 7 - protocol, 8 - flags @dshield_array=parse($line); if ($dshield_array[0]>$line_count) { $lines=$dshield_array[0] if ($dshield_array[0]>$lines); if ( ! ( $excluded_sources =~ /\|$dshield_array[3]\|/ ) ) { if ( ! ( $excluded_targets =~ /\|$dshield_array[5]\|/ ) ) { if ($obfus eq 'Y' ) { $dshield_array[5]=~ s/^\d+\./10./; } $dshield=join("\t",@dshield_array); print "PARSE RESULT $dshield\n" if ($verbose eq 'Y'); if (validate_dshield($dshield) ) { print TMP "$dshield\n"; print "WRITTEN: $dshield\n" if ($verbose eq 'Y'); } else { print "nothing written\n" if ($verbose eq 'Y'); } } else { print "target $dshield_array[5] excluded\n" if ($verbose eq 'Y'); } } else { print "source $dshield_array[3] excluded\n" if ($verbose eq 'Y'); } } else { print "Lines skiped because date ($dshield_array[0]) is too early \n" if ($verbose eq 'Y'); } } close LOG; close TMP; if ( $rotate eq 'Y' && $log ne '-' ) { if ( -f "$log.bak" ) { unlink ("$log.bak"); } `mv $log $log.bak`; } if ( $rotate eq 'A' ) { `cat $log >> $log.bak`; unlink $log; } if ( $rotate eq 'N' && $log ne '-' ) { open (CNT,"> $linecnt") || die "Can't open line count file for writing($linecnt)\n"; print CNT $lines; close CNT; } unless ( -z $tmp ) { if ( $verbose eq 'Y' ) { print "Sending mail using $sendmail\n"; print "To: $to, From: $from\n"; } open (MAIL,"| $sendmail"); print MAIL "To: $to\n"; print MAIL "From: $from\n"; print MAIL "Subject: USERID $userid FORMAT DSHIELD TZ $tz VERSION $VERSION\n\n"; open (TMP,"$tmp") || die ("Can't open temp file ($tmp) for reading\n"); foreach () { print MAIL $_; } close TMP; close MAIL; } else { print "TMP file empty. Not sending any mail\n" if ( $verbose eq 'Y' ); } if ( -f $tmp ) { unlink ($tmp); } sub validate_dshield { my $line=shift; if ( $line =~ /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [+-]\d{2}:?\d{2}\t\d+\t\d+\t[0-9.]{7,15}\t\d+\t[0-9.]{7,15}\t\d+\t[0-9TCPIMPUD]{0,4}\t?[12UPFSAR]{0,6}$/ ) { return 1; } else { print "INVALID: $line\n"; return 0; } } # # OpenBSD ipf Parser # parser for ipf log files from OpenBSD # # sample: # 24/06/2001 23:41:01.084778 ep0 @0:11 p 1.2.3.4,2986 -> 5.6.7.8,27374 PR tcp len 20 48 -S IN # 25/06/2001 01:46:56.062860 ep0 @0:11 p 1.2.3.4,53 -> 5.6.7.8,46996 PR udp len 20 71 IN # 25/06/2001 13:18:46.614329 2x ep0 @0:11 p 1.2.3.4,18940 -> 5.6.7.8,27374 PR tcp len 20 48 -S IN sub parse { my $line = shift; my ($day,$month,$year,$hour,$minute,$second,$count,$action,$ipSource,$portSource,$ipDestin,$portDestin,$protocol); my ($extra,$flags); my @rline; print "PARSING: $line" if ($verbose eq 'Y'); return 0 unless ( $line =~ / -> / ); ($day,$month,$year,$hour,$minute,$second,undef,$count,undef,undef,$action,$ipSource,$portSource,$ipDestin,$portDestin,$protocol,$extra) = ($line =~ m/^\s*(\d+)\/(\d+)\/(\d+)\s+(\d+):(\d+):(\d+)\.(\d+)\s+(\w*)\s+(\w+)\s+(\S+)\s+(\w+)\s+([\.\d]+),?(\d*)\s+\->\s+([\.\d]+),?(\d*)\s+\w+\s+(\w+)\s+(.*)/); # parse the count value if ( $count ) { $count =~ s/\D//g; # toss away non-digit characters } else { $count = 1; } # uppercase the protocol field $protocol = uc $protocol; # split the extra field if TCP to get the flags and then remove the "-" from the flags if ( $protocol eq "TCP" ) { (undef,undef,undef,$flags)=split ' ',$extra; $flags =~ s/-//g; } else { $flags=""; } # if the protocol is ICMP, get the type and place in ports fields if ( $protocol eq "ICMP" ) { ($portSource,$portDestin)=($extra =~ m/^\s*\w+\s+\d+\s+\d+\s+\w+\s+(\d+)\/(\d+)/); } $rline[0]=sprintf("%0.4d-%0.2d-%0.2d %0.2d:%0.2d:%0.2d %s",$year,$month,$day,$hour,$minute,$second,$tz); $rline[1]=$userid; $rline[2]=$count; $rline[3]=$ipSource; $rline[4]=$portSource; $rline[5]=$ipDestin; $rline[6]=$portDestin; $rline[7]=$protocol; $rline[8]=$flags; return @rline; }