1package Nmap::Parser; 2 3use strict; 4use XML::Twig; 5 6our $VERSION = 1.37; 7 8 9sub new { 10 11 my ( $class, $self ) = shift; 12 $class = ref($class) || $class; 13 14 $self->{twig} = new XML::Twig( 15 start_tag_handlers => { nmaprun => sub {$self->_nmaprun_start_tag_hdlr(@_)} }, 16 17 twig_roots => { 18 scaninfo => sub {$self->_scaninfo_tag_hdlr(@_)}, 19 prescript => sub {$self->_prescript_tag_hdlr(@_)}, 20 postscript => sub {$self->_postscript_tag_hdlr(@_)}, 21 finished => sub {$self->_finished_tag_hdlr(@_)}, 22 host => sub {$self->_host_tag_hdlr(@_)}, 23 }, 24 ignore_elts => { 25 addport => 1, 26 debugging => 1, 27 verbose => 1, 28 hosts => 1, 29 taskbegin => 1, 30 taskend => 1, 31 taskprogress => 1 32 } 33 ); 34 35 bless( $self, $class ); 36 return $self; 37} 38 39#/*****************************************************************************/ 40# NMAP::PARSER OBJECT METHODS 41#/*****************************************************************************/ 42 43#Safe parse and parsefile will return $@ which will contain the error 44#that occured if the parsing failed (it might be empty when no error occurred) 45 46sub callback { 47 my $self = shift; 48 my $callback = shift; #first arg is CODE 49 if ( ref($callback) eq 'CODE' ) { 50 $self->{callback}{coderef} = $callback; 51 $self->{callback}{is_registered} = 1; 52 } 53 else { 54 $self->{callback}{is_registered} = 0; 55 } 56 57 #returns if a callback is registered or not 58 return $self->{callback}{is_registered}; 59} 60 61sub _parse { 62 my $type = shift; 63 my $self = shift; 64 $self->{HOSTS} = undef; 65 $self->{SESSION} = undef; 66 { 67 file => sub { $self->{twig}->safe_parsefile(@_); }, 68 string => sub { $self->{twig}->safe_parse(@_); }, 69 }->{$type}->(@_); 70 if ($@) { die $@; } 71 $self->purge; 72 return $self; 73} 74 75sub parse { 76 return _parse('string', @_); 77} 78 79sub parsefile { 80 return _parse('file', @_); 81} 82 83sub parsescan { 84 my $self = shift; 85 my $nmap = shift; 86 my $args = shift; 87 my @ips = @_; 88 my $FH; 89 90 if ( $args =~ /-o(?:X|N|G)/ ) { 91 die 92"[Nmap-Parser] Cannot pass option '-oX', '-oN' or '-oG' to parsecan()"; 93 } 94 95 my $cmd; 96 97#if output file is defined, point it to a localfile then call parsefile instead. 98 if ( defined( $self->{cache_file} ) ) { 99 $cmd = 100 "$nmap $args -v -v -v -oX " 101 . $self->{cache_file} . " " 102 . ( join ' ', @ips ); 103 `$cmd`; #remove output from STDOUT 104 $self->parsefile( $self->{cache_file} ); 105 } 106 else { 107 $cmd = "$nmap $args -v -v -v -oX - " . ( join ' ', @ips ); 108 open $FH, 109 "$cmd |" || die "[Nmap-Parser] Could not perform nmap scan - $!"; 110 $self->parse($FH); 111 close $FH; 112 } 113 114 return $self; 115} 116 117sub cache_scan { 118 my $self = shift; 119 $self->{cache_file} = shift || 'nmap-parser-cache.' . time() . '.xml'; 120} 121 122sub purge { 123 my $self = shift; 124 $self->{twig}->purge; 125 return $self; 126} 127 128sub addr_sort { 129 my $self = shift if ref $_[0]; 130 131 return ( 132 map { unpack("x16A*", $_) } 133 sort { $a cmp $b } 134 map { 135 my @vals; 136 if( /:/ ) { #IPv6 137 @vals = split /:/; 138 @vals = map { $_ eq '' ? (0) x (8-$#vals) : hex } @vals 139 } else { #IPv4 140 my @v4 = split /\./; 141 # Sort as IPv4-mapped IPv6, per RFC 4291 Section 2.5.5.2 142 @vals = ( (0) x 5, 0xffff, map { 256*$v4[$_] + $v4[$_+1] } (0,2) ); 143 } 144 pack("n8A*", @vals, $_) 145 } @_ 146 ); 147} 148 149#MAIN SCAN INFORMATION 150sub get_session { 151 my $self = shift; 152 my $obj = Nmap::Parser::Session->new( $self->{SESSION} ); 153 return $obj; 154} 155 156#HOST STUFF 157sub get_host { 158 my ( $self, $ip ) = (@_); 159 if ( $ip eq '' ) { 160 warn "[Nmap-Parser] No IP address given to get_host()\n"; 161 return undef; 162 } 163 $self->{HOSTS}{$ip}; 164} 165 166sub del_host { 167 my ( $self, $ip ) = (@_); 168 if ( $ip eq '' ) { 169 warn "[Nmap-Parser] No IP address given to del_host()\n"; 170 return undef; 171 } 172 delete $self->{HOSTS}{$ip}; 173} 174 175sub all_hosts { 176 my $self = shift; 177 my $status = shift || ''; 178 179 return ( values %{ $self->{HOSTS} } ) if ( $status eq '' ); 180 181 my @hosts = grep { $_->{status} eq $status } ( values %{ $self->{HOSTS} } ); 182 return @hosts; 183} 184 185sub get_ips { 186 my $self = shift; 187 my $status = shift || ''; 188 189 return $self->addr_sort( keys %{ $self->{HOSTS} } ) if ( $status eq '' ); 190 191 my @hosts = 192 grep { $self->{HOSTS}{$_}{status} eq $status } 193 ( keys %{ $self->{HOSTS} } ); 194 return $self->addr_sort(@hosts); 195 196} 197 198#/*****************************************************************************/ 199# PARSING TAG HANDLERS FOR XML::TWIG 200#/*****************************************************************************/ 201 202sub _nmaprun_start_tag_hdlr { 203 204 my ($self, $twig, $tag ) = @_; 205 206 $self->{SESSION}{start_time} = $tag->{att}->{start}; 207 $self->{SESSION}{nmap_version} = $tag->{att}->{version}; 208 $self->{SESSION}{start_str} = $tag->{att}->{startstr}; 209 $self->{SESSION}{xml_version} = $tag->{att}->{xmloutputversion}; 210 $self->{SESSION}{scan_args} = $tag->{att}->{args}; 211 $self->{SESSION} = Nmap::Parser::Session->new( $self->{SESSION} ); 212 213 $twig->purge; 214 215} 216 217sub _scaninfo_tag_hdlr { 218 my ( $self, $twig, $tag ) = @_; 219 my $type = $tag->{att}->{type}; 220 my $proto = $tag->{att}->{protocol}; 221 my $numservices = $tag->{att}->{numservices}; 222 223 if ( defined($type) ) { #there can be more than one type in one scan 224 $self->{SESSION}{type}{$type} = $proto; 225 $self->{SESSION}{numservices}{$type} = $numservices; 226 } 227 $twig->purge; 228} 229 230sub _prescript_tag_hdlr { 231 my ($self, $twig, $tag ) = @_; 232 my $scripts_hashref; 233 for my $script ( $tag->children('script') ) { 234 $scripts_hashref->{ $script->{att}->{id} } = 235 __script_tag_hdlr( $script ); 236 } 237 $self->{SESSION}{prescript} = $scripts_hashref; 238 $twig->purge; 239} 240 241sub _postscript_tag_hdlr { 242 my ($self, $twig, $tag ) = @_; 243 my $scripts_hashref; 244 for my $script ( $tag->children('script') ) { 245 $scripts_hashref->{ $script->{att}->{id} } = 246 __script_tag_hdlr( $script ); 247 } 248 $self->{SESSION}{postscript} = $scripts_hashref; 249 $twig->purge; 250} 251 252sub _finished_tag_hdlr { 253 my ($self, $twig, $tag ) = @_; 254 $self->{SESSION}{finish_time} = $tag->{att}->{time}; 255 $self->{SESSION}{time_str} = $tag->{att}->{timestr}; 256 $twig->purge; 257} 258 259#parses all the host information in one swoop (calling __host_*_tag_hdlrs) 260sub _host_tag_hdlr { 261 my ($self, $twig, $tag ) = @_; 262 my $id = undef; 263 264 return undef unless ( defined $tag ); 265 266 #GET ADDRESS INFO 267 my $addr_hashref; 268 $addr_hashref = __host_addr_tag_hdlr($tag); 269 270 #use this as the identifier 271 $id = 272 $addr_hashref->{ipv4} 273 || $addr_hashref->{ipv6} 274 || $addr_hashref->{mac}; #worstcase use MAC 275 276 $self->{HOSTS}{$id}{addrs} = $addr_hashref; 277 278 return undef unless ( defined($id) || $id ne '' ); 279 280 #GET HOSTNAMES 281 $self->{HOSTS}{$id}{hostnames} = __host_hostnames_tag_hdlr($tag); 282 283 #GET STARTTIME 284 $self->{HOSTS}{$id}{starttime} = $tag->{att}->{starttime}; 285 286 #GET STATUS 287 $self->{HOSTS}{$id}{status} = $tag->first_child('status')->{att}->{state}; 288 289 #CONTINUE PROCESSING IF STATUS IS UP - OTHERWISE NO MORE XML 290 if ( lc( $self->{HOSTS}{$id}{status} ) eq 'up' ) { 291 292 $self->{HOSTS}{$id}{ports} = __host_port_tag_hdlr($tag); 293 $self->{HOSTS}{$id}{os} = __host_os_tag_hdlr($tag); 294 $self->{HOSTS}{$id}{uptime} = __host_uptime_tag_hdlr($tag); 295 $self->{HOSTS}{$id}{tcpsequence} = __host_tcpsequence_tag_hdlr($tag); 296 $self->{HOSTS}{$id}{ipidsequence} = __host_ipidsequence_tag_hdlr($tag); 297 $self->{HOSTS}{$id}{tcptssequence} = __host_tcptssequence_tag_hdlr($tag); 298 $self->{HOSTS}{$id}{hostscript} = __host_hostscript_tag_hdlr($tag); 299 $self->{HOSTS}{$id}{distance} = 300 __host_distance_tag_hdlr($tag); #returns simple value 301 $self->{HOSTS}{$id}{trace} = __host_trace_tag_hdlr($tag); 302 $self->{HOSTS}{$id}{trace_error} = __host_trace_error_tag_hdlr($tag); 303 } 304 305 #CREATE HOST OBJECT FOR USER 306 $self->{HOSTS}{$id} = Nmap::Parser::Host->new( $self->{HOSTS}{$id} ); 307 308 if ( $self->{callback}{is_registered} ) { 309 &{ $self->{callback}{coderef} }( $self->{HOSTS}{$id} ); 310 delete $self->{HOSTS}{$id}; 311 } 312 313 $twig->purge; 314 315} 316 317sub __host_addr_tag_hdlr { 318 my $tag = shift; 319 my $addr_hashref; 320 321 #children() will return all children with tag name address 322 for my $addr ( $tag->children('address') ) { 323 if ( lc( $addr->{att}->{addrtype} ) eq 'mac' ) { 324 325 #we'll assume for now, only 1 MAC address per system 326 $addr_hashref->{mac}{addr} = $addr->{att}->{addr}; 327 $addr_hashref->{mac}{vendor} = $addr->{att}->{vendor}; 328 } 329 elsif ( lc( $addr->{att}->{addrtype} ) eq 'ipv4' ) { 330 $addr_hashref->{ipv4} = $addr->{att}->{addr}; 331 } #support for ipv6? we'll see 332 elsif ( lc( $addr->{att}->{addrtype} ) eq 'ipv6' ) { 333 $addr_hashref->{ipv6} = $addr->{att}->{addr}; 334 } 335 336 } 337 338 return $addr_hashref; 339} 340 341sub __host_hostnames_tag_hdlr { 342 my $tag = shift; 343 344 my $hostnames_tag = $tag->first_child('hostnames'); 345 return undef unless ( defined $hostnames_tag ); 346 347 my @hostnames; 348 349 for my $name ( $hostnames_tag->children('hostname') ) { 350 push @hostnames, $name->{att}->{name}; 351 } 352 353 return \@hostnames; 354 355} 356 357sub __host_port_tag_hdlr { 358 my $tag = shift; 359 my ( $port_hashref, $ports_tag ); 360 361 $ports_tag = $tag->first_child('ports'); 362 363 return undef unless ( defined $ports_tag ); 364 365 #Parsing Extraports 366 my $extraports_tag = $ports_tag->first_child('extraports'); 367 if ( defined $extraports_tag && $extraports_tag ne '' ) { 368 $port_hashref->{extraports}{state} = $extraports_tag->{att}->{state}; 369 $port_hashref->{extraports}{count} = $extraports_tag->{att}->{count}; 370 } 371 372 #Parsing regular port information 373 374 my ( $tcp_port_count, $udp_port_count ) = ( 0, 0 ); 375 376 for my $port_tag ( $ports_tag->children('port') ) { 377 my $proto = $port_tag->{att}->{protocol}; 378 my $portid = $port_tag->{att}->{portid}; 379 my $state = $port_tag->first_child('state'); 380 my $owner = $port_tag->first_child('owner') || undef; 381 382 $tcp_port_count++ if ( $proto eq 'tcp' ); 383 $udp_port_count++ if ( $proto eq 'udp' ); 384 385 $port_hashref->{$proto}{$portid}{state} = $state->{att}->{state} 386 || 'unknown' 387 if ( $state ne '' ); 388 389 $port_hashref->{$proto}{$portid}{reason_ttl} = $state->{att}->{reason_ttl} 390 || 'unknown' 391 if($state ne ''); 392 393 #GET SERVICE INFORMATION 394 $port_hashref->{$proto}{$portid}{service} = 395 __host_service_tag_hdlr( $port_tag, $portid ) 396 if ( defined($proto) && defined($portid) ); 397 398 #GET SCRIPT INFORMATION 399 $port_hashref->{$proto}{$portid}{service}{script} = 400 __host_script_tag_hdlr( $port_tag, $portid) 401 if ( defined($proto) && defined($portid) ); 402 403 #GET OWNER INFORMATION 404 $port_hashref->{$proto}{$portid}{service}{owner} = $owner->{att}->{name} 405 if ( defined($owner) ); 406 407 #These are added at the end, otherwise __host_service_tag_hdlr will overwrite 408 #GET PORT STATE 409 410 } 411 412 $port_hashref->{tcp_port_count} = $tcp_port_count; 413 $port_hashref->{udp_port_count} = $udp_port_count; 414 415 return $port_hashref; 416 417} 418 419sub __host_service_tag_hdlr { 420 my $tag = shift; 421 my $portid = shift; #need a way to remember what port this service runs on 422 my $service = $tag->first_child('service[@name]'); 423 my $service_hashref; 424 $service_hashref->{port} = $portid; 425 426 if ( defined $service ) { 427 $service_hashref->{name} = $service->{att}->{name} || 'unknown'; 428 $service_hashref->{version} = $service->{att}->{version}; 429 $service_hashref->{product} = $service->{att}->{product}; 430 $service_hashref->{devicetype} = $service->{att}->{devicetype}; 431 $service_hashref->{extrainfo} = $service->{att}->{extrainfo}; 432 $service_hashref->{proto} = 433 $service->{att}->{proto} 434 || $service->{att}->{protocol} 435 || 'unknown'; 436 $service_hashref->{rpcnum} = $service->{att}->{rpcnum}; 437 $service_hashref->{tunnel} = $service->{att}->{tunnel}; 438 $service_hashref->{method} = $service->{att}->{method}; 439 $service_hashref->{confidence} = $service->{att}->{conf}; 440 $service_hashref->{fingerprint} = $service->{att}->{servicefp}; 441 } 442 443 return $service_hashref; 444} 445 446sub __host_script_tag_hdlr { 447 my $tag = shift; 448 my $script_hashref; 449 450 for ( $tag->children('script') ) { 451 $script_hashref->{ $_->{att}->{id} } = 452 __script_tag_hdlr($_); 453 } 454 455 return $script_hashref; 456} 457 458sub __host_os_tag_hdlr { 459 my $tag = shift; 460 my $os_tag = $tag->first_child('os'); 461 my $os_hashref; 462 my $portused_tag; 463 my $os_fingerprint; 464 465 if ( defined $os_tag ) { 466 467 #get the open port used to match os 468 $portused_tag = $os_tag->first_child("portused[\@state='open']"); 469 $os_hashref->{portused}{open} = $portused_tag->{att}->{portid} 470 if ( defined $portused_tag ); 471 472 #get the closed port used to match os 473 $portused_tag = $os_tag->first_child("portused[\@state='closed']"); 474 $os_hashref->{portused}{closed} = $portused_tag->{att}->{portid} 475 if ( defined $portused_tag ); 476 477 #os fingerprint 478 $os_fingerprint = $os_tag->first_child("osfingerprint"); 479 $os_hashref->{os_fingerprint} = 480 $os_fingerprint->{'att'}->{'fingerprint'} 481 if ( defined $os_fingerprint ); 482 483 #This will go in Nmap::Parser::Host::OS 484 my $osmatch_index = 0; 485 my $osclass_index = 0; 486 for my $osmatch ( $os_tag->children('osmatch') ) { 487 $os_hashref->{osmatch_name}[$osmatch_index] = 488 $osmatch->{att}->{name}; 489 $os_hashref->{osmatch_name_accuracy}[$osmatch_index] = 490 $osmatch->{att}->{accuracy}; 491 $osmatch_index++; 492 for my $osclass ( $osmatch->children('osclass') ) { 493 $os_hashref->{osclass_osfamily}[$osclass_index] = 494 $osclass->{att}->{osfamily}; 495 $os_hashref->{osclass_osgen}[$osclass_index] = 496 $osclass->{att}->{osgen}; 497 $os_hashref->{osclass_vendor}[$osclass_index] = 498 $osclass->{att}->{vendor}; 499 $os_hashref->{osclass_type}[$osclass_index] = 500 $osclass->{att}->{type}; 501 $os_hashref->{osclass_class_accuracy}[$osclass_index] = 502 $osclass->{att}->{accuracy}; 503 $osclass_index++; 504 } 505 } 506 $os_hashref->{'osmatch_count'} = $osmatch_index; 507 508 #parse osclass tags 509 for my $osclass ( $os_tag->children('osclass') ) { 510 $os_hashref->{osclass_osfamily}[$osclass_index] = 511 $osclass->{att}->{osfamily}; 512 $os_hashref->{osclass_osgen}[$osclass_index] = 513 $osclass->{att}->{osgen}; 514 $os_hashref->{osclass_vendor}[$osclass_index] = 515 $osclass->{att}->{vendor}; 516 $os_hashref->{osclass_type}[$osclass_index] = 517 $osclass->{att}->{type}; 518 $os_hashref->{osclass_class_accuracy}[$osclass_index] = 519 $osclass->{att}->{accuracy}; 520 $osclass_index++; 521 } 522 $os_hashref->{'osclass_count'} = $osclass_index; 523 } 524 525 return $os_hashref; 526 527} 528 529sub __host_uptime_tag_hdlr { 530 my $tag = shift; 531 my $uptime = $tag->first_child('uptime'); 532 my $uptime_hashref; 533 534 if ( defined $uptime ) { 535 $uptime_hashref->{seconds} = $uptime->{att}->{seconds}; 536 $uptime_hashref->{lastboot} = $uptime->{att}->{lastboot}; 537 538 } 539 540 return $uptime_hashref; 541 542} 543 544sub __host_tcpsequence_tag_hdlr { 545 my $tag = shift; 546 my $sequence = $tag->first_child('tcpsequence'); 547 my $sequence_hashref; 548 return undef unless ($sequence); 549 $sequence_hashref->{class} = $sequence->{att}->{class}; 550 $sequence_hashref->{difficulty} = $sequence->{att}->{difficulty}; 551 $sequence_hashref->{values} = $sequence->{att}->{values}; 552 $sequence_hashref->{index} = $sequence->{att}->{index}; 553 554 return $sequence_hashref; 555 556} 557 558sub __host_ipidsequence_tag_hdlr { 559 my $tag = shift; 560 my $sequence = $tag->first_child('ipidsequence'); 561 my $sequence_hashref; 562 return undef unless ($sequence); 563 $sequence_hashref->{class} = $sequence->{att}->{class}; 564 $sequence_hashref->{values} = $sequence->{att}->{values}; 565 return $sequence_hashref; 566 567} 568 569sub __host_tcptssequence_tag_hdlr { 570 my $tag = shift; 571 my $sequence = $tag->first_child('tcptssequence'); 572 my $sequence_hashref; 573 return undef unless ($sequence); 574 $sequence_hashref->{class} = $sequence->{att}->{class}; 575 $sequence_hashref->{values} = $sequence->{att}->{values}; 576 return $sequence_hashref; 577} 578 579sub __host_hostscript_tag_hdlr { 580 my $tag = shift; 581 my $scripts = $tag->first_child('hostscript'); 582 my $scripts_hashref; 583 return undef unless ($scripts); 584 for my $script ( $scripts->children('script') ) { 585 $scripts_hashref->{ $script->{att}->{id} } = 586 __script_tag_hdlr( $script ); 587 } 588 return $scripts_hashref; 589} 590 591sub __host_distance_tag_hdlr { 592 my $tag = shift; 593 my $distance = $tag->first_child('distance'); 594 return undef unless ($distance); 595 return $distance->{att}->{value}; 596} 597 598sub __host_trace_tag_hdlr { 599 my $tag = shift; 600 my $trace_tag = $tag->first_child('trace'); 601 my $trace_hashref = { hops => [], }; 602 603 if ( defined $trace_tag ) { 604 605 my $proto = $trace_tag->{att}->{proto}; 606 $trace_hashref->{proto} = $proto if defined $proto; 607 608 my $port = $trace_tag->{att}->{port}; 609 $trace_hashref->{port} = $port if defined $port; 610 611 for my $hop_tag ( $trace_tag->children('hop') ) { 612 613 # Copy the known hop attributes, they will go in 614 # Nmap::Parser::Host::TraceHop 615 my %hop_data; 616 $hop_data{$_} = $hop_tag->{att}->{$_} for qw( ttl rtt ipaddr host ); 617 delete $hop_data{rtt} if $hop_data{rtt} !~ /^[\d.]+$/; 618 619 push @{ $trace_hashref->{hops} }, \%hop_data; 620 } 621 622 } 623 624 return $trace_hashref; 625} 626 627sub __host_trace_error_tag_hdlr { 628 my $tag = shift; 629 my $trace_tag = $tag->first_child('trace'); 630 631 if ( defined $trace_tag ) { 632 633 my $error_tag = $trace_tag->first_child('error'); 634 if ( defined $error_tag ) { 635 636 # If an error happens, always provide a true value even if 637 # it doesn't contains a useful string 638 my $errorstr = $error_tag->{att}->{errorstr} || 1; 639 return $errorstr; 640 } 641 } 642 643 return; 644} 645 646sub __script_tag_hdlr { 647 my $tag = shift; 648 my $script_hashref = { 649 output => $tag->{att}->{output} 650 }; 651 chomp %$script_hashref; 652 if ( not $tag->is_empty()) { 653 $script_hashref->{contents} = __script_table($tag); 654 } 655 return $script_hashref; 656} 657 658sub __script_table { 659 my $tag = shift; 660 my ($ref, $subref); 661 my $fc = $tag->first_child(); 662 if ($fc) { 663 if ($fc->is_text) { 664 $ref = $fc->text; 665 } 666 else { 667 if ($fc->{att}->{key}) { 668 $ref = {}; 669 $subref = sub { 670 $ref->{$_->{att}->{key}} = shift; 671 }; 672 } 673 else { 674 $ref = []; 675 $subref = sub { 676 push @$ref, shift; 677 }; 678 } 679 for ($tag->children()) { 680 if ($_->tag() eq "table") { 681 $subref->(__script_table( $_ )); 682 } 683 else { 684 $subref->($_->text); 685 } 686 } 687 } 688 } 689 return $ref 690} 691 692#/*****************************************************************************/ 693# NMAP::PARSER::SESSION 694#/*****************************************************************************/ 695 696package Nmap::Parser::Session; 697use vars qw($AUTOLOAD); 698 699sub new { 700 my $class = shift; 701 $class = ref($class) || $class; 702 my $self = shift || {}; 703 bless( $self, $class ); 704 return $self; 705} 706 707#Support for: 708#start_time, start_str, finish_time, time_str, nmap_version, xml_version, scan_args 709sub AUTOLOAD { 710 ( my $param = $AUTOLOAD ) =~ s{.*::}{}xms; 711 return if ( $param eq 'DESTROY' ); 712 no strict 'refs'; 713 *$AUTOLOAD = sub { return $_[0]->{ lc $param } }; 714 goto &$AUTOLOAD; 715} 716 717sub numservices { 718 my $self = shift; 719 my $type = shift 720 || ''; #(syn|ack|bounce|connect|null|xmas|window|maimon|fin|udp|ipproto) 721 722 return unless ( ref( $self->{numservices} ) eq 'HASH' ); 723 724 if ( $type ne '' ) { return $self->{numservices}{$type}; } 725 else { 726 my $total = 0; 727 for ( values %{ $self->{numservices} } ) { $total += $_; } 728 return $total; 729 } #(else) total number of services together 730} 731 732sub scan_types { 733 return sort { $a cmp $b } ( keys %{ $_[0]->{type} } ) 734 if ( ref( $_[0]->{type} ) eq 'HASH' ); 735} 736sub scan_type_proto { return $_[1] ? $_[0]->{type}{ $_[1] } : undef; } 737 738sub prescripts { 739 my $self = shift; 740 my $id = shift; 741 unless ( defined $id ) { 742 return sort keys %{ $self->{prescript} }; 743 } 744 else { 745 return $self->{prescript}{$id}; 746 } 747} 748 749sub postscripts { 750 my $self = shift; 751 my $id = shift; 752 unless ( defined $id ) { 753 return sort keys %{ $self->{postscript} }; 754 } 755 else { 756 return $self->{postscript}{$id}; 757 } 758} 759 760#/*****************************************************************************/ 761# NMAP::PARSER::HOST 762#/*****************************************************************************/ 763 764package Nmap::Parser::Host; 765use vars qw($AUTOLOAD); 766 767sub new { 768 my $class = shift; 769 $class = ref($class) || $class; 770 my $self = shift || {}; 771 bless( $self, $class ); 772 return $self; 773} 774 775sub starttime { return $_[0]->{starttime}; } 776 777sub status { return $_[0]->{status}; } 778 779sub addr { 780 my $default = $_[0]->{addrs}{ipv4} || $_[0]->{addrs}{ipv6}; 781 return $default; 782} 783 784sub addrtype { 785 if ( $_[0]->{addrs}{ipv4} ) { return 'ipv4'; } 786 elsif ( $_[0]->{addrs}{ipv6} ) { return 'ipv6'; } 787} 788 789sub ipv4_addr { return $_[0]->{addrs}{ipv4}; } 790sub ipv6_addr { return $_[0]->{addrs}{ipv6}; } 791 792sub mac_addr { return $_[0]->{addrs}{mac}{addr}; } 793sub mac_vendor { return $_[0]->{addrs}{mac}{vendor}; } 794 795#returns the first hostname 796sub hostname { 797 my $self = shift; 798 my $index = shift || 0; 799 if ( ref( $self->{hostnames} ) ne 'ARRAY' ) { return ''; } 800 if ( scalar @{ $self->{hostnames} } <= $index ) { 801 $index = scalar @{ $self->{hostnames} } - 1; 802 } 803 return $self->{hostnames}[$index] if ( scalar @{ $self->{hostnames} } ); 804} 805 806sub all_hostnames { return @{ $_[0]->{hostnames} || [] }; } 807sub extraports_state { return $_[0]->{ports}{extraports}{state}; } 808sub extraports_count { return $_[0]->{ports}{extraports}{count}; } 809sub distance { return $_[0]->{distance}; } 810 811sub hostscripts { 812 my $self = shift; 813 my $id = shift; 814 unless ( defined $id ) { 815 return sort keys %{ $self->{hostscript} }; 816 } 817 else { 818 return $self->{hostscript}{$id}; 819 } 820} 821 822sub all_trace_hops { 823 824 my $self = shift; 825 826 return unless defined $self->{trace}->{hops}; 827 return map { Nmap::Parser::Host::TraceHop->new( $_ ) } 828 @{ $self->{trace}->{hops} }; 829} 830 831sub trace_port { return $_[0]->{trace}->{port} } 832sub trace_proto { return $_[0]->{trace}->{proto} } 833sub trace_error { return $_[0]->{trace_error} } 834 835sub _del_port { 836 my $self = shift; 837 my $proto = pop; #portid might be empty, so this goes first 838 my @portids = @_; 839 @portids = grep { $_ + 0 } @portids; 840 841 unless ( scalar @portids ) { 842 warn "[Nmap-Parser] No port number given to del_port()\n"; 843 return undef; 844 } 845 846 delete $self->{ports}{$proto}{$_} for (@portids); 847} 848 849sub _get_ports { 850 my $self = shift; 851 my $proto = pop; #param might be empty, so this goes first 852 my $state = shift; #open, filtered, closed or any combination 853 my @matched_ports = (); 854 855 #if $state is undef, then tcp_ports or udp_ports was called for all ports 856 #therefore, only return the keys of all ports found 857 if ( not defined $state ) { 858 return sort { $a <=> $b } ( keys %{ $self->{ports}{$proto} } ); 859 } 860 else { 861 $state = lc($state) 862 } 863 864#the port parameter can be set to either any of these also 'open|filtered' 865#can count as 'open' and 'filetered'. Therefore I need to use a regex from now on 866#if $param is empty, then all ports match. 867 868 for my $portid ( keys %{ $self->{ports}{$proto} } ) { 869 870 #escape metacharacters ('|', for example in: open|filtered) 871 #using \Q and \E 872 push( @matched_ports, $portid ) 873 if ( $self->{ports}{$proto}{$portid}{state} =~ /\Q$state\E/ ); 874 875 } 876 877 return sort { $a <=> $b } @matched_ports; 878 879} 880 881sub _get_port_state { 882 my $self = shift; 883 my $proto = pop; #portid might be empty, so this goes first 884 my $portid = lc(shift); 885 886 return undef unless ( exists $self->{ports}{$proto}{$portid} ); 887 return $self->{ports}{$proto}{$portid}{state}; 888 889} 890 891sub _get_port_state_ttl { 892 my $self = shift; 893 my $proto = pop; 894 my $portid = lc(shift); 895 896 return undef unless ( exists $self->{ports}{$proto}{$portid} ); 897 return $self->{ports}{$proto}{$portid}{reason_ttl}; 898} 899 900#changed this to use _get_ports since it was similar code 901sub tcp_ports { return _get_ports( @_, 'tcp' ); } 902sub udp_ports { return _get_ports( @_, 'udp' ); } 903 904sub tcp_port_count { return $_[0]->{ports}{tcp_port_count}; } 905sub udp_port_count { return $_[0]->{ports}{udp_port_count}; } 906 907sub tcp_port_state_ttl { return _get_port_state_ttl( @_, 'tcp' ); } 908 909sub tcp_port_state { return _get_port_state( @_, 'tcp' ); } 910sub udp_port_state { return _get_port_state( @_, 'udp' ); } 911 912sub tcp_del_ports { return _del_port( @_, 'tcp' ); } 913sub udp_del_ports { return _del_port( @_, 'udp' ); } 914 915sub tcp_service { 916 my $self = shift; 917 my $portid = shift; 918 if ( $portid eq '' ) { 919 warn "[Nmap-Parser] No port number passed to tcp_service()\n"; 920 return undef; 921 } 922 return Nmap::Parser::Host::Service->new( 923 $self->{ports}{tcp}{$portid}{service} ); 924} 925 926sub udp_service { 927 my $self = shift; 928 my $portid = shift; 929 if ( $portid eq '' ) { 930 warn "[Nmap-Parser] No port number passed to udp_service()\n"; 931 return undef; 932 } 933 return Nmap::Parser::Host::Service->new( 934 $self->{ports}{udp}{$portid}{service} ); 935 936} 937 938#usually the first one is the highest accuracy 939 940sub os_sig { return Nmap::Parser::Host::OS->new( $_[0]->{os} ); } 941 942#Support for: 943#tcpsequence_class, tcpsequence_values, tcpsequence_index, 944#ipidsequence_class, ipidsequence_values, tcptssequence_values, 945#tcptssequence_class, uptime_seconds, uptime_lastboot 946#tcp_open_ports, udp_open_ports, tcp_filtered_ports, udp_filtered_ports, 947#tcp_closed_ports, udp_closed_ports 948sub AUTOLOAD { 949 ( my $param = $AUTOLOAD ) =~ s{.*::}{}xms; 950 return if ( $param eq 'DESTROY' ); 951 my ( $type, $val ) = split /_/, lc($param); 952 953#splits the given method name by '_'. This will determine the function and param 954 no strict 'refs'; 955 956 if ( ( $type eq 'tcp' || $type eq 'udp' ) 957 && ( $val eq 'open' || $val eq 'filtered' || $val eq 'closed' ) ) 958 { 959 960#they must be looking for port info: tcp or udp. The $val is either open|filtered|closed 961 *$AUTOLOAD = sub { return _get_ports( $_[0], $val, $type ); }; 962 goto &$AUTOLOAD; 963 964 } 965 elsif ( defined $type && defined $val ) { 966 967 #must be one of the 'sequence' functions asking for class/values/index 968 *$AUTOLOAD = sub { return $_[0]->{$type}{$val} }; 969 goto &$AUTOLOAD; 970 } 971 else { die '[Nmap-Parser] method ->' . $param . "() not defined!\n"; } 972} 973 974#/*****************************************************************************/ 975# NMAP::PARSER::HOST::SERVICE 976#/*****************************************************************************/ 977 978package Nmap::Parser::Host::Service; 979use vars qw($AUTOLOAD); 980 981sub new { 982 my $class = shift; 983 $class = ref($class) || $class; 984 my $self = shift || {}; 985 bless( $self, $class ); 986 return $self; 987} 988 989sub scripts { 990 my $self = shift; 991 my $id = shift; 992 993 unless ( defined $id ) { 994 return sort keys %{ $self->{script} }; 995 } 996 else { 997 return $self->{script}{$id}; 998 } 999} 1000 1001#Support for: 1002#name port proto rpcnum owner version product extrainfo tunnel method confidence 1003#this will now only load functions that will be used. This saves 1004#on delay (increase speed) and memory 1005 1006sub AUTOLOAD { 1007 ( my $param = $AUTOLOAD ) =~ s{.*::}{}xms; 1008 return if ( $param eq 'DESTROY' ); 1009 no strict 'refs'; 1010 1011 *$AUTOLOAD = sub { return $_[0]->{ lc $param } }; 1012 goto &$AUTOLOAD; 1013} 1014 1015#/*****************************************************************************/ 1016# NMAP::PARSER::HOST::OS 1017#/*****************************************************************************/ 1018 1019package Nmap::Parser::Host::OS; 1020use vars qw($AUTOLOAD); 1021 1022sub new { 1023 my $class = shift; 1024 $class = ref($class) || $class; 1025 my $self = shift || {}; 1026 bless( $self, $class ); 1027 return $self; 1028} 1029 1030sub portused_open { return $_[0]->{portused}{open}; } 1031sub portused_closed { return $_[0]->{portused}{closed}; } 1032sub os_fingerprint { return $_[0]->{os_fingerprint}; } 1033 1034sub name_count { return $_[0]->{osmatch_count}; } 1035 1036sub all_names { 1037 my $self = shift; 1038 @_ = (); 1039 if ( $self->{osclass_count} < 1 ) { return @_; } 1040 if ( ref( $self->{osmatch_name} ) eq 'ARRAY' ) { 1041 return sort @{ $self->{osmatch_name} }; 1042 } 1043 1044} #given by decreasing accuracy 1045 1046sub class_count { return $_[0]->{osclass_count}; } 1047 1048#Support for: 1049#name,names, name_accuracy, osfamily, vendor, type, osgen, class_accuracy 1050sub AUTOLOAD { 1051 ( my $param = $AUTOLOAD ) =~ s{.*::}{}xms; 1052 return if ( $param eq 'DESTROY' ); 1053 no strict 'refs'; 1054 $param = lc($param); 1055 1056 $param = 'name' if ( $param eq 'names' ); 1057 if ( $param eq 'name' || $param eq 'name_accuracy' ) { 1058 1059 *$AUTOLOAD = sub { _get_info( $_[0], $_[1], $param, 'osmatch' ); }; 1060 goto &$AUTOLOAD; 1061 } 1062 else { 1063 1064 *$AUTOLOAD = sub { _get_info( $_[0], $_[1], $param, 'osclass' ); }; 1065 goto &$AUTOLOAD; 1066 } 1067} 1068 1069sub _get_info { 1070 my ( $self, $index, $param, $type ) = @_; 1071 $index ||= 0; 1072 1073 #type is either osclass or osmatch 1074 if ( $index >= $self->{ $type . '_count' } ) { 1075 $index = $self->{ $type . '_count' } - 1; 1076 } 1077 return $self->{ $type . '_' . $param }[$index]; 1078} 1079 1080#/*****************************************************************************/ 1081# NMAP::PARSER::HOST::TRACEHOP 1082#/*****************************************************************************/ 1083 1084package Nmap::Parser::Host::TraceHop; 1085use vars qw($AUTOLOAD); 1086 1087sub new { 1088 my $class = shift; 1089 $class = ref($class) || $class; 1090 my $self = shift || {}; 1091 bless( $self, $class ); 1092 return $self; 1093} 1094 1095sub AUTOLOAD { 1096 ( my $param = $AUTOLOAD ) =~ s{.*::}{}xms; 1097 return if ( $param eq 'DESTROY' ); 1098 no strict 'refs'; 1099 $param = lc($param); 1100 1101 # Supported accessors: 1102 my %subs; 1103 @subs{ qw( ttl rtt ipaddr host ) } = 1; 1104 1105 if ( exists $subs{$param} ) { 1106 1107 *$AUTOLOAD = sub { $_[0]->{$param} }; 1108 goto &$AUTOLOAD; 1109 } 1110 else { die '[Nmap-Parser] method ->' . $param . "() not defined!\n"; } 1111} 1112 11131; 1114 1115__END__ 1116 1117=pod 1118 1119=head1 NAME 1120 1121Nmap::Parser - parse nmap scan data with perl 1122 1123=head1 SYNOPSIS 1124 1125 use Nmap::Parser; 1126 my $np = new Nmap::Parser; 1127 1128 $np->parsescan($nmap_path, $nmap_args, @ips); 1129 #or 1130 $np->parsefile($file_xml); 1131 1132 my $session = $np->get_session(); 1133 #a Nmap::Parser::Session object 1134 1135 my $host = $np->get_host($ip_addr); 1136 #a Nmap::Parser::Host object 1137 1138 my $service = $host->tcp_service(80); 1139 #a Nmap::Parser::Host::Service object 1140 1141 my $os = $host->os_sig(); 1142 #a Nmap::Parser::Host::OS object 1143 1144 #--------------------------------------- 1145 1146 my $np2 = new Nmap::Parser; 1147 1148 $np2->callback(\&my_callback); 1149 1150 $np2->parsefile($file_xml); 1151 #or 1152 $np2->parsescan($nmap_path, $nmap_args, @ips); 1153 1154 sub my_callback { 1155 1156 my $host = shift; 1157 #Nmap::Parser::Host object 1158 #.. see documentation for all methods ... 1159 1160 } 1161 1162 1163I<For a full listing of methods see the documentation corresponding to each object.> 1164You can also visit the website L<https://github.com/modernistik/Nmap-Parser> for additional installation instructions. 1165 1166=head1 DESCRIPTION 1167 1168This module implements a interface to the information contained in an nmap scan. 1169It is implemented by parsing the xml scan data that is generated by nmap. This 1170will enable anyone who utilizes nmap to quickly create fast and robust security scripts 1171that utilize the powerful port scanning abilities of nmap. 1172 1173The latest version of this module can be found on here L<https://github.com/modernistik/Nmap-Parser> 1174 1175=head1 OVERVIEW 1176 1177This module has an internal framework to make it easy to retrieve the desired information of a scan. 1178Every nmap scan is based on two main sections of informations: the scan session, and the scan information of all hosts. 1179The session information will be stored as a Nmap::Parser::Session object. This object will contain its own methods 1180to obtain the desired information. The same is true for any hosts that were scanned using the Nmap::Parser::Host object. 1181There are two sub objects under Nmap::Parser::Host. One is the Nmap::Parser::Host::Service object which will be used to obtain 1182information of a given service running on a given port. The second is the Nmap::Parser::Host::OS object which contains the 1183operating system signature information (OS guessed names, classes, osfamily..etc). 1184 1185 Nmap::Parser -- Core parser 1186 | 1187 +--Nmap::Parser::Session -- Nmap scan session information 1188 | 1189 +--Nmap::Parser::Host -- General host information 1190 | | 1191 | |-Nmap::Parser::Host::Service -- Port service information 1192 | | 1193 | |-Nmap::Parser::Host::OS -- Operating system signature information 1194 1195 1196=head1 METHODS 1197 1198=head2 Nmap::Parser 1199 1200The main idea behind the core module is, you will first parse the information 1201and then extract data. Therefore, all parse*() methods should be executed before 1202any get_*() methods. 1203 1204=over 4 1205 1206 1207=item B<parse($string)> 1208 1209=item B<parse($filehandle)> 1210 1211Parses the nmap scan information in $string. Note that is usually only used if 1212you have the whole xml scan information in $string or if you are piping the 1213scan information. 1214 1215=item B<parsefile($xml_file)> 1216 1217Parses the nmap scan data in $xml_file. This file can be generated from an nmap 1218scan by using the '-oX filename.xml' option with nmap. If you get an error or your program dies due to parsing, please check that the 1219xml information is compliant. The file is closed no matter how C<parsefile()> returns. 1220 1221=item B<parsescan($nmap,$args,@ips)> 1222 1223This method runs an nmap scan where $nmap is the path to the nmap executable or binary, 1224$args are the nmap command line parameters, and @ips are the list of IP addresses 1225to scan. parsescan() will automagically run the nmap scan and parse the information. 1226 1227If you wish to save the xml output from parsescan(), you must call cache_scan() method B<BEFORE> 1228you start the parsescan() process. This is done to conserve memory while parsing. cache_scan() will 1229let Nmap::Parser know to save the output before parsing the xml since Nmap::Parser purges everything that has 1230been parsed by the script to conserve memory and increase speed. 1231 1232I<See section EXAMPLES for a short tutorial> 1233 1234I<Note: You cannot have one of the nmap options to be '-oX', '-oN' or '-oG'. Your 1235program will die if you try and pass any of these options because it decides the 1236type of output nmap will generate. The IP addresses can be nmap-formatted 1237addresses see nmap(1)> 1238 1239If you get an error or your program dies due to parsing, please check that the 1240xml information is compliant. If you are using parsescan() or an open filehandle 1241, make sure that the nmap scan that you are performing is successful in returning 1242xml information. (Sometimes using loopback addresses causes nmap to fail). 1243 1244=item B<cache_scan($filename)> 1245 1246This function allows you to save the output of a parsescan() (or nmap scan) to the disk. $filename 1247is the name of the file you wish to save the nmap scan information to. It defaults to nmap-parser-cache.xml 1248It returns the name of the file to be used as the cache. 1249 1250 #Must be called before parsescan(). 1251 $np->cache_scan($filename); #output set to nmap-parser-cache.xml 1252 1253 #.. do other stuff to prepare for parsescan(), ex. setup callbacks 1254 1255 $np->parsescan('/usr/bin/nmap',$args,@IPS); 1256 1257=item B<purge()> 1258 1259Cleans the xml scan data from memory. This is useful if you have a program where 1260you are parsing lots of nmap scan data files with persistent variables. 1261 1262=item B<callback(\&code_ref)> 1263 1264Sets the parsing mode to be done using the callback function. It takes the parameter 1265of a code reference or a reference to a function. If no code reference is given, 1266it resets the mode to normal (no callback). 1267 1268 $np->callback(\&my_function); #sets callback, my_function() will be called 1269 $np->callback(); #resets it, no callback function called. Back to normal. 1270 1271 1272=item B<get_session()> 1273 1274Obtains the Nmap::Parser::Session object which contains the session scan information. 1275 1276=item B<get_host($ip_addr)> 1277 1278Obtains the Nmap::Parser::Host object for the given $ip_addr. 1279 1280=item B<del_host($ip_addr)> 1281 1282Deletes the stored Nmap::Parser::Host object whose IP is $ip_addr. 1283 1284=item B<all_hosts()> 1285 1286=item B<all_hosts($status)> 1287 1288Returns an array of all the Nmap::Parser::Host objects for the scan. If the optional 1289status is given, it will only return those hosts that match that status. The status 1290can be any of the following: C<(up|down|unknown|skipped)> 1291 1292=item B<get_ips()> 1293 1294=item B<get_ips($status)> 1295 1296Returns the list of IP addresses that were scanned in this nmap session. They are 1297sorted using addr_sort. If the optional status is given, it will only return 1298those IP addresses that match that status. The status can be any of the 1299following: C<(up|down|unknown|skipped)> 1300 1301=item B<addr_sort(@ips)> 1302 1303This function takes a list of IP addresses and returns the correctly sorted 1304version of the list. 1305 1306=back 1307 1308=head2 Nmap::Parser::Session 1309 1310This object contains the scan session information of the nmap scan. 1311 1312 1313=over 4 1314 1315 1316=item B<finish_time()> 1317 1318Returns the numeric time that the nmap scan finished. 1319 1320=item B<nmap_version()> 1321 1322Returns the version of nmap used for the scan. 1323 1324=item B<numservices()> 1325 1326=item B<numservices($type)> 1327 1328If numservices is called without argument, it returns the total number of services 1329that were scanned for all types. If $type is given, it returns the number of services 1330for that given scan type. See scan_types() for more info. 1331 1332=item B<scan_args()> 1333 1334Returns a string which contains the nmap executed command line used to run the 1335scan. 1336 1337=item B<scan_type_proto($type)> 1338 1339Returns the protocol type of the given scan type (provided by $type). See scan_types() for 1340more info. 1341 1342=item B<scan_types()> 1343 1344Returns the list of scan types that were performed. It can be any of the following: 1345C<(syn|ack|bounce|connect|null|xmas|window|maimon|fin|udp|ipproto)>. 1346 1347=item B<start_str()> 1348 1349Returns the human readable format of the start time. 1350 1351=item B<start_time()> 1352 1353Returns the numeric form of the time the nmap scan started. 1354 1355=item B<time_str()> 1356 1357Returns the human readable format of the finish time. 1358 1359=item B<xml_version()> 1360 1361Returns the version of nmap xml file. 1362 1363=item B<prescripts()> 1364 1365=item B<prescripts($name)> 1366 1367A basic call to prescripts() returns a list of the names of the NSE scripts 1368run in the pre-scanning phase. If C<$name> is given, it returns the text output of the 1369a reference to a hash with "output" and "contents" keys for the 1370script with that name, or undef if that script was not run. 1371The value of the "output" key is the text output of the script. The value of the 1372"contents" key is a data structure based on the XML output of the NSE script. 1373 1374=item B<postscripts()> 1375 1376=item B<postscripts($name)> 1377 1378A basic call to postscripts() returns a list of the names of the NSE scripts 1379run in the post-scaning phase. If C<$name> is given, it returns the text output of the 1380a reference to a hash with "output" and "contents" keys for the 1381script with that name, or undef if that script was not run. 1382The value of the "output" key is the text output of the script. The value of the 1383"contents" key is a data structure based on the XML output of the NSE script. 1384 1385=back 1386 1387=head2 Nmap::Parser::Host 1388 1389This object represents the information collected from a scanned host. 1390 1391 1392=over 4 1393 1394=item B<status()> 1395 1396Returns the state of the host. It is usually one of these 1397C<(up|down|unknown|skipped)>. 1398 1399=item B<addr()> 1400 1401Returns the main IP address of the host. This is usually the IPv4 address. If 1402there is no IPv4 address, the IPv6 is returned (hopefully there is one). 1403 1404=item B<addrtype()> 1405 1406Returns the address type of the address given by addr() . 1407 1408=item B<all_hostnames()> 1409 1410Returns a list of all hostnames found for the given host. 1411 1412=item B<extraports_count()> 1413 1414Returns the number of extraports found. 1415 1416=item B<extraports_state()> 1417 1418Returns the state of all the extraports found. 1419 1420=item B<hostname()> 1421 1422=item B<hostname($index)> 1423 1424As a basic call, hostname() returns the first hostname obtained for the given 1425host. If there exists more than one hostname, you can provide a number, which 1426is used as the location in the array. The index starts at 0; 1427 1428 #in the case that there are only 2 hostnames 1429 hostname() eq hostname(0); 1430 hostname(1); #second hostname found 1431 hostname(400) eq hostname(1) #nothing at 400; return the name at the last index 1432 1433 1434=item B<ipv4_addr()> 1435 1436Explicitly return the IPv4 address. 1437 1438=item B<ipv6_addr()> 1439 1440Explicitly return the IPv6 address. 1441 1442=item B<mac_addr()> 1443 1444Explicitly return the MAC address. 1445 1446=item B<mac_vendor()> 1447 1448Return the vendor information of the MAC. 1449 1450=item B<distance()> 1451 1452Return the distance (in hops) of the target machine from the machine that performed the scan. 1453 1454=item B<trace_error()> 1455 1456Returns a true value (usually a meaningful error message) if the traceroute was 1457performed but could not reach the destination. In this case C<all_trace_hops()> 1458contains only the part of the path that could be determined. 1459 1460=item B<all_trace_hops()> 1461 1462Returns an array of Nmap::Parser::Host::TraceHop objects representing the path 1463to the target host. This array may be empty if Nmap did not perform the 1464traceroute for some reason (same network, for example). 1465 1466Some hops may be missing if Nmap could not figure out information about them. 1467In this case there is a gap between the C<ttl()> values of consecutive returned 1468hops. See also C<trace_error()>. 1469 1470=item B<trace_proto()> 1471 1472Returns the name of the protocol used to perform the traceroute. 1473 1474=item B<trace_port()> 1475 1476Returns the port used to perform the traceroute. 1477 1478=item B<os_sig()> 1479 1480Returns an Nmap::Parser::Host::OS object that can be used to obtain all the 1481Operating System signature (fingerprint) information. See Nmap::Parser::Host::OS 1482for more details. 1483 1484 $os = $host->os_sig; 1485 $os->name; 1486 $os->osfamily; 1487 1488=item B<tcpsequence_class()> 1489 1490=item B<tcpsequence_index()> 1491 1492=item B<tcpsequence_values()> 1493 1494Returns the class, index and values information respectively of the tcp sequence. 1495 1496=item B<ipidsequence_class()> 1497 1498=item B<ipidsequence_values()> 1499 1500Returns the class and values information respectively of the ipid sequence. 1501 1502=item B<tcptssequence_class()> 1503 1504=item B<tcptssequence_values()> 1505 1506Returns the class and values information respectively of the tcpts sequence. 1507 1508=item B<uptime_lastboot()> 1509 1510Returns the human readable format of the timestamp of when the host had last 1511rebooted. 1512 1513=item B<uptime_seconds()> 1514 1515Returns the number of seconds that have passed since the host's last boot from 1516when the scan was performed. 1517 1518=item B<hostscripts()> 1519 1520=item B<hostscripts($name)> 1521 1522A basic call to hostscripts() returns a list of the names of the host scripts 1523run. If C<$name> is given, it returns the text output of the 1524a reference to a hash with "output" and "contents" keys for the 1525script with that name, or undef if that script was not run. 1526The value of the "output" key is the text output of the script. The value of the 1527"contents" key is a data structure based on the XML output of the NSE script. 1528 1529=item B<tcp_ports()> 1530 1531=item B<udp_ports()> 1532 1533Returns the sorted list of TCP|UDP ports respectively that were scanned on this host. Optionally 1534a string argument can be given to these functions to filter the list. 1535 1536 $host->tcp_ports('open') #returns all only 'open' ports (even 'open|filtered') 1537 $host->udp_ports('open|filtered'); #matches exactly ports with 'open|filtered' 1538 1539I<Note that if a port state is set to 'open|filtered' (or any combination), it will 1540be counted as an 'open' port as well as a 'filtered' one.> 1541 1542=item B<tcp_port_count()> 1543 1544=item B<udp_port_count()> 1545 1546Returns the total of TCP|UDP ports scanned respectively. 1547 1548=item B<tcp_port_state_ttl()> 1549 1550Returns the 'reason_ttl' value present in nmap xml result. 1551 1552=item B<tcp_del_ports($portid, [$portid, ...])> 1553 1554=item B<udp_del_ports($portid, [ $portid, ...])> 1555 1556Deletes the current $portid from the list of ports for given protocol. 1557 1558=item B<tcp_port_state($portid)> 1559 1560=item B<udp_port_state($portid)> 1561 1562Returns the state of the given port, provided by the port number in $portid. 1563 1564=item B<tcp_open_ports()> 1565 1566=item B<udp_open_ports()> 1567 1568Returns the list of open TCP|UDP ports respectively. Note that if a port state is 1569for example, 'open|filtered', it will appear on this list as well. 1570 1571=item B<tcp_filtered_ports()> 1572 1573=item B<udp_filtered_ports()> 1574 1575Returns the list of filtered TCP|UDP ports respectively. Note that if a port state is 1576for example, 'open|filtered', it will appear on this list as well. 1577 1578=item B<tcp_closed_ports()> 1579 1580=item B<udp_closed_ports()> 1581 1582Returns the list of closed TCP|UDP ports respectively. Note that if a port state is 1583for example, 'closed|filtered', it will appear on this list as well. 1584 1585=item B<tcp_service($portid)> 1586 1587=item B<udp_service($portid)> 1588 1589Returns the Nmap::Parser::Host::Service object of a given service running on port, 1590provided by $portid. See Nmap::Parser::Host::Service for more info. 1591 1592 $svc = $host->tcp_service(80); 1593 $svc->name; 1594 $svc->proto; 1595 1596 1597=back 1598 1599=head3 Nmap::Parser::Host::Service 1600 1601This object represents the service running on a given port in a given host. This 1602object is obtained by using the tcp_service($portid) or udp_service($portid) method from the 1603Nmap::Parser::Host object. If a portid is given that does not exist on the given 1604host, these functions will still return an object (so your script doesn't die). 1605Its good to use tcp_ports() or udp_ports() to see what ports were collected. 1606 1607=over 4 1608 1609 1610=item B<confidence()> 1611 1612Returns the confidence level in service detection. 1613 1614=item B<extrainfo()> 1615 1616Returns any additional information nmap knows about the service. 1617 1618=item B<method()> 1619 1620Returns the detection method. 1621 1622=item B<name()> 1623 1624Returns the service name. 1625 1626=item B<owner()> 1627 1628Returns the process owner of the given service. (If available) 1629 1630=item B<port()> 1631 1632Returns the port number where the service is running on. 1633 1634=item B<product()> 1635 1636Returns the product information of the service. 1637 1638=item B<proto()> 1639 1640Returns the protocol type of the service. 1641 1642=item B<rpcnum()> 1643 1644Returns the RPC number. 1645 1646=item B<tunnel()> 1647 1648Returns the tunnel value. (If available) 1649 1650=item B<fingerprint()> 1651 1652Returns the service fingerprint. (If available) 1653 1654=item B<version()> 1655 1656Returns the version of the given product of the running service. 1657 1658=item B<scripts()> 1659 1660=item B<scripts($name)> 1661 1662A basic call to scripts() returns a list of the names of the NSE scripts 1663run for this port. If C<$name> is given, it returns 1664a reference to a hash with "output" and "contents" keys for the 1665script with that name, or undef if that script was not run. 1666The value of the "output" key is the text output of the script. The value of the 1667"contents" key is a data structure based on the XML output of the NSE script. 1668 1669=back 1670 1671=head3 Nmap::Parser::Host::OS 1672 1673This object represents the Operating System signature (fingerprint) information 1674of the given host. This object is obtained from an Nmap::Parser::Host object 1675using the C<os_sig()> method. One important thing to note is that the order of OS 1676names and classes are sorted by B<DECREASING ACCURACY>. This is more important than 1677alphabetical ordering. Therefore, a basic call 1678to any of these functions will return the record with the highest accuracy. 1679(Which is probably the one you want anyways). 1680 1681=over 4 1682 1683=item B<all_names()> 1684 1685Returns the list of all the guessed OS names for the given host. 1686 1687=item B<class_accuracy()> 1688 1689=item B<class_accuracy($index)> 1690 1691A basic call to class_accuracy() returns the osclass accuracy of the first record. 1692If C<$index> is given, it returns the osclass accuracy for the given record. The 1693index starts at 0. 1694 1695=item B<class_count()> 1696 1697Returns the total number of OS class records obtained from the nmap scan. 1698 1699=item B<name()> 1700 1701=item B<name($index)> 1702 1703=item B<names()> 1704 1705=item B<names($index)> 1706 1707A basic call to name() returns the OS name of the first record which is the name 1708with the highest accuracy. If C<$index> is given, it returns the name for the given record. The 1709index starts at 0. 1710 1711=item B<name_accuracy()> 1712 1713=item B<name_accuracy($index)> 1714 1715A basic call to name_accuracy() returns the OS name accuracy of the first record. If C<$index> is given, it returns the name for the given record. The 1716index starts at 0. 1717 1718=item B<name_count()> 1719 1720Returns the total number of OS names (records) for the given host. 1721 1722=item B<osfamily()> 1723 1724=item B<osfamily($index)> 1725 1726A basic call to osfamily() returns the OS family information of the first record. 1727If C<$index> is given, it returns the OS family information for the given record. The 1728index starts at 0. 1729 1730=item B<osgen()> 1731 1732=item B<osgen($index)> 1733 1734A basic call to osgen() returns the OS generation information of the first record. 1735If C<$index> is given, it returns the OS generation information for the given record. The 1736index starts at 0. 1737 1738=item B<portused_closed()> 1739 1740Returns the closed port number used to help identify the OS signatures. This might not 1741be available for all hosts. 1742 1743=item B<portused_open()> 1744 1745Returns the open port number used to help identify the OS signatures. This might 1746not be available for all hosts. 1747 1748=item B<os_fingerprint()> 1749 1750Returns the OS fingerprint used to help identify the OS signatures. This might not be available for all hosts. 1751 1752=item B<type()> 1753 1754=item B<type($index)> 1755 1756A basic call to type() returns the OS type information of the first record. 1757If C<$index> is given, it returns the OS type information for the given record. The 1758index starts at 0. 1759 1760=item B<vendor()> 1761 1762=item B<vendor($index)> 1763 1764A basic call to vendor() returns the OS vendor information of the first record. 1765If C<$index> is given, it returns the OS vendor information for the given record. The 1766index starts at 0. 1767 1768=back 1769 1770=head3 Nmap::Parser::Host::TraceHop 1771 1772This object represents a router on the IP path towards the destination or the 1773destination itself. This is similar to what the C<traceroute> command outputs. 1774 1775Nmap::Parser::Host::TraceHop objects are obtained through the 1776C<all_trace_hops()> and C<trace_hop()> Nmap::Parser::Host methods. 1777 1778=over 4 1779 1780=item B<ttl()> 1781 1782The Time To Live is the network distance of this hop. 1783 1784=item B<rtt()> 1785 1786The Round Trip Time is roughly equivalent to the "ping" time towards this hop. 1787It is not always available (in which case it will be undef). 1788 1789=item B<ipaddr()> 1790 1791The known IP address of this hop. 1792 1793=item B<host()> 1794 1795The host name of this hop, if known. 1796 1797=back 1798 1799=head1 EXAMPLES 1800 1801I think some of us best learn from examples. These are a couple of examples to help 1802create custom security audit tools using some of the nice features 1803of the Nmap::Parser module. Hopefully this can double as a tutorial. 1804More tutorials (articles) can be found at L<https://github.com/modernistik/Nmap-Parser> 1805 1806=head2 Real-Time Scanning 1807 1808You can run a nmap scan and have the parser parse the information automagically. 1809The only constraint is that you cannot use '-oX', '-oN', or '-oG' as one of your 1810arguments for nmap command line parameters passed to parsescan(). 1811 1812 use Nmap::Parser; 1813 1814 my $np = new Nmap::Parser; 1815 my @hosts = @ARGV; #get hosts from cmd line 1816 1817 #runs the nmap command with hosts and parses it automagically 1818 $np->parsescan('/usr/bin/nmap','-sS O -p 1-1023',@hosts); 1819 1820 for my $host ($np->all_hosts()){ 1821 print $host->hostname."\n"; 1822 #do mor stuff... 1823 } 1824 1825If you would like to run the scan using parsescan() but also save the scan xml output, 1826you can use cache_scan(). You must call cache_scan() BEFORE you initiate the parsescan() method. 1827 1828 use Nmap::Parser; 1829 my $np = new Nmap::Parser; 1830 1831 #telling np to save output 1832 $np->cache_scan('nmap.localhost.xml'); 1833 $np->parsescan('/usr/bin/nmap','-F','localhost'); 1834 #do other stuff... 1835 1836=head2 Callbacks 1837 1838This is probably the easiest way to write a script with using Nmap::Parser, 1839if you don't need the general scan session information. During the parsing 1840process, the parser will obtain information of every host. The 1841callback function (in this case 'booyah()') is called after the parsing of 1842every host (sequentially). When the callback returns, the parser will delete all 1843information of the host it had sent to the callback. This callback function is 1844called for every host that the parser encounters. I<The callback function must be 1845setup before parsing> 1846 1847 use Nmap::Parser; 1848 my $np = new Nmap::Parser; 1849 1850 1851 $np->callback( \&booyah ); 1852 1853 $np->parsefile('nmap_results.xml'); 1854 # or use parsescan() 1855 1856 sub booyah { 1857 my $host = shift; #Nmap::Parser::Host object, just parsed 1858 print 'IP: ',$host->addr,"\n"; 1859 # ... do more stuff with $host ... 1860 1861 #when it returns, host object will be deleted from memory 1862 #(good for processing VERY LARGE files or scans) 1863 } 1864 1865 1866=head2 Multiple Instances - (C<no less 'of'; my $self>) 1867 1868Using multiple instances of Nmap::Parser is extremely useful in helping 1869audit/monitor the network B<P>olicy (ohh noo! its that 'P' word!). 1870In this example, we have a set of hosts that had been scanned previously for tcp 1871services where the image was saved in I<base_image.xml>. We now will scan the 1872same hosts, and compare if any new tcp have been open since then 1873(good way to look for suspicious new services). Easy security B<C>ompliance detection. 1874(ooh noo! The 'C' word too!). 1875 1876 1877 use Nmap::Parser; 1878 use vars qw($nmap_exe $nmap_args @ips); 1879 my $base = new Nmap::Parser; 1880 my $curr = new Nmap::Parser; 1881 1882 1883 $base->parsefile('base_image.xml'); #load previous state 1884 $curr->parsescan($nmap_exe, $nmap_args, @ips); #scan current hosts 1885 1886 for my $ip ($curr->get_ips ) 1887 { 1888 #assume that IPs in base == IPs in curr scan 1889 my $ip_base = $base->get_host($ip); 1890 my $ip_curr = $curr->get_host($ip); 1891 my %port = (); 1892 1893 #find ports that are open that were not open before 1894 #by finding the difference in port lists 1895 my @diff = grep { $port{$_} < 2} 1896 (map {$port{$_}++; $_} 1897 ( $ip_curr->tcp_open_ports , $ip_base->tcp_open_ports )); 1898 1899 print "$ip has these new ports open: ".join(',',@diff) if(scalar @diff); 1900 1901 for (@diff){print "$_ seems to be ",$ip_curr->tcp_service($_)->name,"\n";} 1902 1903 } 1904 1905 1906=head1 SUPPORT 1907 1908=head2 Discussion Forum 1909 1910If you have questions about how to use the module, or any of its features, you 1911can post messages to the Nmap::Parser module forum on CPAN::Forum. 1912L<https://github.com/modernistik/Nmap-Parser/issues> 1913 1914=head2 Bug Reports, Enhancements, Merge Requests 1915 1916Please submit any bugs or feature requests to: 1917L<https://github.com/modernistik/Nmap-Parser/issues> 1918 1919B<Please make sure that you submit the xml-output file of the scan which you are having 1920trouble with.> This can be done by running your scan with the I<-oX filename.xml> nmap switch. 1921Please remove any important IP addresses for security reasons. It saves time in reproducing issues. 1922 1923=head1 SEE ALSO 1924 1925 nmap, XML::Twig 1926 1927The Nmap::Parser page can be found at: L<https://github.com/modernistik/Nmap-Parser>. 1928It contains the latest developments on the module. The nmap security scanner 1929homepage can be found at: L<http://www.insecure.org/nmap/>. 1930 1931=head1 AUTHORS 1932 1933Origiinal author, Anthony Persaud L<https://www.modernistik.com>. However, special thanks to: Daniel Miller L<https://github.com/bonsaiviking> and Robin Bowes L<http://robinbowes.com>. 1934Please see Changes.md file for a list of other great contributors. 1935 1936=head1 COPYRIGHT 1937Copyright (c) <2003-2017> <Anthony Persaud> L<https://www.modernistik.com> 1938 1939MIT License 1940 1941Permission is hereby granted, free of charge, to any person obtaining a copy 1942of this software and associated documentation files (the "Software"), to deal 1943in the Software without restriction, including without limitation the rights 1944to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1945copies of the Software, and to permit persons to whom the Software is 1946furnished to do so, subject to the following conditions: 1947 1948The above copyright notice and this permission notice shall be included in 1949all copies or substantial portions of the Software. 1950 1951THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1952IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1953FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1954AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1955LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1956OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 1957THE SOFTWARE. 1958 1959=cut 1960