my $csv_regex = '';
my $csv_neg_regex = '';
my $csv_have_timestamp = 0;
+my $pkts_from_stdin = 0;
my $dump_ipt_policy = 0;
my $fw_include_ips = 0;
my $benchmark = 0;
### we are looking to analyze packets from a specific IP/subnet
if ($pkt_hr->{'is_ipv6'}) {
if ($restrict_ip->version() == 6) {
- return $PKT_IGNORE unless
+ return $PKT_IGNORE unless
$pkt_hr->{'s_obj'}->within($restrict_ip) or
$pkt_hr->{'d_obj'}->within($restrict_ip);
}
}
print "[+] Entering analysis mode. Parsing $fw_data_file\n";
- open MSGS, "< $fw_data_file" or die "[*] Could not open ",
- "$fw_data_file: $!";
- my @lines = <MSGS>;
- close MSGS;
+ my $fh = '';
+ if ($pkts_from_stdin) {
+ $fh = *STDIN;
+ } else {
+ open MSGS, "< $fw_data_file" or die "[*] Could not open ",
+ "$fw_data_file: $!";
+ $fh = *MSGS;
+ }
my @ipt_msgs = ();
my $pkt_ctr = 0;
- PKT: for my $line (@lines) {
+ my $line_ctr = 0;
+ PKT: while (<$fh>) {
+ my $line = $_;
+ $line_ctr++;
if ($num_packets > 0) {
last PKT if $pkt_ctr >= $num_packets;
}
}
}
}
- print "[+] Found ", ($#ipt_msgs+1), " iptables log messages out of ",
- ($#lines+1), " total lines.\n";
+ close $fh unless $pkts_from_stdin;
+
+ print "[+] Found ", ($#ipt_msgs+1), " iptables log messages out of " .
+ "$line_ctr total lines.\n";
print " This may take a while...\n" if $#ipt_msgs > 15000;
### analyze all packets
return [], '' unless $pkt_hr->{$tok} =~ m|$match_hr->{'re'}|;
}
} elsif (defined $match_hr->{'net'} or defined $match_hr->{'ip'}) {
+ my $net_or_ip_key = 'ip_obj';
+ $net_or_ip_key = 'net_obj' if defined $match_hr->{'net'};
if ($pkt_hr->{$tok} =~ m|$ipv4_re|
or $pkt_hr->{$tok} =~ m|$ipv6_re|) {
my $ip_match_obj = '';
if ($tok eq 'src') {
$ip_match_obj = $pkt_hr->{'s_obj'};
} elsif ($tok eq 'dst') {
- $ip_match_obj = $pkt_hr->{'s_obj'};
+ $ip_match_obj = $pkt_hr->{'d_obj'};
}
if ($match_hr->{'negate'}) {
- return [], '' if $ip_match_obj->within($match_hr->{'net'});
+ return [], '' if $ip_match_obj->within($match_hr->{$net_or_ip_key});
} else {
- return [], '' unless $ip_match_obj->within($match_hr->{'net'});
+ return [], '' unless $ip_match_obj->within($match_hr->{$net_or_ip_key});
}
} else {
return [], '';
}
}
}
- close MSGS;
+ close $fh unless $csv_stdin;
}
if ($gnuplot_mode) {
die "[*] $tok_str requires a search criteria in -A mode.";
}
}
+ $search =~ s/\,$//;
if ($token eq 'timestamp') {
$csv_have_timestamp = 1;
}
$search_hsh{'str'} = $1;
} elsif ($search =~ m|^$ipv4_re/$ipv4_re$|) {
$search_hsh{'net'} = $search;
+ $search_hsh{'net_obj'} = new NetAddr::IP($search)
+ or die "[*] NetAddr::IP($search) error";
} elsif ($search =~ m|^$ipv4_re/\d+$|) {
$search_hsh{'net'} = $search;
+ $search_hsh{'net_obj'} = new NetAddr::IP($search)
+ or die "[*] NetAddr::IP($search) error";
} elsif ($search =~ m|^$ipv4_re$|) {
$search_hsh{'ip'} = $search;
+ $search_hsh{'ip_obj'} = new NetAddr::IP($search)
+ or die "[*] NetAddr::IP($search) error";
+ } elsif ($search =~ m|\:|) {
+ ### see if this is an IPv6 address
+ if ($search =~ m|\/|) {
+ $search_hsh{'net'} = $search;
+ $search_hsh{'net_obj'} = new6 NetAddr::IP($search)
+ or die "[*] NetAddr::IP($search) error";
+ } else {
+ $search_hsh{'net'} = $search;
+ $search_hsh{'net_obj'} = new6 NetAddr::IP($search)
+ or die "[*] NetAddr::IP($search) error";
+ }
} else {
die "[*] Unrecognized value for $token";
}
$val = 50000;
}
}
- $val++ if $val == 0;
+ $val++;
return $val;
}
'analysis-fields=s' => \$analysis_fields, # Place a criteria on various fields
# that are parsed from an iptables
# logfile.
- 'analyze-fields=s' => \$analysis_fields,
+ 'analyze-fields=s' => \$analysis_fields, # Synonym.
+ 'stdin' => \$pkts_from_stdin,
'whois-analysis' => \$analysis_whois, # Issue whois lookups in analysis
# mode.
'dns-analysis' => \$enable_analysis_dns, # Issue DNS lookups in -A mode.
#!/usr/bin/perl -w
+use Cwd;
use File::Copy;
use File::Path;
use Getopt::Long 'GetOptions';
my $conf_dir = 'conf';
my $run_dir = 'run';
my $scans_dir = 'scans';
+my $test_install_dir = 'psad-install';
my $syn_scan_file = 'syn_scan_1000_1500';
my $fin_scan_file = 'fin_scan_1000_1150';
my $xmas_scan_file = 'xmas_scan_1000_1150';
my $dl5_ipv4_subnet_auto_dl_file_tcp = "$conf_dir/auto_dl_5_192.168.10.0_24_tcp";
my $dl5_ipv4_subnet_auto_dl_file_udp = "$conf_dir/auto_dl_5_192.168.10.0_24_udp";
-my $psadCmd = 'psad-install/usr/sbin/psad';
+my $psadCmd = "$test_install_dir/usr/sbin/psad";
my $cmd_out_tmp = 'cmd.out';
my $default_conf = "$conf_dir/default_psad.conf";
### define all tests
my @tests = (
{
+ 'category' => 'install',
+ 'detail' => "test directory: $test_install_dir",
+ 'err_msg' => 'could not install',
+ 'function' => \&install_test_dir,
+ 'cmdline' => "./install.pl --install-test-dir --Use-answers " .
+ "--answers-file test/install.answers",
+ 'exec_err' => $NO,
+ 'fatal' => $YES
+ },
+ {
'category' => 'compilation',
'detail' => 'psad compiles',
'err_msg' => 'could not compile',
'exec_err' => $NO,
'fatal' => $NO
},
+ {
+ 'category' => 'operations',
+ 'detail' => 'ignore src via --analysis-fields SRC:1.2.3.4',
+ 'err_msg' => 'did not ignore SRC:1.2.3.4',
+ 'positive_output_matches' => [
+ qr/Level 1\: 0 IP addresses/,
+ qr/Level 2\: 0 IP addresses/,
+ qr/Level 3\: 0 IP addresses/,
+ qr/Level 4\: 0 IP addresses/,
+ qr/Level 5\: 0 IP addresses/,
+ ],
+ 'match_all' => $MATCH_ALL_RE,
+ 'function' => \&generic_exec,
+ 'cmdline' => "$psadCmd --test-mode -A --analysis-fields SRC:1.2.3.4 " .
+ "-m $scans_dir/" . &fw_type() . "/$syn_scan_file -c $default_conf $normal_root_override_str",
+ 'exec_err' => $NO,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'operations',
+ 'detail' => 'match src via --analysis-fields SRC:192.168.10.55',
+ 'err_msg' => 'did not match SRC:192.168.10.55',
+ 'positive_output_matches' => [
+ qr/Top\s\d+\sattackers/i,
+ qr/scanned\sports.*?1000\-1500\b/i,
+ qr/Source\sOS/i, qr/BACKDOOR/i,
+ qr/IP\sstatus/i,
+ qr/192\.168\.10\.55/
+ ],
+ 'match_all' => $MATCH_ALL_RE,
+ 'function' => \&generic_exec,
+ 'cmdline' => "$psadCmd --test-mode -A --analysis-fields SRC:192.168.10.55 " .
+ "-m $scans_dir/" . &fw_type() . "/$syn_scan_file -c $default_conf $normal_root_override_str",
+ 'exec_err' => $NO,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'operations',
+ 'detail' => 'ignore src via --analysis-fields DST:1.2.3.4',
+ 'err_msg' => 'did not ignore DST:1.2.3.4',
+ 'positive_output_matches' => [
+ qr/Level 1\: 0 IP addresses/,
+ qr/Level 2\: 0 IP addresses/,
+ qr/Level 3\: 0 IP addresses/,
+ qr/Level 4\: 0 IP addresses/,
+ qr/Level 5\: 0 IP addresses/,
+ ],
+ 'match_all' => $MATCH_ALL_RE,
+ 'function' => \&generic_exec,
+ 'cmdline' => "$psadCmd --test-mode -A --analysis-fields DST:1.2.3.4 " .
+ "-m $scans_dir/" . &fw_type() . "/$syn_scan_file -c $default_conf $normal_root_override_str",
+ 'exec_err' => $NO,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'operations',
+ 'detail' => 'match src via --analysis-fields DST:192.168.10.1',
+ 'err_msg' => 'did not match DST:192.168.10.1',
+ 'positive_output_matches' => [
+ qr/Top\s\d+\sattackers/i,
+ qr/scanned\sports.*?1000\-1500\b/i,
+ qr/Source\sOS/i, qr/BACKDOOR/i,
+ qr/IP\sstatus/i,
+ qr/192\.168\.10\.55/
+ ],
+ 'match_all' => $MATCH_ALL_RE,
+ 'function' => \&generic_exec,
+ 'cmdline' => "$psadCmd --test-mode -A --analysis-fields DST:192.168.10.1 " .
+ "-m $scans_dir/" . &fw_type() . "/$syn_scan_file -c $default_conf $normal_root_override_str",
+ 'exec_err' => $NO,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'operations',
+ 'detail' => '--analysis-fields SRC:192.168.10.55, DST:192.168.10.1',
+ 'err_msg' => 'did not match SRC:192.168.10.55, DST:192.168.10.1',
+ 'positive_output_matches' => [
+ qr/Top\s\d+\sattackers/i,
+ qr/scanned\sports.*?1000\-1500\b/i,
+ qr/Source\sOS/i, qr/BACKDOOR/i,
+ qr/IP\sstatus/i,
+ qr/192\.168\.10\.55/
+ ],
+ 'match_all' => $MATCH_ALL_RE,
+ 'function' => \&generic_exec,
+ 'cmdline' => qq|$psadCmd --test-mode -A --analysis-fields "SRC:192.168.10.55, DST:192.168.10.1" | .
+ "-m $scans_dir/" . &fw_type() . "/$syn_scan_file -c $default_conf $normal_root_override_str",
+ 'exec_err' => $NO,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'operations',
+ 'detail' => 'ignore length via --analysis-fields LEN:15',
+ 'err_msg' => 'did not ignore LEN:15',
+ 'positive_output_matches' => [
+ qr/Level 1\: 0 IP addresses/,
+ qr/Level 2\: 0 IP addresses/,
+ qr/Level 3\: 0 IP addresses/,
+ qr/Level 4\: 0 IP addresses/,
+ qr/Level 5\: 0 IP addresses/,
+ ],
+ 'match_all' => $MATCH_ALL_RE,
+ 'function' => \&generic_exec,
+ 'cmdline' => "$psadCmd --test-mode -A --analysis-fields LEN:15 " .
+ "-m $scans_dir/" . &fw_type() . "/$syn_scan_file -c $default_conf $normal_root_override_str",
+ 'exec_err' => $NO,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'operations',
+ 'detail' => 'match length via --analysis-fields LEN:44',
+ 'err_msg' => 'did not match LEN:44',
+ 'positive_output_matches' => [
+ qr/Top\s\d+\sattackers/i,
+ qr/scanned\sports.*?1000\-1500\b/i,
+ qr/Source\sOS/i, qr/BACKDOOR/i,
+ qr/IP\sstatus/i,
+ qr/192\.168\.10\.55/
+ ],
+ 'match_all' => $MATCH_ALL_RE,
+ 'function' => \&generic_exec,
+ 'cmdline' => "$psadCmd --test-mode -A --analysis-fields LEN:44 " .
+ "-m $scans_dir/" . &fw_type() . "/$syn_scan_file -c $default_conf $normal_root_override_str",
+ 'exec_err' => $NO,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'operations',
+ 'detail' => 'invalid --analysis-fields BOGUS:44',
+ 'err_msg' => 'allowed BOGUS:44',
+ 'positive_output_matches' => [
+ qr/valid fields are/
+ ],
+ 'match_all' => $MATCH_ALL_RE,
+ 'function' => \&generic_exec,
+ 'cmdline' => "$psadCmd --test-mode -A --analysis-fields BOGUS:44 " .
+ "-m $scans_dir/" . &fw_type() . "/$syn_scan_file -c $default_conf $normal_root_override_str",
+ 'exec_err' => $YES,
+ 'fatal' => $NO
+ },
{
'category' => 'operations',
return $rv;
}
+sub install_test_dir() {
+ my $test_hr = shift;
+
+ my $rv = 1;
+ my $curr_pwd = cwd() or die $!;
+
+ if (-d $test_install_dir) {
+ rmtree $test_install_dir or die $!;
+ }
+ mkdir $test_install_dir or die $!;
+
+ chdir '..' or die $!;
+
+ my $exec_rv = &run_cmd($test_hr->{'cmdline'},
+ "test/$cmd_out_tmp", "test/$current_test_file");
+
+ if ($test_hr->{'exec_err'} eq $YES) {
+ $rv = 0 if $exec_rv;
+ } elsif ($test_hr->{'exec_err'} eq $NO) {
+ $rv = 0 unless $exec_rv;
+ } else {
+ $rv = 1;
+ }
+
+ if ($test_hr->{'positive_output_matches'}) {
+ $rv = 0 unless &file_find_regex(
+ $test_hr->{'positive_output_matches'},
+ $test_hr->{'match_all'},
+ $current_test_file);
+ }
+
+ if ($test_hr->{'negative_output_matches'}) {
+ $rv = 0 if &file_find_regex(
+ $test_hr->{'negative_output_matches'},
+ $test_hr->{'match_all'},
+ $current_test_file);
+ }
+
+ chdir $curr_pwd or die $!;
+
+ return $rv;
+}
+
sub generic_exec() {
my $test_hr = shift;