Skip to content

Commit

Permalink
Update NSE script element parsing
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bonsaiviking committed Jan 24, 2013
1 parent 57a6c16 commit 7ccf752
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 30 deletions.
107 changes: 92 additions & 15 deletions Parser.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
#/*****************************************************************************/
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1308,6 +1357,28 @@ Returns the human readable format of the finish time.
Returns the version of nmap xml file.
=item B<prescripts()>
=item B<prescripts($name)>
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<postscripts()>
=item B<postscripts($name)>
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
Expand Down Expand Up @@ -1446,8 +1517,11 @@ when the scan was performed.
=item B<hostscripts($name)>
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<tcp_ports()>
Expand Down Expand Up @@ -1578,9 +1652,12 @@ Returns the version of the given product of the running service.
=item B<scripts($name)>
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
Expand Down
57 changes: 48 additions & 9 deletions t/nmap_results.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!-- nmap 3.27 scan initiated Tue Jul 1 14:48:03 2003 as: nmap -v -v -v -oX test.xml -O -sTUR -p 1-1023 localhost -->
<nmaprun scanner="nmap" args="nmap -v -v -v -oX test.xml -O -sTUR -p 1-1023 127.0.0.[1-4]" start="1057088883" startstr="Tue Jul 1 14:48:03 2003" version="3.80" xmloutputversion="1.01">
<nmaprun scanner="nmap" args="nmap -v -v -v -oX test.xml -O -sTUR -p 1-1023 127.0.0.[1-4]" start="1057088883" startstr="Tue Jul 1 14:48:03 2003" version="3.80" xmloutputversion="1.04">
<scaninfo type="connect" protocol="tcp" numservices="1023" services="1-1023"/>
<scaninfo type="udp" protocol="udp" numservices="1023" services="1-1023"/>
<verbose level="3"/>
Expand Down Expand Up @@ -77,11 +77,13 @@
<service name="rpcbind" proto="rpc" rpcnum="100000" lowver="2" highver="2" method="detection" conf="5"/>
</port>
</ports>
<os>
<portused state="open" proto="tcp" portid="22"/>
<portused state="closed" proto="tcp" portid="1"/>
<osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.4.x" accuracy="100"/>
<osmatch name="Linux Kernel 2.4.0 - 2.5.20" accuracy="100"/>
<os><portused state="open" proto="tcp" portid="22"/>
<portused state="closed" proto="tcp" portid="1"/>
<portused state="closed" proto="udp" portid="31994"/>
<osmatch name="Linux 2.6.38 - 3.0" accuracy="100" line="43893">
<osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.6.X" accuracy="100"><cpe>cpe:/o:linux:linux_kernel:2.6</cpe></osclass>
<osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="3.X" accuracy="100"><cpe>cpe:/o:linux:linux_kernel:3</cpe></osclass>
</osmatch>
<osfingerprint fingerprint=" SEQ(SP=C5%GCD=1%ISR=C7%TI=Z%II=I%TS=8) ECN(R=Y%DF=Y%T=40%W=16D0%O=M5B4NNSNW2%CC=N%Q=) T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=) T2(R=N) T3(R=Y%DF=Y%T=40%W=16A0%S=O%A=S+%F=AS%O=M5B4ST11NW2%RD=0%Q=) T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=) T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=) U1(R=Y%DF=N%T=40%TOS=C0%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUL=G%RUD=G) IE(R=Y%DFI=N%T=40%TOSI=S%CD=S%SI=S%DLI=S) "/>
</os>
<uptime seconds="1973" lastboot="Tue Jul 1 14:15:27 2003"/>
Expand Down Expand Up @@ -120,8 +122,20 @@
<state state="open"/>
<owner name="root"/>
<service name="ssh" product="OpenSSH" version="3.4p1" extrainfo="protocol 1.99" method="probed" conf="10"/>
<script id="ssh-hostkey" output="1024 e8:2e:5e:dd:03:6b:7c:0a:ed:bb:a0:b1:00:75:22:d4 (DSA)&#xa;2048 a9:dd:cb:b1:29:5e:ec:2c:98:22:3b:b4:34:20:a8:e1 (RSA)"/>
</port>
<script id="ssh-hostkey" output="1024 8d:60:f1:7c:ca:b7:3d:0a:d6:67:54:9d:69:d9:b9:dd (DSA)&#xa;2048 79:f8:09:ac:d4:e2:32:42:10:49:d3:bd:20:82:85:ec (RSA)"><table>

<elem key="key">AAAAB3NzaC1kc3MAAACBAKD2mBsTTPspSqp/bnnX3RTQ/wwjJATJkr+uRLEGaMIfMpiajcj5P5ZOig/XS+/axlBtg9T810QkPr3JlPbB/mItY8EVxTbG82yQP8mus1hdr9FcNXHZtPJTAAaAd1v9W1oV6oQN4dtS6JjZwnkIR+p75CuVjoR1jvEAg1c9dUeLAAAAFQCi9Z+Rm1ukDp+xcAFo7Kf7o1OLNwAAAIBx2KL29trebdj6G+IzB7eUUTNCFqGT0Tvcq/mapkjumwmZt1ZJgUdK+f2BFLkQNqXPY02SQBsK/pOfinfhAIebMN4Sp+AleQvF1zIbgBoD4Kr6BRm7Lx5ftqv8tdwBNk0izhBYsc7opuNu35LppocdBESu4Y/4NuIxVk2kI2GXvgAAAIBlY/K4R+SmZAkt9zZyx8dIH9anrZbNBzrT2tK4eXItjXlv4XvIx0wQJqV0CTmJk8tlxTQREnDwsXdhE8hd7oPFoKaGGT3UajkBVarLA/qK+q4pAfbl8VmHTnxvqluCPEXFWaJSGyoYQqKaKLczcjBfQiIpl4vz0oJ/5OCYjwcxkQ==</elem>
<elem key="fingerprint">8d60f17ccab73d0ad667549d69d9b9dd</elem>
<elem key="type">ssh-dss</elem>
<elem key="bits">1024</elem>
</table>
<table>
<elem key="key">AAAAB3NzaC1yc2EAAAABIwAAAQEAwVKoTY/7GFG7BmKkG6qFAHY/f3ciDX2MXTBLMEJP0xyUJsoy/CVRYw2b4qUB/GCJ5lh2InP+LVnPD3ZdtpyIvbS0eRZs/BH+mVLGh9xA/wOEUiiCfzQRsHj1xn7cqeWViAzQtdGluk/5CVAvr1FU3HNaaWkg7KQOSiKAzgDwCBtQhlgI40xdXgbqMkrHeP4M1p4MxoEVpZMe4oObACWwazeHP/Xas1vy5rbnmE59MpEZaA8t7AfGlW4MrVMhAB1JsFMdd0qFLpy/l93H3ptSlx1+6PQ5gUyjhmDUjMR+k6fb0yOeGdOrjN8IrWPmebZRFBjK5aCJwubgY/03VsSBMQ==</elem>
<elem key="fingerprint">79f809acd4e232421049d3bd208285ec</elem>
<elem key="type">ssh-rsa</elem>
<elem key="bits">2048</elem>
</table>
</script></port>
<port protocol="tcp" portid="25">
<state state="open"/>
<owner name="root"/>
Expand Down Expand Up @@ -190,7 +204,32 @@
<hop ttl="4" rtt="26.48" ipaddr="1.1.1.1" host="www.straton-it.fr"/>
</trace>
</host>
<postscript><script id="ssh-hostkey" output="Possible duplicate hosts&#xa;Key 2048 7b:b2:ce:9a:84:00:57:68:a5:52:dd:e8:0e:1b:b1:b7 (RSA) used by:&#xa; 192.168.1.107&#xa; 192.168.1.81&#xa; 192.168.1.124&#xa;Key 1024 84:7d:33:d3:9a:51:3a:2b:09:6b:49:bb:46:12:03:02 (DSA) used by:&#xa; 192.168.1.107&#xa; 192.168.1.81&#xa; 192.168.1.124"/></postscript>
<postscript><script id="ssh-hostkey" output="Possible duplicate hosts&#xa;Key 2048 7b:b2:ce:9a:84:00:57:68:a5:52:dd:e8:0e:1b:b1:b7 (RSA) used by:&#xa; 192.168.1.107&#xa; 192.168.1.81&#xa; 192.168.1.124&#xa;Key 1024 84:7d:33:d3:9a:51:3a:2b:09:6b:49:bb:46:12:03:02 (DSA) used by:&#xa; 192.168.1.107&#xa; 192.168.1.81&#xa; 192.168.1.124">
<table>
<table key="hosts">
<elem>192.168.1.107</elem>
<elem>192.168.1.81</elem>
<elem>192.168.1.124</elem>
</table>
<table key="key">
<elem key="fingerprint">7bb2ce9a84005768a552dde80e1bb1b7</elem>
<elem key="bits">2048</elem>
<elem key="type">ssh-rsa</elem>
</table>
</table>
<table>
<table key="hosts">
<elem>192.168.1.107</elem>
<elem>192.168.1.81</elem>
<elem>192.168.1.124</elem>
</table>
<table key="key">
<elem key="fingerprint">847d33d39a513a2b096b49bb46120302</elem>
<elem key="bits">1024</elem>
<elem key="type">ssh-dss</elem>
</table>
</table>
</script></postscript>
<runstats>
<finished time="1057088900" timestr="Tue Jul 1 14:48:20 2003"/>
<hosts up="1" down="0" total="1"/>
Expand Down
12 changes: 6 additions & 6 deletions t/parser.t
Original file line number Diff line number Diff line change
Expand Up @@ -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]',
Expand All @@ -126,15 +126,15 @@ 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' );

is_deeply(
[ $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' );
}
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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' );
}
Expand Down

0 comments on commit 7ccf752

Please sign in to comment.