From 57a6c16c3bc809e5f5c19ce2aaf4eac5c362740c Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Thu, 2 Aug 2012 12:18:13 -0500 Subject: [PATCH 1/2] Fix #4: Allow osclass within osmatch, from Nmap 6.00 --- Changes | 3 +++ Parser.pm | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index ee4b7f0..e761182 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,9 @@ Latest version: http://code.google.com/p/nmap-parser/ Website: http://anthonypersaud.com/ +Changes for 1.22 + - Allow osclass elements within osmatch, Nmap XML format changed in 6.00 + Changes for 1.21 - Added support for hostscript and script tags - Changed ipv4_sort() to use a 10x faster sort method diff --git a/Parser.pm b/Parser.pm index 37c3980..45c64f4 100644 --- a/Parser.pm +++ b/Parser.pm @@ -5,7 +5,7 @@ use XML::Twig; use Storable qw(dclone); use vars qw($VERSION %D); -$VERSION = 1.21; +$VERSION = 1.22; sub new { @@ -472,6 +472,7 @@ sub __host_os_tag_hdlr { my $portused_tag; my $os_fingerprint; + #if( $D{$$}{SESSION}{xml_version} eq "1.04") if ( defined $os_tag ) { #get the open port used to match os @@ -492,17 +493,30 @@ sub __host_os_tag_hdlr { #This will go in Nmap::Parser::Host::OS my $osmatch_index = 0; + my $osclass_index = 0; for my $osmatch ( $os_tag->children('osmatch') ) { $os_hashref->{osmatch_name}[$osmatch_index] = $osmatch->{att}->{name}; $os_hashref->{osmatch_name_accuracy}[$osmatch_index] = $osmatch->{att}->{accuracy}; $osmatch_index++; + for my $osclass ( $osmatch->children('osclass') ) { + $os_hashref->{osclass_osfamily}[$osclass_index] = + $osclass->{att}->{osfamily}; + $os_hashref->{osclass_osgen}[$osclass_index] = + $osclass->{att}->{osgen}; + $os_hashref->{osclass_vendor}[$osclass_index] = + $osclass->{att}->{vendor}; + $os_hashref->{osclass_type}[$osclass_index] = + $osclass->{att}->{type}; + $os_hashref->{osclass_class_accuracy}[$osclass_index] = + $osclass->{att}->{accuracy}; + $osclass_index++; + } } $os_hashref->{'osmatch_count'} = $osmatch_index; #parse osclass tags - my $osclass_index = 0; for my $osclass ( $os_tag->children('osclass') ) { $os_hashref->{osclass_osfamily}[$osclass_index] = $osclass->{att}->{osfamily}; From 7ccf752af5dcf9e25d9aafeedb93c2d2ca92c22c Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Thu, 24 Jan 2013 17:13:00 -0600 Subject: [PATCH 2/2] Update NSE script element parsing 1. Add parsing of NSE scripts' XML output. Structured output is built into a data structure recursively. 2. Fix a warning: Use of uninitialized value in lc (in `_get_ports`) 2. Document `prescripts` and `postscripts` methods of `Nmap::Parser::Session` 3. Add appropriate tests for script functionality. --- Parser.pm | 107 ++++++++++++++++++++++++++++++++++++++------- t/nmap_results.xml | 57 ++++++++++++++++++++---- t/parser.t | 12 ++--- 3 files changed, 146 insertions(+), 30 deletions(-) diff --git a/Parser.pm b/Parser.pm index 45c64f4..ae4d4db 100644 --- a/Parser.pm +++ b/Parser.pm @@ -249,8 +249,8 @@ sub _prescript_tag_hdlr { my ( $twig, $tag ) = @_; my $scripts_hashref; for my $script ( $tag->children('script') ) { - chomp($scripts_hashref->{ $script->{att}->{id} } = - $script->{att}->{output}); + $scripts_hashref->{ $script->{att}->{id} } = + __script_tag_hdlr( $script ); } $D{$$}{SESSION}{prescript} = $scripts_hashref; $twig->purge; @@ -260,8 +260,8 @@ sub _postscript_tag_hdlr { my ( $twig, $tag ) = @_; my $scripts_hashref; for my $script ( $tag->children('script') ) { - chomp($scripts_hashref->{ $script->{att}->{id} } = - $script->{att}->{output}); + $scripts_hashref->{ $script->{att}->{id} } = + __script_tag_hdlr( $script ); } $D{$$}{SESSION}{postscript} = $scripts_hashref; $twig->purge; @@ -458,8 +458,8 @@ sub __host_script_tag_hdlr { my $script_hashref; for ( $tag->children('script') ) { - chomp($script_hashref->{ $_->{att}->{id} } = - $_->{att}->{output}); + $script_hashref->{ $_->{att}->{id} } = + __script_tag_hdlr($_); } return $script_hashref; @@ -593,8 +593,8 @@ sub __host_hostscript_tag_hdlr { my $scripts_hashref; return undef unless ($scripts); for my $script ( $scripts->children('script') ) { - chomp($scripts_hashref->{ $script->{att}->{id} } = - $script->{att}->{output}); + $scripts_hashref->{ $script->{att}->{id} } = + __script_tag_hdlr( $script ); } return $scripts_hashref; } @@ -654,6 +654,52 @@ sub __host_trace_error_tag_hdlr { return; } +sub __script_tag_hdlr { + my $tag = shift; + my $script_hashref = { + output => $tag->{att}->{output} + }; + chomp %$script_hashref; + if ( not $tag->is_empty()) { + $script_hashref->{contents} = __script_table($tag); + } + return $script_hashref; +} + +sub __script_table { + my $tag = shift; + my ($ref, $subref); + my $fc = $tag->first_child(); + if ($fc) { + if ($fc->is_text) { + $ref = $fc->text; + } + else { + if ($fc->{att}->{key}) { + $ref = {}; + $subref = sub { + $ref->{$_->{att}->{key}} = shift; + }; + } + else { + $ref = []; + $subref = sub { + push @$ref, shift; + }; + } + for ($tag->children()) { + if ($_->tag() eq "table") { + $subref->(__script_table( $_ )); + } + else { + $subref->($_->text); + } + } + } + } + return $ref +} + #/*****************************************************************************/ # NMAP::PARSER::SESSION #/*****************************************************************************/ @@ -812,14 +858,17 @@ sub _del_port { sub _get_ports { my $self = shift; my $proto = pop; #param might be empty, so this goes first - my $state = lc(shift); #open, filtered, closed or any combination + my $state = shift; #open, filtered, closed or any combination my @matched_ports = (); - #if $state eq '', then tcp_ports or udp_ports was called for all ports + #if $state is undef, then tcp_ports or udp_ports was called for all ports #therefore, only return the keys of all ports found - if ( $state eq '' ) { + if ( not defined $state ) { return sort { $a <=> $b } ( keys %{ $self->{ports}{$proto} } ); } + else { + $state = lc($state) + } #the port parameter can be set to either any of these also 'open|filtered' #can count as 'open' and 'filetered'. Therefore I need to use a regex from now on @@ -1308,6 +1357,28 @@ Returns the human readable format of the finish time. Returns the version of nmap xml file. +=item B + +=item B + +A basic call to prescripts() returns a list of the names of the NSE scripts +run in the pre-scanning phase. If C<$name> is given, it returns the text output of the +a reference to a hash with "output" and "content" keys for the +script with that name, or undef if that script was not run. +The value of the "output" key is the text output of the script. The value of the +"content" key is a data structure based on the XML output of the NSE script. + +=item B + +=item B + +A basic call to postscripts() returns a list of the names of the NSE scripts +run in the post-scaning phase. If C<$name> is given, it returns the text output of the +a reference to a hash with "output" and "content" keys for the +script with that name, or undef if that script was not run. +The value of the "output" key is the text output of the script. The value of the +"content" key is a data structure based on the XML output of the NSE script. + =back =head2 Nmap::Parser::Host @@ -1446,8 +1517,11 @@ when the scan was performed. =item B A basic call to hostscripts() returns a list of the names of the host scripts -run. If C<$name> is given, it returns the text output of the script with that -name, or undef if that script was not run. +run. If C<$name> is given, it returns the text output of the +a reference to a hash with "output" and "content" keys for the +script with that name, or undef if that script was not run. +The value of the "output" key is the text output of the script. The value of the +"content" key is a data structure based on the XML output of the NSE script. =item B @@ -1578,9 +1652,12 @@ Returns the version of the given product of the running service. =item B -A basic call to scripts() returns a list of the names of the scripts -run for this port. If C<$name> is given, it returns the text output of the +A basic call to scripts() returns a list of the names of the NSE scripts +run for this port. If C<$name> is given, it returns +a reference to a hash with "output" and "content" keys for the script with that name, or undef if that script was not run. +The value of the "output" key is the text output of the script. The value of the +"content" key is a data structure based on the XML output of the NSE script. =back diff --git a/t/nmap_results.xml b/t/nmap_results.xml index 6666883..a23264a 100644 --- a/t/nmap_results.xml +++ b/t/nmap_results.xml @@ -1,6 +1,6 @@ - + @@ -77,11 +77,13 @@ - - - - - + + + + + cpe:/o:linux:linux_kernel:2.6 + cpe:/o:linux:linux_kernel:3 + @@ -120,8 +122,20 @@ - @@ -190,7 +204,32 @@ - diff --git a/t/parser.t b/t/parser.t index 041aa58..5f4dc06 100644 --- a/t/parser.t +++ b/t/parser.t @@ -110,7 +110,7 @@ sub session_test { is( $session->finish_time(), 1057088900, 'Session: finish_time' ); is( $session->time_str(), 'Tue Jul 1 14:48:20 2003', 'Session: time_str' ); is( $session->nmap_version(), '3.80', 'Session: nmap_version' ); - is( $session->xml_version(), '1.01', 'Session: xml_version' ); + is( $session->xml_version(), '1.04', 'Session: xml_version' ); is( $session->scan_args(), 'nmap -v -v -v -oX test.xml -O -sTUR -p 1-1023 127.0.0.[1-4]', @@ -126,7 +126,7 @@ sub session_test { [ $session->prescripts ], [ 'broadcast-dropbox-listener' ], 'Session has prescripts' ); - like( $session->prescripts('broadcast-dropbox-listener'), + like( $session->prescripts('broadcast-dropbox-listener')->{output}, qr/\ndisplayname .* 10422330$/s, 'Prescript output correct' ); @@ -134,7 +134,7 @@ sub session_test { [ $session->postscripts ], [ 'ssh-hostkey' ], 'Session has postscripts' ); - like( $session->postscripts('ssh-hostkey'), + like( $session->postscripts('ssh-hostkey')->{output}, qr/Possible .* 192.168.1.124$/s, 'Postscript output correct' ); } @@ -320,8 +320,8 @@ sub host_3 { is( $svc->extrainfo, 'protocol 1.99', 'TCP Service: extrainfo' ); is_deeply( [ $svc->scripts() ], [ 'ssh-hostkey' ], 'Port has scripts' ); { - my $output = $svc->scripts('ssh-hostkey'); - like( $output, qr/^1024 e8:2.*RSA\)$/s, 'Script output ok'); + my $output = $svc->scripts('ssh-hostkey')->{output}; + like( $output, qr/^1024 .* \(RSA\)$/s, 'Script output ok'); } isa_ok( @@ -374,7 +374,7 @@ sub host_3 { [ 'nbstat' ], 'Host3 has one hostscript' ); { - my $output = $host->hostscripts('nbstat'); + my $output = $host->hostscripts('nbstat')->{output}; is( substr($output,0,16), "\n NetBIOS name:", 'Host3 hostscript correct' ); }