#!/usr/local/bin/perl

# A perl script for finding echo response packets.

# The major change is a new summary table that counts hits from 
# a source IP to an entire class C net and a threshold variable 
# ($SCAN_LIMIT, default 5). After SCAN_LIMIT packets are seen from
# a source to a net further packets from that source to the net
# are suppressed in the next table (further down there is still
# a complete table) and the count is displayed in the first table
# (again below the summaries there is a complete copy of the
# table if needed). You can search for "###" to find the start
# of a table, and for "---" to get to the end of the summary
# tables and start of the full tables (so you can delete them
# easily if you don't want them). The change is because of gag
# scans. The original gets unwieldy under a multi subnet gag
# scan (I haven't seen one yet so it hadn't bitten me, but
# undoubtably will :-) ) with the summarization and threshold
# you should still be able to see attacks on individual machines
# even while a scan or scans is going on.

# Peter Van Epp / Operations and Technical Support 



# when SCAN_LIMIT is exceeded for a source ip dest net pair the detail recording
# of the source and destination ip pairs stops (for table size limits) but 
# further hits are counted in the source ip to dest net count (the first
# table). Change the limit to suit your taste (or tolorance).

$SCAN_LIMIT = 5;

open(STDIN,$ARGV[0]) || die "Can't open $ARGV[0]: $!\n"
	if $ARGV[0];
$line = 0;
$start_time = "";
$end_time = "";
while (<STDIN>) {
	$line ++;
	if (($line % 10000) == 0) {
		print STDERR "Processing $line\n";
	}
	chop;
	$src_bytes = " ";
	$dest_bytes = " ";
	$source_net ="";
	$dest_net ="";
	$src_port = " ";
	$dst_port = " ";
	($date, $flag, $rest) = unpack("A18 A5 A200",$_);
	if ($start_time eq "") {
		$start_time = $date;
	}
	($type, $rest) = split(' ',$rest,2);
	if ($type eq "man") {
		$mid_flag = ' ';
	 	($source_ip, $dest_ip, $src_pkt, $dest_pkt, $src_bytes, 
		 $dest_bytes, $end_flag) = split(' ',$rest,7); 
	} elsif ($type eq "icmp") {
	 	($source_ip, $mid_flag, $dest_ip, $src_pkt, $dest_pkt, 
		 $end_flag) = split(' ',$rest,6); 
		if ($end_flag =~ /port/) {
			($t, $p, $dst_port, $rest) = split(' ',$end_flag);
		}
		($a,$b,$c,$d)= split(/\./,$source_ip);
		($e,$f,$g,$h)= split(/\./,$dest_ip);
	} else {
	 	($source_ip, $mid_flag, $dest_ip, $src_pkt, $dest_pkt, 
		 $src_bytes, $dest_bytes, $end_flag) = split(' ',$rest,8); 
		($a,$b,$c,$d,$src_port)= split(/\./,$source_ip);
		($e,$f,$g,$h,$dst_port)= split(/\./,$dest_ip);
	}
	$source_ip = "$a.$b.$c.$d";
	$source_net = "$a.$b.$c";
	$dest_ip = "$e.$f.$g.$h";
	$dest_net = "$e.$f.$g";
	if ($type eq "icmp") {
		if ($end_flag eq "ECO" || $end_flag eq "ECR") {
			$all_data{"$dest_ip"} .= "$date $source_ip $mid_flag $dest_ip $end_flag\n";
		}
		if ($end_flag eq "ECR") {

# Sometimes argus gets source and dest reversed, so correct everything to one
# format ...

			if ($mid_flag eq "<-") {
				$tmp = $source_ip;
				$source_ip = $dest_ip;
				$dest_ip = $tmp;
				$tmp = $source_net;
				$source_net = $dest_net;
				$dest_net = $tmp;
				$mid_flag = "->";
			}
			$ecr_dest_net_data{"$source_ip $dest_net"} += 1;
			if ($ecr_dest_net_data{"$source_ip $dest_net"} < $SCAN_LIMIT) {
				$ecr_src_data_limited{"$source_ip"} .= "$date $source_ip $mid_flag $dest_ip $end_flag\n";
				$ecr_dest_data_limited{"$dest_ip"} .= "$date $source_ip $mid_flag $dest_ip $end_flag\n";
			}
			$ecr_src_data{"$source_ip"} .= "$date $source_ip $mid_flag $dest_ip $end_flag\n";
			$ecr_dest_data{"$dest_ip"} .= "$date $source_ip $mid_flag $dest_ip $end_flag\n";
		}
	}
}
$end_time = $date;
print "Search for '###' to find next table header, search for '---' for full data\n\n*** Data from $start_time til $end_time\n\n";
print "### Unsolicited echo response by source IP and destination net count summary\n\t(counts greater than SCAN_LIMIT)\n\n";
foreach $source (sort (keys %ecr_dest_net_data)) {
	if ($ecr_dest_net_data{$source} >= $SCAN_LIMIT) {
		($src, $dst) = split(' ',$source);
		write;
	}
}

print "\n### Only echo response data sorted by destination machine less than SCAN_LIMIT\n\n";
foreach $source (sort (keys %ecr_dest_data_limited)) {
	print "$ecr_dest_data_limited{$source}\n";
}
print "\n### Only echo response data sorted by source machine less than SCAN_LIMIT\n\n";
foreach $source (sort (keys %ecr_src_data_limited)) {
	print "$ecr_src_data_limited{$source}\n";
}
print "---\n### Unsolicited echo response by source IP and destination net count summary\n\n";
foreach $source (sort (keys %ecr_dest_net_data)) {
	($src, $dst) = split(' ',$source);
	write;
}
print "\n### Only echo response data sorted by destination machine\n\n";
foreach $source (sort (keys %ecr_dest_data)) {
	print "$ecr_dest_data{$source}\n";
}
print "\n### Only echo response data sorted by source machine\n\n";
foreach $source (sort (keys %ecr_src_data)) {
	print "$ecr_src_data{$source}\n";
}
print "\n### All echo data\n\n";
foreach $source (sort (keys %all_data)) {
	print "$all_data{$source}\n";
}

format STDOUT =
@<<<<<<<<<<<<<<< @<<<<<<<<<<< @>>>>>>>>>>>>>>>>>
$src, $dst, $ecr_dest_net_data{$source}
.


