#!/usr/bin/perl -s # # DShield Client SNORT PORTSCAN # # Parameters: # # -config - Name of config file (default /etc/dshield.cnf ) # -from - e-mail 'from' information # -to - e-mail 'to' information (default dshield@dshield.cirt.vt.edu ) # -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"; $$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='dshield@dshield.cirt.vt.edu' 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 $verbose; $rotate='Y' unless $verbose; $linecnt='/var/log/dshield.cnt' unless $linecnt; $obfus='N' unless $obfus; $version="VERSION"; 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' ) { 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' ) { 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 writing\n"); foreach () { print MAIL $_; } close TMP; close MAIL; } else { print "TMP file empty. Not sending any mail\n" if ( $verbose='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; } } # START cisco # # CISCO Parser # currently only tcp/udp are dealt with. icmp is ignored. # # sample line: # Apr 1 01:02:03 [1.2.3.4] 12345: %ABC-1-ABCDEFGH: abcd 123 denied tcp # 5.6.7.9(12345) -> 10.10.10.10(111), 1 packet # # sub cisco_parser { sub parse { my $line=shift; my @rline; my ($year,$i,$marker,$x,$y); print "$line\n" if ($verbose eq 'Y'); return 0 unless ( $line =~ / denied / ); my @parts=split(" ",$line); $parts[0]=$Months{$parts[0]}; $year=$cur_year; $year=$cur_year+1 if ($parts[0]>$cur_month); $rline[0]=sprintf("%0.4d-%0.2d-%0.2d %s %s",$year,$parts[0],$parts[1],$parts[2],$tz); $rline[1]=$userid; foreach (@parts) { if (/denied/) { $marker=$i; } $i++; } $rline[7]=uc($parts[$marker+1]); if ($rline[7] eq 'ICMP') { $rline[2]=$parts[$marker+6]; $rline[3]=$parts[$marker+2]; $rline[5]=$parts[$marker+4]; $parts[$marker+5]=~ /\((\d+)\/(\d+)\)/; $rline[4]=$1; $rline[6]=$2; } else { $rline[2]=$parts[$marker+5]; ($rline[3],$rline[4])=split('\(',$parts[$marker+2]); chop($rline[4]); ($rline[5],$rline[6])=split('\(',$parts[$marker+4]); chop $rline[6]; chop $rline[6]; } return @rline; } # START snort_portscan # # SNORT PORTSCAN Parser # parser for snort portscan log # # sample: # Jun 10 01:02:03 1.2.3.4:1234 -> 4.3.2.1:4321 SYN ******S* # sub parse { sub snort_portscan_parser { my $line=shift; my @rline; my $year; print "PARSING: $line" if ($verbose eq 'Y'); return 0 unless ( $line =~ / -> / ); my @parts=split(" ",$line); $parts[0]=$Months{$parts[0]}; $year=$cur_year; $year=$cur_year+1 if ($parts[0]>$cur_month); $rline[0]=sprintf("%0.4d-%0.2d-%0.2d %s %s",$year,$parts[0],$parts[1],$parts[2],$tz); $rline[1]=$userid; $rline[2]=1; ($rline[3],$rline[4])=split(":",$parts[3]); ($rline[5],$rline[6])=split(":",$parts[5]); $rline[7]=$parts[6]; $rline[7]='TCP' unless ($parts[6] eq 'UDP'); $rline[8]=$parts[7]; $rline[8] =~ s/\*//g; return @rline; } # START iptables # # IP TABLES Parsers # parser for Linux kernel 2.4 iptables logs. # # sample: # Jun 13 03:57:40 dshield kernel: auditIN=eth0 OUT= # MAC=00:01:02:36:ef:a6:00:00:bc:11:d1:1a:08:00 SRC=216.72.212.110 # DST=209.140.49.4 LEN=60 TOS=0x00 PREC=0x00 TTL=49 ID=8371 DF PROTO=TCP # SPT=4694 DPT=111 WINDOW=32120 RES=0x00 SYN URGP=0 # # sub parse { sub iptables_parser { my $line=shift; my @rline; my $year; my %vals; print $line if ( $verbose eq 'Y' ); return 0 unless ( $line =~ / kernel: auditIN=/ ); my @parts=split(" ",$line); $parts[0]=$Months{$parts[0]}; $year=$cur_year; $year=$cur_year+1 if ($parts[0]>$cur_month); $rline[0]=sprintf("%0.4d-%0.2d-%0.2d %s %s",$year,$parts[0],$parts[1],$parts[2],$tz); foreach $i (4..$#parts) { if ($parts[$i] =~ /=/ ) { ($name,$value)=split('=',$parts[$i]); $vals{$name}=$value; print "$name,$value\n"; } } $rline[1]=$userid; $rline[2]=1; $rline[3]=$vals{SRC}; $rline[4]=$vals{SPT}; $rline[5]=$vals{DST}; $rline[6]=$vals{DPT}; $rline[7]=$vals{PROTO}; $rline[8].='S' if ( $line=~/ SYN / ); $rline[8].='R' if ( $line=~/ RST / ); $rline[8].='F' if ( $line=~/ FIN / ); $rline[8].='A' if ( $line=~/ ACK / ); $rline[8].='U' if ( $vals{URGP} ne '0' ); return @rline; }