Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Bug fix for NetAddr::IP usage in --analysis-fields IP search mode

Bug fix in --Analyze mode when IP fields are to be searched with the
--analysis-fields argument (such as --analysis-fields "SRC:1.2.3.4").
The bug was reported by Gregorio Narvaez, and looked like this:

  Use of uninitialized value $_[0] in length at
  ../../blib/lib/NetAddr/IP/UtilPP.pm (autosplit into
  ../../blib/lib/auto/NetAddr/IP/UtilPP/hasbits.al) line 126.
  Use of uninitialized value $_[0] in length at
  ../../blib/lib/NetAddr/IP/UtilPP.pm (autosplit into
  ../../blib/lib/auto/NetAddr/IP/UtilPP/hasbits.al) line 126.
  Bad argument length for NetAddr::IP::UtilPP::hasbits, is 0, should be
  128 at ../../blib/lib/NetAddr/IP/UtilPP.pm (autosplit into
  ../../blib/lib/auto/NetAddr/IP/UtilPP/_deadlen.al) line 122.

Added --stdin argument to allow psad to collect iptables log data from
STDIN in --Analyze mode.
  • Loading branch information...
commit ff46fe12b238b7f7b63b2f31345bb6a8f99f7efe 1 parent 30120fb
@mrash authored
Showing with 289 additions and 15 deletions.
  1. +18 −0 ChangeLog
  2. +45 −14 psad
  3. +6 −0 psad.8
  4. +24 −0 test/install.answers
  5. +196 −1 test/test-psad.pl
View
18 ChangeLog
@@ -1,3 +1,21 @@
+psad-2.2.1 (11/24/2012):
+ - Bug fix in --Analyze mode when IP fields are to be searched with the
+ --analysis-fields argument (such as --analysis-fields "SRC:1.2.3.4").
+ The bug was reported by Gregorio Narvaez, and looked like this:
+
+ Use of uninitialized value $_[0] in length at
+ ../../blib/lib/NetAddr/IP/UtilPP.pm (autosplit into
+ ../../blib/lib/auto/NetAddr/IP/UtilPP/hasbits.al) line 126.
+ Use of uninitialized value $_[0] in length at
+ ../../blib/lib/NetAddr/IP/UtilPP.pm (autosplit into
+ ../../blib/lib/auto/NetAddr/IP/UtilPP/hasbits.al) line 126.
+ Bad argument length for NetAddr::IP::UtilPP::hasbits, is 0, should be
+ 128 at ../../blib/lib/NetAddr/IP/UtilPP.pm (autosplit into
+ ../../blib/lib/auto/NetAddr/IP/UtilPP/_deadlen.al) line 122.
+
+ - Added --stdin argument to allow psad to collect iptables log data from
+ STDIN in --Analyze mode.
+
psad-2.2 (02/20/2012):
- Added support for detection of malicious traffic that is delivered via
IPv6. This is accomplished by parsing ip6tables log messages - these are
View
59 psad
@@ -393,6 +393,7 @@ my $csv_end_line = 0;
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;
@@ -1925,7 +1926,7 @@ sub parse_NF_pkt_str() {
### 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);
}
@@ -7194,13 +7195,20 @@ sub analysis_mode() {
}
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;
}
@@ -7223,8 +7231,10 @@ sub analysis_mode() {
}
}
}
- 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
@@ -7278,18 +7288,20 @@ sub ipt_match_criteria() {
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 [], '';
@@ -7413,7 +7425,7 @@ sub csv_mode() {
}
}
}
- close MSGS;
+ close $fh unless $csv_stdin;
}
if ($gnuplot_mode) {
@@ -8113,6 +8125,7 @@ sub csv_tokens() {
die "[*] $tok_str requires a search criteria in -A mode.";
}
}
+ $search =~ s/\,$//;
if ($token eq 'timestamp') {
$csv_have_timestamp = 1;
}
@@ -8195,10 +8208,27 @@ sub csv_tokens() {
$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";
}
@@ -9825,7 +9855,7 @@ sub get_scale_factor() {
$val = 50000;
}
}
- $val++ if $val == 0;
+ $val++;
return $val;
}
@@ -10509,7 +10539,8 @@ sub getopt_wrapper() {
'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.
View
6 psad.8
@@ -82,6 +82,12 @@ to point psad at your
.I /var/log/messages
file.
.TP
+.BR \-\^\-analysis-fields\ \<search\ fields>
+In --Analyze mode restrict analysis to iptables log messages that have specific
+values for particular fields. Examples include "SRC:1.2.3.4", "DST:10.0.0.0/24,
+and "TTL:64", and multiple fields are supported as a comma-separated list like
+"SRC:1.2.3.4, LEN:44, DST:10.0.0.0/24".
+.TP
.BR \-i "\fR,\fP " \-\^\-interface\ \<interface>
Specify the interface that
.B psad
View
24 test/install.answers
@@ -0,0 +1,24 @@
+Would you like to merge the config from the existing psad installation: y;
+Preserve any user modfications in etc psad signatures: y;
+Preserve any user modfications in etc psad icmp_types: y;
+Preserve any user modfications in etc psad icmp6_types: y;
+Preserve any user modfications in etc psad posf: y;
+Preserve any user modfications in etc psad auto_dl: y;
+Preserve any user modfications in etc psad snort_rule_dl: y;
+Preserve any user modfications in etc psad pf os: y;
+Preserve any user modfications in etc psad ip_options: y;
+Would you like alerts sent to a different address: n;
+Email addresses: root@localhost;
+Would you like psad to only parse specific strings in iptables messages: n;
+First is it ok to leave the HOME_NET setting as any: y;
+Would you like to enable DShield alerts: n;
+Would you like to install the latest signatures from http www cipherdyne org psad signatures: n;
+Enable psad at boot time: n;
+Preserve any user modfications in home mbr git psad git test psad install etc psad signatures: y;
+Preserve any user modfications in home mbr git psad git test psad install etc psad icmp_types: y;
+Preserve any user modfications in home mbr git psad git test psad install etc psad icmp6_types: y;
+Preserve any user modfications in home mbr git psad git test psad install etc psad posf: y;
+Preserve any user modfications in home mbr git psad git test psad install etc psad auto_dl: y;
+Preserve any user modfications in home mbr git psad git test psad install etc psad snort_rule_dl: y;
+Preserve any user modfications in home mbr git psad git test psad install etc psad pf os: y;
+Preserve any user modfications in home mbr git psad git test psad install etc psad ip_options: y;
View
197 test/test-psad.pl
@@ -1,5 +1,6 @@
#!/usr/bin/perl -w
+use Cwd;
use File::Copy;
use File::Path;
use Getopt::Long 'GetOptions';
@@ -11,6 +12,7 @@
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';
@@ -34,7 +36,7 @@
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";
@@ -106,6 +108,16 @@
### 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',
@@ -418,6 +430,146 @@
'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',
@@ -796,6 +948,49 @@ ()
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;
Please sign in to comment.
Something went wrong with that request. Please try again.