Created
December 20, 2013 18:56
-
-
Save alecnunn/8059619 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/perl | |
| # | |
| # Copyright (C) 2005-2006 Joshua D. Abraham (jabra@ccs.neu.edu) | |
| # | |
| # This program is released under the terms of the GNU General Public License | |
| # (GPL), which is distributed with this software in the file "COPYING". | |
| # The GPL specifies the terms under which users may copy and use this software. | |
| # | |
| # PBNJ 2.0 | |
| # (P)orts (B)anners N' (J)unk | |
| # | |
| # Author: Joshua D. Abraham | |
| # Date: March 15, 2006 | |
| # Updated: November 15, 2006 | |
| # Version: 2.04 | |
| # | |
| # ScanPBNJ - a program for running Nmap scans and storing the results in | |
| # a PBNJ 2.0 database. | |
| # | |
| # Modified by Alec Nunn | |
| # Date: December 20, 2013 | |
| use strict; | |
| use warnings; | |
| use Shell; | |
| use DBI; | |
| use Nmap::Parser; | |
| use Socket; | |
| use Net::hostent; | |
| use Sys::Hostname; | |
| use Getopt::Long; | |
| use FileHandle; | |
| use YAML; | |
| use File::Which; | |
| use Term::ANSIColor qw(:constants); | |
| use File::HomeDir; | |
| use POSIX; | |
| $Term::ANSIColor::AUTORESET = 1; | |
| use vars qw( $PROG ); | |
| ( $PROG = $0 ) =~ s/^.*[\/\\]//; # Truncate calling path from the prog name | |
| my $AUTH = 'Joshua D. Abraham'; # author | |
| my $VERSION = '2.04'; # version | |
| my $np = new Nmap::Parser; # parser object | |
| my $type = 'help'; # parse scan or file | |
| my @ipRange; # ip Address or ip range to scan | |
| my $iplist; # ip list file | |
| my $xmlFile; # xml file to parse | |
| my $Range = '1-1025'; # port range to scan | |
| my $args = "-vv -O -P0 "; # scan args | |
| my $scantype = "-sSV"; # default scantype | |
| my $cargs = 'no'; # user changed args | |
| my $outputdir = '.'; # output database directory | |
| my $nmapPath = which('nmap'); # nmap path | |
| my $interface = ""; # default interface to perform scan | |
| my %options; # getopts hash | |
| my $dbh; # database connection | |
| my $dbconfig = 'config.yaml'; # database config | |
| my $database; # database file | |
| my $db; # db backend | |
| my $hostname; # db host ip | |
| my $port; # db port | |
| my $user; # db username | |
| my $passwd; # db password | |
| my $datadb; # results from the config of the db | |
| my $dir = "."; # dir of the config for the db | |
| my $colors = 1; # print using colors | |
| my $diff = 'version'; | |
| $options{test} = 0; # testing flag | |
| $options{debug} = 0; # debug flag | |
| my $configdir; | |
| #chown() that reports errors | |
| sub safe_chown { | |
| my $uid = shift; | |
| my $gid = shift; | |
| my $file = shift; | |
| if ( chown( $uid, $gid, $file ) != 1 ) { | |
| error( 'Unable to change the owner of the file ' . $file . '.' | |
| . "\n\n" ); | |
| } | |
| } | |
| if ( $^O eq 'MSWin32' || $^O =~ /cygwin/ ) { | |
| $colors = 0; | |
| require Win32; | |
| import Win32; | |
| Win32->import(qw(CSIDL_APPDATA)); | |
| my $dir = Win32::GetFolderPath( CSIDL_APPDATA() ); | |
| # 2.0 | |
| $configdir = $dir . "\\" . "pbnj-2.0"; | |
| if ( -e $configdir and -d $configdir ) { | |
| if ( $options{test} > 2 ) { | |
| print "$configdir exists\n"; | |
| } | |
| } | |
| else { | |
| mkdir $configdir; | |
| print "mkdir $configdir\n"; | |
| } | |
| if ( !-e $dbconfig ) { | |
| $dbconfig = "$configdir\\$dbconfig"; | |
| # check if the config exists in ~/.pbnj-2.0/config.yaml | |
| if ( !-e $dbconfig ) { | |
| open( CONFIG, ">$dbconfig" ); | |
| while (<DATA>) { | |
| if ( defined($_) ) { | |
| print CONFIG $_; | |
| } | |
| } | |
| close(CONFIG); | |
| print "$dbconfig generated\n"; | |
| } | |
| } | |
| } | |
| else { | |
| $configdir = File::HomeDir->my_home; | |
| my $tmpuser = $configdir; | |
| $configdir .= "/.pbnj-2.0"; | |
| $tmpuser =~ s/home//; | |
| $tmpuser =~ s/\///g; | |
| my $uid = getpwnam($tmpuser); | |
| my $gid = id("-g $tmpuser"); | |
| if ( !defined($gid) ) { | |
| error("gid not defined\n"); | |
| } | |
| if ( !defined($uid) ) { | |
| error("uid not defined\n"); | |
| } | |
| if ( -e $configdir and -d $configdir ) { | |
| if ( $options{test} > 2 ) { | |
| print "$configdir exists\n"; | |
| } | |
| } | |
| else { | |
| umask 077; | |
| mkdir $configdir; | |
| print "mkdir $configdir\n"; | |
| safe_chown( $uid, $gid, $configdir ); | |
| } | |
| # check if config exists in the current directory | |
| if ( !-e $dbconfig ) { | |
| $dbconfig = "$configdir/$dbconfig"; | |
| # check if the config exists in ~/.pbnj-2.0/config.yaml | |
| if ( !-e $dbconfig ) { | |
| umask 077; | |
| open( CONFIG, ">$dbconfig" ); | |
| while (<DATA>) { | |
| if ( defined($_) ) { | |
| print CONFIG $_; | |
| } | |
| } | |
| close(CONFIG); | |
| safe_chown( $uid, $gid, $dbconfig ); | |
| print "$dbconfig generated\n"; | |
| } | |
| } | |
| } | |
| ############################################################################## | |
| # | |
| # createMachinesTable: scalar -> scalar | |
| # creates the table for machines in the database | |
| # mid INTEGER PRIMARY KEY, | |
| # | |
| ############################################################################## | |
| sub createMachinesTable { | |
| my $dbh = shift; | |
| my $auto_increment = "AUTO_INCREMENT"; | |
| my $mid_type; | |
| if ( $db eq "SQLite" ) { | |
| $auto_increment = "AUTOINCREMENT"; | |
| $mid_type = "INTEGER PRIMARY KEY $auto_increment"; | |
| } | |
| elsif ( $db eq "mysql" or $db eq "CSV" ) { | |
| $mid_type = "INTEGER PRIMARY KEY AUTO_INCREMENT"; | |
| } | |
| elsif ( $db eq "Pg" ) { | |
| $mid_type = "SERIAL PRIMARY KEY"; | |
| } | |
| else { | |
| print "db not supported\n"; | |
| exit 1; | |
| } | |
| eval { | |
| $dbh->do( | |
| "CREATE TABLE machines ( | |
| mid $mid_type, | |
| ip TEXT, | |
| host TEXT, | |
| localh INTEGER, | |
| os TEXT, | |
| machine_created TEXT, | |
| created_on TEXT)" | |
| ); | |
| }; | |
| if ($@) { | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| ############################################################################## | |
| # | |
| # createService: scalar -> scalar | |
| # creates the table for services in the database | |
| # | |
| ############################################################################## | |
| sub createServicesTable { | |
| my $dbh = shift; | |
| #sid INTEGER PRIMARY KEY, | |
| eval { | |
| $dbh->do( | |
| "CREATE TABLE services ( | |
| mid INTEGER, | |
| service TEXT, | |
| state TEXT, | |
| port INTEGER, | |
| protocol TEXT, | |
| version TEXT, | |
| banner TEXT, | |
| machine_updated TEXT, | |
| updated_on TEXT)" | |
| ); | |
| }; | |
| if ($@) { | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| sub createReportsTable { | |
| my $dbh = shift; | |
| eval { | |
| $dbh->do( | |
| "CREATE TABLE report ( | |
| mid INTEGER PRIMARY KEY AUTO_INCREMENT, | |
| ip TEXT, | |
| host TEXT, | |
| os TEXT, | |
| http INTEGER, | |
| httpproxy INTEGER, | |
| https INTEGER)" | |
| ); | |
| if ($@) { | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| } | |
| ############################################################################## | |
| # | |
| # insertService: anoy hash -> scalar | |
| # inserts information about a service into the database | |
| # | |
| ############################################################################## | |
| sub insertService { | |
| my ($href) = @_; | |
| die "insertService: mid not defined" unless defined $href->{mid}; | |
| die "insertService: service not defined" unless defined $href->{service}; | |
| die "insertService: state not defined" unless defined $href->{state}; | |
| die "insertService: port not defined" unless defined $href->{port}; | |
| die "insertService: proto not defined" unless defined $href->{proto}; | |
| die "insertService: ver not defined" unless defined $href->{ver}; | |
| die "insertService: banner not defined" unless defined $href->{banner}; | |
| die "insertService: updated not defined" unless defined $href->{updated}; | |
| die "insertService: machine_updated not defined" | |
| unless defined $href->{machine_updated}; | |
| my $mid = $href->{mid}; | |
| my $service = $href->{service}; | |
| my $state = $href->{state}; | |
| my $port = $href->{port}; | |
| my $proto = $href->{proto}; | |
| my $ver = $href->{ver}; | |
| my $banner = $href->{banner}; | |
| my $updated = $href->{updated}; | |
| my $machine_updated = $href->{machine_updated}; | |
| my $insert | |
| = $dbh->prepare('INSERT INTO services VALUES (?,?,?,?,?,?,?,?,?)'); | |
| my $success = 1; | |
| $success &&= $insert->execute( | |
| $mid, $service, $state, | |
| $port, $proto, $ver, | |
| $banner, $machine_updated, $updated | |
| ); | |
| return $success; | |
| } | |
| ############################################################################## | |
| # | |
| # setLocal: anoy hash -> scalar | |
| # set the localhost bit | |
| # | |
| ############################################################################## | |
| sub setLocal { | |
| my ($href) = @_; | |
| die "setLocal: ip not defined" unless defined $href->{ip}; | |
| die "setLocal: hostname not defined" unless defined $href->{hostname}; | |
| my $ip = $href->{ip}; | |
| my $hostname = $href->{hostname}; | |
| my $localh = 0; | |
| if ( $hostname =~ m/localhost/ and $ip eq "127.0.0.1" ) { | |
| if ( $options{test} > 0 or $options{debug} > 1 ) { | |
| print "localhost scan\n"; | |
| } | |
| $localh = 1; | |
| } | |
| return $localh; | |
| } | |
| ############################################################################## | |
| # | |
| # insertMachine: anoy hash -> scalar | |
| # inserts information about a machine into the database | |
| # | |
| ############################################################################## | |
| sub insertMachine { | |
| my ($href) = @_; | |
| die "insertMachine: ip not defined" unless defined $href->{ip}; | |
| die "insertMachine: host not defined" unless defined $href->{host}; | |
| die "insertMachine: localh not defined" unless defined $href->{localh}; | |
| die "insertMachine: os not defined" unless defined $href->{os}; | |
| die "insertMachine: created not defined" unless defined $href->{created}; | |
| die "insertMachine: machine_created not defined" | |
| unless defined $href->{machine_created}; | |
| my $ip = $href->{ip}; | |
| my $host = $href->{host}; | |
| my $localh = $href->{localh}; | |
| my $os = $href->{os}; | |
| my $created = $href->{created}; | |
| my $machine_created = $href->{machine_created}; | |
| if ( $db eq "CSV" ) { | |
| my $sth = $dbh->selectrow_hashref("select max('mid') from machines"); | |
| my $mid = $sth->{mid}; | |
| if ( !defined($mid) ) { | |
| $mid = 1; | |
| } | |
| else { | |
| $mid++; | |
| } | |
| my $insert = $dbh->prepare( | |
| 'INSERT INTO machines VALUES (?,?,?,?,?,?,?,?,?)'); | |
| my $success = 1; | |
| $success &&= $insert->execute( $mid, $ip, $host, $localh, $os, | |
| $machine_created, $created ); | |
| return $mid; | |
| } | |
| else { | |
| $dbh->do( | |
| "INSERT INTO machines (ip, host, localh, os, | |
| machine_created, created_on) VALUES ( | |
| '$ip', '$host', '$localh', '$os', | |
| '$machine_created', '$created' ) " | |
| ); | |
| } | |
| my $sth = $dbh->selectrow_hashref( | |
| "SELECT mid from machines | |
| WHERE ip='$ip' AND host='$host' | |
| AND localh='$localh' AND os='$os' AND | |
| machine_created='$machine_created' AND created_on='$created'" | |
| ); | |
| return $sth->{'mid'}; | |
| } | |
| sub insertReport { | |
| my ($href) = @_; | |
| my $ip = $href->{ip}; | |
| } | |
| ############################################################################## | |
| # | |
| # flushTables: -> | |
| # flush the machines and services tables | |
| # | |
| ############################################################################## | |
| sub flushTables { | |
| print "flushing tables\n"; | |
| $dbh->do("DROP TABLE machines"); | |
| $dbh->do("DROP TABLE services"); | |
| } | |
| ############################################################################## | |
| # | |
| # addHost: anoy hash -> scalar | |
| # insert a host only if it is uniq | |
| # | |
| ############################################################################## | |
| sub addHost { | |
| my ($href) = @_; | |
| die "addHost: os not defined" unless defined $href->{os}; | |
| die "addHost: session not defined" unless defined $href->{session}; | |
| die "addHost: host not defined" unless defined $href->{host}; | |
| my $os = $href->{os}; | |
| my $session = $href->{session}; #a Nmap::Parser::Session object | |
| my $host = $href->{host}; | |
| my $mid; | |
| my $ip = $host->addr; | |
| my $machine_created = $session->finish_time(); | |
| my $human_time = localtime($machine_created); | |
| my $uniqhost = 0; | |
| my $hostname = $host->hostname; | |
| my $localh = setLocal( { ip => $ip, hostname => $host->hostname } ); | |
| my $uniq = $dbh->selectall_arrayref( | |
| "select mid from | |
| machines WHERE ip='$ip' AND os='$os' AND host='$hostname' | |
| AND localh='$localh'" | |
| ); | |
| if ( $options{test} > 1 ) { | |
| print "uniq is $uniq\n"; | |
| } | |
| foreach my $uniqtmp (@$uniq) { | |
| my ($midtmp) = @$uniqtmp; | |
| $uniqhost = 1; | |
| $mid = $midtmp; | |
| if ( $options{test} > 0 or $options{debug} > 1 ) { | |
| print "non unique host\n"; | |
| print "mid shift is $mid\n"; | |
| } | |
| } | |
| if ( $uniqhost eq 0 ) { | |
| if ( $colors eq 1 ) { | |
| print BOLD GREEN "Inserting Machine\n"; | |
| } | |
| else { | |
| print "Inserting Machine\n"; | |
| } | |
| $mid = insertMachine( | |
| { ip => $ip, | |
| host => $hostname, | |
| localh => $localh, | |
| os => $os, | |
| created => $human_time, | |
| machine_created => $machine_created | |
| } | |
| ); | |
| if ( $options{test} > 0 or $options{debug} > 1 ) { | |
| ; | |
| print "mid is $mid\n"; | |
| } | |
| } | |
| else { | |
| print "Machine is already in the database\n"; | |
| print "Checking Current Services\n"; | |
| } | |
| return ( $mid, $uniqhost ); | |
| } | |
| ############################################################################## | |
| # | |
| # insertServiceDB: anoy hash -> | |
| # take information about a services from the database and insert it as | |
| # as being down | |
| # | |
| ############################################################################## | |
| sub insertServiceDB { | |
| my ($href) = @_; | |
| die "insertServiceDB: mid not defined" unless defined $href->{mid}; | |
| die "insertServiceDB: port not defined" unless defined $href->{port}; | |
| die "insertServiceDB: state not defined" unless defined $href->{state}; | |
| die "insertServiceDB: proto not defined" unless defined $href->{proto}; | |
| die "insertSerciceDB: updated not defined" | |
| unless defined $href->{updated}; | |
| die "insertSerciceDB: machine_updated not defined" | |
| unless defined $href->{machine_updated}; | |
| my $mid = $href->{mid}; | |
| my $port = $href->{port}; | |
| my $state = $href->{state}; | |
| my $proto = $href->{proto}; | |
| my $human_time = $href->{updated}; | |
| my $machine_updated = $href->{machine_updated}; | |
| my $uniq_service = $dbh->selectall_arrayref( | |
| "SELECT | |
| service,version,protocol,banner | |
| FROM services WHERE mid='$mid' AND port='$port' | |
| AND protocol='$proto' AND state='up' | |
| ORDER BY machine_updated DESC" | |
| ); | |
| my ( $version, $service, $banner, $uniqservice ); | |
| foreach my $uniqtmp (@$uniq_service) { | |
| ( $service, $version, $proto, $banner ) = @$uniqtmp; | |
| #print "service is $service\n"; | |
| } | |
| if ( $colors eq 1 ) { | |
| print BOLD RED "\t! Service $port:$proto $service is down\n"; | |
| } | |
| else { | |
| print "\t! Service $port:$proto $service is down\n"; | |
| } | |
| insertService( | |
| { mid => $mid, | |
| service => $service, | |
| port => $port, | |
| proto => $proto, | |
| ver => $version, | |
| state => $state, | |
| banner => $banner, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| } | |
| ############################################################################## | |
| # | |
| # insertServiceScan: anoy hash -> | |
| # take information about a services from the scan and insert it as | |
| # as being up | |
| # | |
| ############################################################################## | |
| sub insertServiceScan { | |
| my ($href) = @_; | |
| die "insertServiceScan: host not defined" unless defined $href->{host}; | |
| die "insertServiceScan: mid not defined" unless defined $href->{mid}; | |
| die "insertServiceScan: port not defined" unless defined $href->{port}; | |
| die "insertServiceScan: state not defined" unless defined $href->{state}; | |
| die "insertServiceScan: proto not defined" unless defined $href->{proto}; | |
| die "insertServiceScan: change not defined" | |
| unless defined $href->{change}; | |
| die "insertSerciceScan: updated not defined" | |
| unless defined $href->{updated}; | |
| die "insertSerciceScan: machine_updated not defined" | |
| unless defined $href->{machine_updated}; | |
| my $host = $href->{host}; | |
| my $mid = $href->{mid}; | |
| my $port = $href->{port}; | |
| my $state = $href->{state}; | |
| my $proto = $href->{proto}; | |
| my $human_time = $href->{updated}; | |
| my $machine_updated = $href->{machine_updated}; | |
| my $change = $href->{change}; | |
| my $service; | |
| if ( $proto eq "tcp" ) { | |
| $service = $host->tcp_service($port); | |
| } | |
| else { | |
| $service = $host->udp_service($port); | |
| } | |
| my $name = "unknown name"; | |
| my $product = "unknown product"; | |
| my $version = "unknown version"; | |
| if ( defined( $service->name ) ) { | |
| $name = $service->name; | |
| } | |
| if ( defined( $service->version ) ) { | |
| $version = $service->version; | |
| } | |
| if ( defined( $service->product ) ) { | |
| $product = $service->product; | |
| } | |
| if ( $change == 1 ) { | |
| if ( $colors == 1 ) { | |
| print BOLD GREEN "\tInserting Service on $port:$proto $name\n"; | |
| } | |
| else { | |
| print "\tInserting Service on $port:$proto $name\n"; | |
| } | |
| } | |
| insertService( | |
| { mid => $mid, | |
| service => $name, | |
| port => $port, | |
| proto => $proto, | |
| ver => $version, | |
| state => $state, | |
| banner => $product, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| } | |
| ############################################################################## | |
| # | |
| # cmpService: anoy hash -> scalar | |
| # compare the service in the database and the service from the scan. | |
| # Note: | |
| # If the service is up the database & is not in the scan then the state is down. | |
| # If the service is not in the database & is in the scan then the state is up. | |
| # | |
| ############################################################################## | |
| sub cmpService { | |
| my ($href) = @_; | |
| die "cmpService: host not defined" unless defined $href->{host}; | |
| die "cmpService: mid not defined" unless defined $href->{mid}; | |
| die "cmpService: port not defined" unless defined $href->{port}; | |
| die "cmpService: state not defined" unless defined $href->{state}; | |
| die "cmpService: proto not defined" unless defined $href->{proto}; | |
| die "cmpService: updated not defined" unless defined $href->{updated}; | |
| die "cmpService: machine_updated not defined" | |
| unless defined $href->{machine_updated}; | |
| die "cmpService: db_updated not defined" | |
| unless defined $href->{db_updated}; | |
| die "cmpService: db_machine_updated not defined" | |
| unless defined $href->{db_machine_updated}; | |
| my $host = $href->{host}; | |
| my $mid = $href->{mid}; | |
| my $human_time = $href->{updated}; | |
| my $machine_updated = $href->{machine_updated}; | |
| my $state = $href->{state}; | |
| my $port = $href->{port}; | |
| my $proto = $href->{proto}; | |
| my $db_updated = $href->{db_updated}; | |
| my $db_machine_updated = $href->{db_machine_updated}; | |
| my $service; | |
| if ( $proto eq "tcp" ) { | |
| $service = $host->tcp_service($port); | |
| } | |
| elsif ( $proto eq "udp" ) { | |
| $service = $host->udp_service($port); | |
| } | |
| else { | |
| print "error in $proto\n"; | |
| exit 1; | |
| } | |
| my $banner_from_scan = "unknown product"; | |
| my $version_from_scan = "unknown version"; | |
| my $port_from_scan = $service->port; | |
| my $service_name_from_scan = "unknown service"; | |
| if ( defined( $service->name ) ) { | |
| $service_name_from_scan = $service->name; | |
| } | |
| if ( $options{test} eq 1 ) { | |
| print "service name from scan is $service_name_from_scan\n"; | |
| } | |
| if ( defined( $service->version ) ) { | |
| $version_from_scan = $service->version; | |
| if ( $options{test} > 2 ) { | |
| print "version from scan is $version_from_scan\n"; | |
| } | |
| } | |
| if ( defined( $service->product ) ) { | |
| $banner_from_scan = $service->product; | |
| if ( $options{test} > 2 ) { | |
| print "banner from scan is $banner_from_scan\n"; | |
| } | |
| } | |
| my ( $version_from_db, $service_name_from_db, $banner_from_db ); | |
| # note the version banner and service name are the only elements | |
| # of the service that we can comparing | |
| my $uniq_service = $dbh->selectall_arrayref( | |
| "SELECT | |
| service,version,banner FROM services | |
| WHERE mid='$mid' AND | |
| port='$port' AND state='$state' | |
| AND protocol='$proto' | |
| AND updated_on='$db_updated' | |
| AND machine_updated='$db_machine_updated'" | |
| ); | |
| foreach my $uniqtmp (@$uniq_service) { | |
| my ( $nametmp, $vertmp, $bantmp ) = @$uniqtmp; | |
| $service_name_from_db = $nametmp; | |
| $version_from_db = $vertmp; | |
| $banner_from_db = $bantmp; | |
| } | |
| if ( $options{test} > 1 ) { | |
| print "state is $state\n"; | |
| "comparing service $service_name_from_db and $service_name_from_scan\n"; | |
| print "comparing version $version_from_db and $version_from_scan\n"; | |
| print "comparing banner $banner_from_db and $banner_from_scan\n"; | |
| } | |
| if ( $diff eq "banner" | |
| && $version_from_db eq $version_from_scan | |
| && $banner_from_db eq $banner_from_scan | |
| && $service_name_from_db eq $service_name_from_scan ) | |
| { | |
| "\t= $service_name_from_db:$port is ($version_from_db) $banner_from_db\n"; | |
| return 1; | |
| } | |
| elsif ($diff ne "banner" | |
| && $version_from_db eq $version_from_scan | |
| && $service_name_from_db eq $service_name_from_scan ) | |
| { | |
| "\t= $service_name_from_db:$port is ($version_from_db) $banner_from_db\n"; | |
| return 1; | |
| } | |
| else { | |
| if ( $colors == 1 ) { | |
| print BOLD YELLOW | |
| "\t! $service_name_from_db:$port is ($version_from_db) $banner_from_db\n"; | |
| } | |
| else { | |
| "\t! $service_name_from_db:$port is ($version_from_db) $banner_from_db\n"; | |
| } | |
| if ( $service_name_from_db ne $service_name_from_scan ) { | |
| if ( $colors == 1 ) { | |
| print BOLD YELLOW | |
| " Service Name was \"$service_name_from_db\" changed to \"$service_name_from_scan\"\n"; | |
| } | |
| else { | |
| " Service Name was \"$service_name_from_db\" changed to \"$service_name_from_scan\"\n"; | |
| } | |
| } | |
| elsif ( $version_from_db ne $version_from_scan ) { | |
| if ( $colors == 1 ) { | |
| print BOLD YELLOW | |
| " Version was \"$version_from_db\" changed to \"$version_from_scan\"\n"; | |
| } | |
| else { | |
| " Version was \"$version_from_db\" changed to \"$version_from_scan\"\n"; | |
| } | |
| } | |
| elsif ( $diff eq 'banner' and $banner_from_db ne $banner_from_scan ) { | |
| if ( $colors == 1 ) { | |
| print BOLD YELLOW | |
| " Banner was \"$banner_from_db\" changed to \"$banner_from_scan\"\n"; | |
| } | |
| else { | |
| " Banner was \"$banner_from_db\" changed to \"$banner_from_scan\"\n"; | |
| } | |
| } | |
| else { | |
| print "version or banner difference\n"; | |
| } | |
| # set time to earlier so insert works after old verison is down | |
| $machine_updated = scalar($machine_updated); | |
| $machine_updated -= 1; | |
| insertService( | |
| { mid => $mid, | |
| service => $service_name_from_db, | |
| port => $port, | |
| proto => $proto, | |
| ver => $version_from_db, | |
| state => 'down', | |
| banner => $banner_from_db, | |
| updated => $human_time, | |
| machine_updated => $machine_updated, | |
| } | |
| ); | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| ############################################################################## | |
| # | |
| # setAndInsertService: anoy hash -> scalar | |
| # store all the services in the scan into the database | |
| # | |
| ############################################################################## | |
| sub setAndInsertService { | |
| my ($href) = @_; | |
| die "setAndInsertService: mid not defined" unless defined $href->{mid}; | |
| die "setAndInsertService: proto not defined" | |
| unless defined $href->{proto}; | |
| die "setAndInsertService: service not defined" | |
| unless defined $href->{service}; | |
| die "setAndInsertService: updated not defined" | |
| unless defined $href->{updated}; | |
| die "setAndInsertService: machine_updated not defined" | |
| unless defined $href->{machine_updated}; | |
| my $mid = $href->{mid}; | |
| my $human_time = $href->{updated}; | |
| my $machine_updated = $href->{machine_updated}; | |
| my $service = $href->{service}; | |
| my $proto = $href->{proto}; | |
| my $state = 'up'; | |
| my $product_from_scan = "unknown product"; | |
| my $version_from_scan = "unknown version"; | |
| my $port_from_scan = $service->port; | |
| my $name_from_scan = "unknown service"; | |
| if ( defined( $service->name ) ) { | |
| $name_from_scan = $service->name; | |
| } | |
| if ( defined( $service->version ) ) { | |
| $version_from_scan = $service->version; | |
| } | |
| if ( defined( $service->product ) ) { | |
| $product_from_scan = $service->product; | |
| } | |
| print "Inserting Service on $port_from_scan:$proto $name_from_scan\n"; | |
| insertService( | |
| { mid => $mid, | |
| service => $name_from_scan, | |
| port => $port_from_scan, | |
| proto => $proto, | |
| ver => $version_from_scan, | |
| state => $state, | |
| banner => $product_from_scan, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| } | |
| ############################################################################## | |
| # | |
| # storeEverything: anoy hash -> scalar | |
| # store all the services in the scan into the database | |
| # | |
| ############################################################################## | |
| sub storeEverything { | |
| my ($href) = @_; | |
| die "storeEverything: host not defined" unless defined $href->{host}; | |
| die "storeEverything: mid not defined" unless defined $href->{mid}; | |
| die "storeEverything: updated not defined" | |
| unless defined $href->{updated}; | |
| die "storeEverything: machine_updated not defined" | |
| unless defined $href->{machine_updated}; | |
| my $host = $href->{host}; | |
| my $mid = $href->{mid}; | |
| my $human_time = $href->{updated}; | |
| my $machine_updated = $href->{machine_updated}; | |
| my $state; | |
| # $port is up in the database | |
| # store all the services running on tcp ports | |
| foreach ( $host->tcp_ports('open') ) { | |
| my $state = $host->tcp_port_state($_); | |
| if ( $options{test} eq 1 ) { | |
| print "state is $state on port $_\n"; | |
| } | |
| next if ( $state ne "open" ); | |
| my $service = $host->tcp_service($_); | |
| next if ( !defined($service) ); | |
| #my $uniqservice = 1; | |
| setAndInsertService( | |
| { proto => 'tcp', | |
| service => $service, | |
| mid => $mid, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| } | |
| # store all the services running on tcp ports | |
| foreach ( $host->udp_ports('open') ) { | |
| my $state = $host->udp_port_state($_); | |
| if ( $options{test} eq 1 ) { | |
| print "in udp loop\n"; | |
| print "state is $state on port $_\n"; | |
| } | |
| next if ( $state ne "open" ); | |
| my $service = $host->udp_service($_); | |
| next if ( !defined($service) ); | |
| setAndInsertService( | |
| { proto => 'udp', | |
| service => $service, | |
| mid => $mid, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| } | |
| return 0; | |
| } | |
| ############################################################################## | |
| # | |
| # sortDB: anoy hash -> hash | |
| # sort the database based on the time | |
| # | |
| ############################################################################## | |
| sub sortDB { | |
| my ($href) = @_; | |
| die "sortDB: mid not defined" unless defined $href->{mid}; | |
| die "sortDB: updated not defined" unless defined $href->{updated}; | |
| die "sortDB: machine_updated not defined" | |
| unless defined $href->{machine_updated}; | |
| my $mid = $href->{mid}; | |
| my $human_time = $href->{updated}; | |
| my $machine_updated = $href->{machine_updated}; | |
| my ( $port, $service, $banner, $state, $state_db ); | |
| # determines all ports that are in the database and not in the scan | |
| # thus we assume these services went down | |
| # may 30 added limit 1 | |
| my ( %port_db, %seen_ports, $scan_state, $scan_proto ); | |
| my $ports_db_ref = $dbh->selectall_arrayref( | |
| "SELECT | |
| port,state,protocol,machine_updated,updated_on FROM | |
| services WHERE mid='$mid' ORDER BY machine_updated DESC " | |
| ); | |
| foreach my $key (@$ports_db_ref) { | |
| my ( $port, $state, $proto, $machine_updated, $updated ) = @$key; | |
| if ( !$port_db{$port}{machine_updated} ) { | |
| $port_db{$port}{machine_updated} = $machine_updated; | |
| $port_db{$port}{updated} = $updated; | |
| $port_db{$port}{port} = $port; | |
| $port_db{$port}{state} = $state; | |
| $port_db{$port}{proto} = $proto; | |
| } | |
| else { | |
| #print "not less than and exists\n"; | |
| } | |
| } | |
| return %port_db; | |
| } | |
| ############################################################################## | |
| # | |
| # setDown: anoy hash -> | |
| # remove all services on a given protocol as the host has no ports on | |
| # this protocol. | |
| # | |
| ############################################################################## | |
| sub setDown { | |
| my ($href) = @_; | |
| die "setDown: mid not defined" unless defined $href->{mid}; | |
| die "setDown: host not defined" unless defined $href->{host}; | |
| die "setDown: proto not defined" unless defined $href->{proto}; | |
| die "setDown: portdb not defined" unless defined $href->{portdb}; | |
| die "setDown: updated not defined" unless defined $href->{updated}; | |
| die "setDown: machine_updated not defined" | |
| unless defined $href->{machine_updated}; | |
| my $host = $href->{host}; | |
| my $human_time = $href->{updated}; | |
| my $machine_updated = $href->{machine_updated}; | |
| my $mid = $href->{mid}; | |
| my $proto = $href->{proto}; | |
| my $db_ref = $href->{portdb}; | |
| my %portdb = %{$db_ref}; | |
| if ( $options{test} > 0 ) { | |
| print "protocol is $proto\n"; | |
| } | |
| my ( $scan_state, %seen_ports ); | |
| foreach my $port ( sort { $a cmp $b } keys %portdb ) { | |
| #print "key is $port\n"; | |
| if ( $options{test} eq 1 ) { | |
| print $portdb{$port}{state} . " and $port\n"; | |
| print "port db $port proto $proto\n"; | |
| } | |
| $scan_state = "down"; | |
| if ( $options{test} eq 1 ) { | |
| print "scan thinks the status of $port is $scan_state\n"; | |
| print "state is " . $portdb{$port}{state} . "\n"; | |
| } | |
| if ( $scan_state eq "down" | |
| && $portdb{$port}{'state'} eq "up" | |
| && $portdb{$port}{proto} | |
| && $portdb{$port}{proto} eq $proto ) | |
| { | |
| insertServiceDB( | |
| { mid => $mid, | |
| port => $port, | |
| state => $scan_state, | |
| proto => $proto, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| if ( $options{test} eq 1 ) { | |
| print "pulling data about service from database\n"; | |
| } | |
| } | |
| } | |
| } | |
| ############################################################################## | |
| # | |
| # cmpAndInsert: anoy hash -> | |
| # add service changes to the database | |
| # | |
| ############################################################################## | |
| sub cmpAndInsert { | |
| my ($href) = @_; | |
| die "cmpAndInsert: mid not defined" unless defined $href->{mid}; | |
| die "cmpAndInsert: host not defined" unless defined $href->{host}; | |
| die "cmpAndInsert: proto not defined" unless defined $href->{proto}; | |
| die "cmpAndInsert: portdb not defined" unless defined $href->{portdb}; | |
| die "cmpAndInsert: updated not defined" unless defined $href->{updated}; | |
| die "cmpAndInsert: machine_updated not defined" | |
| unless defined $href->{machine_updated}; | |
| my $host = $href->{host}; | |
| my $human_time = $href->{updated}; | |
| my $machine_updated = $href->{machine_updated}; | |
| my $mid = $href->{mid}; | |
| my $proto = $href->{proto}; | |
| my $db_ref = $href->{portdb}; | |
| my %portdb = %{$db_ref}; | |
| if ( $options{test} eq 1 or $options{debug} eq 1 ) { | |
| print "protocol is $proto\n"; | |
| } | |
| my ( $scan_state, %seen_ports ); | |
| foreach my $port ( sort { $a cmp $b } keys %portdb ) { | |
| if ( $options{test} eq 1 ) { | |
| print $portdb{$port}{state} . " and $port\n"; | |
| print "port db $port proto $proto\n"; | |
| } | |
| my $tmp; | |
| if ( $proto eq "tcp" ) { | |
| $tmp = $host->tcp_port_state($port); | |
| } | |
| elsif ( $proto eq "udp" ) { | |
| $tmp = $host->udp_port_state($port); | |
| } | |
| else { | |
| next; | |
| } | |
| $seen_ports{$port} = "up"; | |
| if ($tmp) { | |
| $scan_state = "up"; | |
| } | |
| else { | |
| $scan_state = "down"; | |
| } | |
| if ( $options{test} eq 1 ) { | |
| print "scan thinks the status of $port is $scan_state\n"; | |
| print "state is " . $portdb{$port}{state} . "\n"; | |
| } | |
| if ( $portdb{$port}{state} eq "up" | |
| && $scan_state eq "up" | |
| && $portdb{$port}{proto} | |
| && $portdb{$port}{proto} eq $proto ) | |
| { | |
| if ( $options{test} eq 1 ) { | |
| print "states are both up\n"; | |
| print "checking versions and banners\n"; | |
| } | |
| my $db_updated = $portdb{$port}{updated}; | |
| my $db_machine_updated = $portdb{$port}{machine_updated}; | |
| my $cmp = cmpService( | |
| { mid => $mid, | |
| host => $host, | |
| port => $port, | |
| state => $scan_state, | |
| proto => $proto, | |
| updated => $human_time, | |
| machine_updated => $machine_updated, | |
| db_updated => $db_updated, | |
| db_machine_updated => $db_machine_updated, | |
| } | |
| ); | |
| if ( $cmp eq 0 ) { | |
| if ( $options{test} > 1 ) { | |
| print "port is $port\n"; | |
| print "mark 1\n"; | |
| } | |
| insertServiceScan( | |
| { mid => $mid, | |
| host => $host, | |
| port => $port, | |
| state => $scan_state, | |
| proto => $proto, | |
| updated => $human_time, | |
| machine_updated => $machine_updated, | |
| change => $cmp, | |
| } | |
| ); | |
| } | |
| else { | |
| if ( $options{test} eq 1 ) { | |
| print "no difference in name or version or banner\n"; | |
| } | |
| } | |
| } | |
| else { | |
| #print "scan state is $scan_state\n"; | |
| #print "database state is $port_db{$port}{'state'}\n"; | |
| if ( $scan_state eq "down" | |
| && $portdb{$port}{'state'} eq "up" | |
| && $portdb{$port}{proto} | |
| && $portdb{$port}{proto} eq $proto ) | |
| { | |
| insertServiceDB( | |
| { mid => $mid, | |
| port => $port, | |
| state => $scan_state, | |
| proto => $proto, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| if ( $options{test} eq 1 ) { | |
| print "pulling data about service from database\n"; | |
| } | |
| } | |
| elsif ($scan_state eq "up" | |
| && $portdb{$port}{'state'} eq "down" | |
| && $portdb{$port}{proto} | |
| && $portdb{$port}{proto} eq $proto ) | |
| { | |
| if ( $options{test} > 1 ) { | |
| print "port is $port\n"; | |
| print "mark 2\n"; | |
| } | |
| insertServiceScan( | |
| { host => $host, | |
| mid => $mid, | |
| port => $port, | |
| state => $scan_state, | |
| proto => $proto, | |
| updated => $human_time, | |
| machine_updated => $machine_updated, | |
| change => 1, | |
| } | |
| ); | |
| if ( $options{test} eq 1 ) { | |
| print "pulling data about service from scan\n"; | |
| } | |
| } | |
| else { | |
| if ( $portdb{$port}{proto} | |
| && $portdb{$port}{proto} eq $proto ) | |
| { | |
| if ( $options{test} eq 1 ) { | |
| print "states are both down\n"; | |
| } | |
| } | |
| else { | |
| if ( $options{test} > 1 ) { | |
| print "protocols are different\n"; | |
| } | |
| } | |
| } | |
| } | |
| chomp( $portdb{$port}{updated} ); | |
| #print "\n$port_db{$port}{port} keys is $port\n"; | |
| #print "\n$port_db{$port}{state} keys is $port\n"; | |
| #print "\n$port_db{$port}{updated} keys is $port\n"; | |
| } | |
| # bug for seen ports on different protocols | |
| if ( $proto eq "tcp" ) { | |
| # handles ports that are open in the scan and don't exist in the database | |
| foreach ( $host->tcp_ports('open') ) { | |
| my $tmptest = $host->tcp_port_state($_); | |
| if ( $options{test} eq 1 ) { | |
| print "state is $tmptest\n"; | |
| } | |
| next if ( $tmptest ne "open" ); | |
| my $port_tmp = $_; | |
| my $state_tmp; | |
| if ( $seen_ports{$port_tmp} && $seen_ports{$port_tmp} eq "up" ) { | |
| if ( $options{test} eq 1 ) { | |
| print "Port is already up\n"; | |
| } | |
| } | |
| else { | |
| $state_tmp = "up"; | |
| if ( $options{test} > 1 ) { | |
| print "inserting scan not in the database on $port_tmp\n"; | |
| print "mark 3\n"; | |
| } | |
| insertServiceScan( | |
| { host => $host, | |
| mid => $mid, | |
| port => $port_tmp, | |
| proto => $proto, | |
| state => $state_tmp, | |
| proto => $proto, | |
| updated => $human_time, | |
| machine_updated => $machine_updated, | |
| change => 1, | |
| } | |
| ); | |
| } | |
| } | |
| } | |
| else { | |
| # handles ports that are open in the scan and don't exist in the database | |
| foreach ( $host->udp_ports('open') ) { | |
| my $tmptest = $host->udp_port_state($_); | |
| if ( $options{test} eq 1 ) { | |
| print "state is $tmptest\n"; | |
| } | |
| next if ( $tmptest ne "open" ); | |
| my $port_tmp = $_; | |
| my $state_tmp; | |
| if ( $seen_ports{$port_tmp} && $seen_ports{$port_tmp} eq "up" ) { | |
| if ( $options{test} eq 1 ) { | |
| print "Port is already up\n"; | |
| } | |
| } | |
| else { | |
| $state_tmp = "up"; | |
| if ( $options{test} > 1 ) { | |
| print "inserting scan not in the database on $port_tmp\n"; | |
| print "mark 4\n"; | |
| } | |
| insertServiceScan( | |
| { host => $host, | |
| mid => $mid, | |
| port => $port_tmp, | |
| state => $state_tmp, | |
| proto => $proto, | |
| updated => $human_time, | |
| machine_updated => $machine_updated, | |
| change => 0, | |
| } | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| ############################################################################## | |
| # | |
| # addServices: anoy hash -> | |
| # add service changes to the database | |
| # | |
| ############################################################################## | |
| sub addServices { | |
| my ($href) = @_; | |
| die "addServices: mid not defined" unless defined $href->{mid}; | |
| die "addServices: host not defined" unless defined $href->{host}; | |
| die "addServices: updated not defined" unless defined $href->{updated}; | |
| die "addServices: uniq not defined" unless defined $href->{uniq}; | |
| die "addServices: machine_updated not defined" | |
| unless defined $href->{machine_updated}; | |
| my $host = $href->{host}; | |
| my $human_time = $href->{updated}; | |
| my $machine_updated = $href->{machine_updated}; | |
| my $mid = $href->{mid}; | |
| my $uniq = $href->{uniq}; | |
| my ( $port, $service, $banner, $state, $state_db ); | |
| my %port_db = sortDB( | |
| { host => $host, | |
| mid => $mid, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| # determines all ports that are in the database and not in the scan | |
| # thus we assume these services went down | |
| if ( $uniq eq 0 ) { | |
| if ( $options{test} > 0 ) { | |
| print "store everything from the scan\n"; | |
| } | |
| storeEverything( | |
| { host => $host, | |
| mid => $mid, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| return 0; | |
| } | |
| my @tcp_ports = $host->tcp_open_ports(); | |
| my @udp_ports = $host->udp_open_ports(); | |
| if ( !defined( scalar(@tcp_ports) ) ) { | |
| if ( $options{debug} > 0 or $options{test} eq 1 ) { | |
| print "no TCP ports found\n"; | |
| } | |
| setDown( | |
| { mid => $mid, | |
| host => $host, | |
| proto => "tcp", | |
| portdb => \%port_db, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| } | |
| else { | |
| cmpAndInsert( | |
| { mid => $mid, | |
| host => $host, | |
| proto => "tcp", | |
| portdb => \%port_db, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| } | |
| if ( !defined( scalar(@udp_ports) ) ) { | |
| if ( $options{debug} > 0 or $options{test} eq 1 ) { | |
| print "no UDP ports found\n"; | |
| } | |
| setDown( | |
| { mid => $mid, | |
| host => $host, | |
| proto => "udp", | |
| portdb => \%port_db, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| } | |
| else { | |
| cmpAndInsert( | |
| { mid => $mid, | |
| host => $host, | |
| proto => "udp", | |
| portdb => \%port_db, | |
| updated => $human_time, | |
| machine_updated => $machine_updated | |
| } | |
| ); | |
| } | |
| } | |
| ############################################################################## | |
| # | |
| # storeData: scalar -> | |
| # function to perform scans and insert hosts and services as needed | |
| # | |
| ############################################################################## | |
| sub storeData { | |
| my $mTable = createMachinesTable($dbh); | |
| if ( $mTable eq 0 ) { | |
| print "creating Machines Table\n"; | |
| } | |
| my $sTable = createServicesTable($dbh); | |
| if ( $sTable eq 0 ) { | |
| print "creating Services Table\n"; | |
| } | |
| my $created_on = $np->get_session()->finish_time(); | |
| my $human_time = localtime($created_on); | |
| for my $host ( $np->all_hosts() ) { | |
| my $os = $host->os_sig(); | |
| my $os_scan; | |
| if ( defined($os) | |
| and defined( $os->osfamily ) | |
| and defined( $os->osgen ) ) | |
| { | |
| $os_scan = $os->osfamily . " " . $os->osgen; | |
| } | |
| else { | |
| $os_scan = "unknown os"; | |
| } | |
| if ( !defined($os_scan) or $os_scan eq "" ) { | |
| $os_scan = "unknown os"; | |
| } | |
| print "\n--------------------------------------\n"; | |
| print "Starting Scan of " . $host->addr . "\n"; | |
| my ( $mid, $uniq ) = addHost( | |
| { os => $os_scan, | |
| session => $np->get_session(), | |
| host => $host, | |
| created_on => $human_time, | |
| machine_created => $created_on | |
| } | |
| ); | |
| addServices( | |
| { mid => $mid, | |
| host => $host, | |
| updated => $human_time, | |
| machine_updated => $created_on, | |
| uniq => $uniq, | |
| } | |
| ); | |
| print "Scan Complete for " . $host->addr . "\n"; | |
| print "--------------------------------------\n"; | |
| } | |
| print "\n"; | |
| } | |
| ############################################################################## | |
| # | |
| # verify_windows: array ref -> | |
| # verify that the windows machine isn't trying to scanning itself | |
| # side effect: calls verify_scan and possibly quits | |
| # | |
| ############################################################################## | |
| sub verify_windows { | |
| my $range = shift; | |
| foreach (@$range) { | |
| if ( $_ =~ m/127\.0\.0\.1/ ) { | |
| print "Error: Win32 machines can't scan themselves.\n"; | |
| exit 1; | |
| } | |
| } | |
| verify_scan(); | |
| } | |
| ############################################################################## | |
| # | |
| # verify_ip: anoy hash -> scalar | |
| # determine if ip is in acceptable range | |
| # | |
| ############################################################################## | |
| sub verify_ip { | |
| my ($href) = @_; | |
| die "verify_ip: oct1 not defined" unless defined $href->{oct1}; | |
| die "verify_ip: oct2 not defined" unless defined $href->{oct2}; | |
| die "verify_ip: oct3 not defined" unless defined $href->{oct3}; | |
| die "verify_ip: oct4 not defined" unless defined $href->{oct4}; | |
| my $oct1 = $href->{oct1}; | |
| my $oct2 = $href->{oct2}; | |
| my $oct3 = $href->{oct3}; | |
| my $oct4 = $href->{oct4}; | |
| if ( $oct1 < 255 and $oct1 > 0 ) { | |
| if ( ( $oct2 eq "*" or $oct2 eq "\* " ) | |
| or ( $oct2 == 0 ) | |
| or ( $oct2 < 256 and $oct2 > 0 ) ) | |
| { | |
| if ( ( $oct3 eq "*" or $oct3 eq "\*" ) | |
| or ( $oct3 == 0 ) | |
| or ( $oct3 < 256 and $oct3 > 0 ) ) | |
| { | |
| if ( ( $oct4 eq "*" or $oct4 eq "\*" ) | |
| or ( $oct4 =~ /0\/\d{1,2}/ ) | |
| or ( $oct4 < 256 and $oct4 > 0 ) ) | |
| { | |
| return 1; | |
| } | |
| else { | |
| print "fourth is $4\n" if ( $options{test} > 2 ); | |
| } | |
| } | |
| else { | |
| print "third is $3\n" if ( $options{test} > 2 ); | |
| } | |
| } | |
| else { | |
| print "second is $2\n" if ( $options{test} > 3 ); | |
| } | |
| } | |
| return 0; | |
| } | |
| ############################################################################## | |
| # | |
| # verify_scan | |
| # verifies that the nmap path is set | |
| # side effect: quits if the nmap path is not set | |
| # | |
| ############################################################################## | |
| sub verify_scan { | |
| if ( !defined($nmapPath) or $nmapPath eq "" ) { | |
| print "Error: Nmap was not found in your path.\n"; | |
| print "Please download the latest version from\n"; | |
| print "http://www.insecure.org/nmap/download.html\n"; | |
| exit 1; | |
| } | |
| } | |
| ############################################################################## | |
| # | |
| # help -> | |
| # display help information | |
| # side effect: exits program | |
| # | |
| ############################################################################## | |
| sub help { | |
| print "Usage: $PROG [Options] {target specification} | |
| Target Specification: | |
| Can pass hostnames, IP addresses, networks, etc. | |
| Ex: microsoft.com, 192.168.0.1, 192.168.1.1/24, 10.0.0.1-254 | |
| -i --iplist <iplist> Scan using a list of IPs from a file | |
| -x --xml <xml-file> Parse scan/info from Nmap XML file | |
| Scan Options: | |
| -a --args <args> Execute Nmap with args (needs quotes) | |
| -e --extraargs <args> Add args to the default args (needs quotes) | |
| --inter <interface> Perform Nmap Scan using non default interface | |
| -m --moreports <ports> Add ports to scan ex: 8080 or 3306,5900 | |
| -n --nmap <path> Path to Nmap executable | |
| -p --pingscan Ping Target then scan the host(s) that are alive | |
| --udp Add UDP to the scan arguments | |
| --rpc Add RPC to the scan arguments | |
| -r --range <ports> Ports for scan [def 1-1025] | |
| --diffbanner Parse changes of the banner | |
| Config Options: | |
| -d --dbconfig <config> Config for results database [def config.yaml] | |
| --configdir <dir> Directory for the database config file | |
| --data <file> SQLite Database override [def data.dbl] | |
| --dir <dir> Directory for SQLite or CSV file [def . ] | |
| General Options: | |
| --nocolors Don't Print Colors | |
| --test <level> Testing information | |
| --debug <level> Debug information | |
| -v --version Display version | |
| -h --help Display this information | |
| Send Comments to Joshua D. Abraham ( jabra\@ccs.neu.edu )\n"; | |
| exit; | |
| } | |
| ############################################################################## | |
| # | |
| # print_version -> | |
| # displays version | |
| # side effect: exits program | |
| # | |
| ############################################################################## | |
| sub print_version { | |
| print "$PROG version $VERSION by $AUTH\n"; | |
| exit; | |
| } | |
| ############################################################################## | |
| if ( @ARGV == 0 ) { | |
| help; | |
| exit; | |
| } | |
| GetOptions( | |
| \%options, | |
| 'pingscan|p', 'scan|s', 'iplist|i=s', 'xml|x=s', 'range|r=s', | |
| 'moreports|m=s', 'nmap|n=s', | |
| 'dbconfig|d=s', 'dir=s', 'data=s', 'configdir=s', 'test=s', | |
| 'args|a=s', 'extraargs|e=s', 'udp', 'rpc', 'inter=s', 'diffbanner', | |
| 'help|h' => sub { help(); }, | |
| 'version|v' => sub { print_version(); }, | |
| 'nocolors' => sub { $colors = 0; }, | |
| 'fruitycolors' => sub { $colors = 2; }, | |
| 'debug=s', | |
| ) | |
| or exit 1; | |
| my $dephosts = ""; | |
| if ($options{'scan'}){ | |
| print "WARNING: --scan is deprecated\n"; | |
| print "Please use the following format: $PROG 127.0.0.1 \n"; | |
| $dephosts = $options{'scan'}; | |
| } | |
| if ( ( $options{'iplist'} or $options{'pingscan'} ) | |
| and $options{'xml'} ) | |
| { | |
| print "Invalid type of input\n"; | |
| exit 1; | |
| } | |
| if ( $options{'input'} and $options{'pingscan'} ) { | |
| print "Conflicting type of Scan\n"; | |
| } | |
| if ( $options{'dir'} ) { | |
| $dir = $options{'dir'}; | |
| } | |
| if ( $options{'configdir'} ) { | |
| $configdir = $options{'configdir'}; | |
| } | |
| if ( $options{'dbconfig'} ) { | |
| my $tmpconfig; | |
| if ( $^O eq 'MSWin32' || $^O =~ /cygwin/ ) { | |
| $tmpconfig = $configdir . '\\' . $options{'dbconfig'}; | |
| } | |
| else { | |
| $tmpconfig = $configdir . "/" . $options{'dbconfig'}; | |
| } | |
| if ( -e $tmpconfig && -r $tmpconfig ) { | |
| $dbconfig = $tmpconfig; | |
| } | |
| } | |
| chdir($dir) or die "Couldn't change to $dir directory\n"; | |
| if ( !defined($dbconfig) | |
| or $dbconfig eq "" | |
| or ( !-e $dbconfig or !-r $dbconfig ) ) | |
| { | |
| print "Couldn't open $dbconfig for input\n"; | |
| exit; | |
| } | |
| if ( $options{'inter'} ) { | |
| $interface = "-e " . $options{'inter'} . " "; | |
| } | |
| if ( $options{'nmap'} ) { | |
| if ( -X $options{'nmap'} ) { | |
| $nmapPath = $options{'nmap'}; | |
| } | |
| else { | |
| print $options{'nmap'} . " isn't executable using $nmapPath\n"; | |
| } | |
| } | |
| if ( $options{'iplist'} ) { | |
| if ( -e $options{'iplist'} ) { | |
| if ( -r $options{'iplist'} ) { | |
| my $fh = new FileHandle("<$options{'iplist'}"); | |
| die "$options{'iplist'}:$!" unless defined $fh; | |
| @ipRange = $fh->getlines(); | |
| chomp @ipRange; | |
| $type = 'scan'; | |
| } | |
| else { | |
| print "File $options{'iplist'} isn't readable\n"; | |
| exit 1; | |
| } | |
| } | |
| else { | |
| print "File $options{'iplist'} doesn't exist\n"; | |
| exit 1; | |
| } | |
| } | |
| if ( $options{'xml'} ) { | |
| if ( -e $options{'xml'} ) { | |
| if ( -r $options{'xml'} ) { | |
| $xmlFile = $options{'xml'}; | |
| $type = 'file'; | |
| } | |
| else { | |
| print "Input File isn't readable\n"; | |
| exit 1; | |
| } | |
| } | |
| else { | |
| print "Input File doesn't exist\n"; | |
| exit 1; | |
| } | |
| } | |
| if ( $options{'udp'} ) { | |
| $scantype = $scantype . "U"; | |
| } | |
| if ( $options{'rpc'} ) { | |
| $scantype = $scantype . "R"; | |
| } | |
| if ( $options{'args'} ) { | |
| $args = ""; | |
| $scantype = ""; | |
| $args = $options{'args'}; | |
| $cargs = 'yes'; | |
| } | |
| if ( $options{'extraargs'} ) { | |
| $args = $args . $options{'extraargs'} . " "; | |
| } | |
| if ( $options{'dir'} ) { | |
| $outputdir = $options{'dir'}; | |
| } | |
| if ( $options{'range'} ) { | |
| my (@ports) = split ',', $options{'range'}; | |
| foreach (@ports) { | |
| chomp; | |
| if ( $_ !~ /\d/ ) { | |
| print "$_ not digit\n"; | |
| } | |
| } | |
| $Range = ""; | |
| for ( my $i = 0; $i < scalar(@ports); $i++ ) { | |
| $Range .= $ports[$i]; | |
| if ( $i < scalar(@ports) - 1 ) { | |
| $Range .= ","; | |
| } | |
| } | |
| } | |
| #@ports = @ports - 1; | |
| if ( $options{'moreports'} ) { | |
| my @morePorts = split ',', $options{'moreports'}; | |
| foreach (@morePorts) { | |
| if (/(\d+)/) { | |
| chomp; | |
| if ( $_ =~ /\d/ ) { | |
| $Range .= ",$_"; | |
| } | |
| else { | |
| print "MorePorts: Port $_ isn't between 1 and 65535\n"; | |
| exit 1; | |
| } | |
| } | |
| else { | |
| print "MorePorts: Port $_ isn't a number\n"; | |
| exit 1; | |
| } | |
| } | |
| } | |
| if ( $options{'diffbanner'} ) { | |
| $diff = "banner"; | |
| } | |
| my $tmp = scalar(@ARGV); | |
| my @targets; | |
| if ( $type ne 'file' ) { | |
| foreach (@ARGV) { | |
| s/\ //g; | |
| my @args = split(',', $_); | |
| foreach(@args){ | |
| push( @targets, $_ ); | |
| } | |
| } | |
| my @args = split(',', $dephosts); | |
| foreach(@args){ | |
| push( @targets, $_ ); | |
| } | |
| } | |
| foreach my $host (@targets) { | |
| if ($host) { | |
| my $ipRange; | |
| if ( $host =~ /[a-zA-Z]/ ) { | |
| my $name = $host; | |
| if ( $name eq 'localhost' ) { | |
| $ipRange = '127.0.0.1'; | |
| } | |
| else { | |
| my ( $host, @addresses, $hent, $addr_ref ); | |
| if ( $hent = gethostbyname($name) ) { | |
| $name = $hent->name(); # in case different | |
| $addr_ref = $hent->addr_list(); | |
| @addresses = map { inet_ntoa($_) } @$addr_ref; | |
| } | |
| $ipRange = $addresses[0]; | |
| if ( !defined($ipRange) or $ipRange eq '' ) { | |
| print "Invalid Ip Address being Resolved\n"; | |
| exit 1; | |
| } | |
| } | |
| print "Resolved $host to $ipRange\n" | |
| if ( $options{debug} > 3 ); | |
| $type = 'scan'; | |
| push( @ipRange, $ipRange ); | |
| } | |
| elsif ( $host | |
| =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\-(\d{1,3}|\*|'*')/ | |
| ) | |
| { | |
| my $max; | |
| if ( $5 =~ m/\*/ ) { | |
| $max = 255; | |
| } | |
| elsif ( $5 < 256 ) { | |
| $max = $5; | |
| } | |
| else { | |
| print "IP Address not in proper range\n"; | |
| exit 1; | |
| } | |
| if ( $4 < $5 ) { | |
| for ( $4 .. $max ) { | |
| if (verify_ip( | |
| { oct1 => $1, | |
| oct2 => $2, | |
| oct3 => $3, | |
| oct4 => $_ | |
| } | |
| ) == 1 | |
| ) | |
| { | |
| my $ip = join( ".", $1, $2, $3, $_ ); | |
| push( @ipRange, $ip ); | |
| $type = 'scan'; | |
| } | |
| } | |
| } | |
| } | |
| elsif ( $host | |
| =~ /(\d{1,3})\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(0\/\d{1,2}|\d{1,3}|\*|'*')/ | |
| ) | |
| { | |
| if ( $options{test} > 3 ) { | |
| print "first is $1\n"; | |
| print "second is $2\n"; | |
| print "third is $3\n"; | |
| print "fourth is $4\n"; | |
| } | |
| my $valid = verify_ip( | |
| { oct1 => $1, | |
| oct2 => $2, | |
| oct3 => $3, | |
| oct4 => $4 | |
| } | |
| ); | |
| if ( $valid == 1 ) { | |
| $type = 'scan'; | |
| push( @ipRange, $host ); | |
| } | |
| } | |
| if ( !defined($host) ) { | |
| print "IP Address not in proper range\n"; | |
| exit 1; | |
| } | |
| } | |
| else { | |
| print "Invalid IP Address\n"; | |
| exit 1; | |
| print "scan is $host" if ( $options{debug} eq 1 ); | |
| } | |
| } | |
| if ( $options{'pingscan'} ) { | |
| if ( $^O eq 'MSWin32' || $^O =~ /cygwin/ ) { | |
| $nmapPath = "\"$nmapPath\""; | |
| verify_windows( \@ipRange ); | |
| } | |
| else { | |
| verify_scan(); | |
| } | |
| $np->parsescan( $nmapPath, "-R -sP", @ipRange ); | |
| my $livehosts = ""; | |
| for my $host ( $np->get_ips('up') ) { | |
| if ( $livehosts eq "" ) { | |
| $livehosts = $host; | |
| } | |
| else { | |
| $livehosts = join( " ", $livehosts, $host ); | |
| } | |
| } | |
| if ( $livehosts eq "" ) { | |
| print "Scanned @ipRange\n"; | |
| print "No Hosts Responding to Ping Scan\n"; | |
| exit; | |
| } | |
| else { | |
| print "Live Hosts Found: $livehosts\n"; | |
| @ipRange = $livehosts; | |
| } | |
| } | |
| # make sure something is passed | |
| help() if ( $type ne 'file' and $type ne 'scan' ); | |
| if ( $type eq 'file' ) { | |
| $np->parsefile($xmlFile); | |
| } | |
| elsif ( $type eq 'scan' ) { | |
| $args = $args . $interface; | |
| if ( $cargs eq 'no' ) { | |
| $args = $args . $scantype . " -p $Range"; | |
| } | |
| if ( $options{debug} > 2 or $options{test} > 0 ) { | |
| print "nmap path is $nmapPath\n"; | |
| } | |
| if ( $args =~ /-o(?:X|N|G)/ ) { | |
| warn "$PROG Cannot pass option '-oX', '-oN' or '-oG'\n"; | |
| warn "Removing option\n"; | |
| $args =~ s/-o(?:X|N|G)//g; | |
| print "args is now $args\n"; | |
| } | |
| if ( $^O eq 'MSWin32' || $^O =~ /cygwin/ ) { | |
| $nmapPath = "\"$nmapPath\""; | |
| verify_windows( \@ipRange ); | |
| } | |
| else { | |
| verify_scan(); | |
| if ( $> ne 0 ) { | |
| print "PBNJ Scans requires root privileges.\n"; | |
| exit 1; | |
| } | |
| } | |
| if ( $options{debug} > 0 or $options{test} > 0 ) { | |
| print "Scan Args are $args\n"; | |
| print "Scanning "; | |
| foreach (@ipRange) { print "- $_ "; } | |
| print "\n"; | |
| } | |
| $np->parsescan( $nmapPath, $args, @ipRange ); | |
| } | |
| else { | |
| print "type error $type\n"; | |
| exit 1; | |
| } | |
| # only connect to the database when we need too. | |
| if ( $type eq "scan" or $type eq "file" ) { | |
| if ( $options{data} ) { | |
| $db = "SQLite"; | |
| $database = $options{data}; | |
| } | |
| else { | |
| $datadb = YAML::LoadFile($dbconfig); | |
| foreach my $tmp ( keys %$datadb ) { | |
| $passwd = $$datadb{$tmp} if ( $tmp eq 'passwd' ); | |
| $user = $$datadb{$tmp} if ( $tmp eq 'user' ); | |
| $db = $$datadb{$tmp} if ( $tmp eq 'db' ); | |
| $database = $$datadb{$tmp} if ( $tmp eq 'database' ); | |
| $hostname = $$datadb{$tmp} if ( $tmp eq 'host' ); | |
| $port = $$datadb{$tmp} if ( $tmp eq 'port' ); | |
| } | |
| } | |
| # connection to database | |
| if ( $db eq 'SQLite' or $db eq 'CSV' ) { | |
| $dbh = DBI->connect( | |
| "dbi:$db:$database", | |
| $user, $passwd, | |
| { PrintError => 0, | |
| RaiseError => 0, | |
| AutoCommit => 1 | |
| } | |
| ) | |
| || die "Cannot connect: $DBI::errstr"; | |
| } | |
| elsif ( $db eq 'mysql' ) { | |
| my $dsn = "DBI:$db:database=$database;host=$hostname;port=$port"; | |
| $dbh = DBI->connect( | |
| $dsn, $user, $passwd, | |
| { PrintError => 0, | |
| RaiseError => 0, | |
| AutoCommit => 1 | |
| } | |
| ) | |
| || die "Cannot connect: $DBI::errstr"; | |
| } | |
| elsif ( $db eq 'Pg' ) { | |
| my $dsn = "DBI:$db:database=$database;host=$hostname;port=$port"; | |
| $dbh = DBI->connect( | |
| $dsn, $user, $passwd, | |
| { PrintError => 0, | |
| RaiseError => 0, | |
| AutoCommit => 1, | |
| PrintWarn => 0 | |
| } | |
| ) | |
| || die "Cannot connect: $DBI::errstr"; | |
| } | |
| else { | |
| print "$db isn't supported\n"; | |
| } | |
| } | |
| storeData($dbh); | |
| $dbh->disconnect; | |
| __DATA__ | |
| # Config.yaml | |
| # | |
| # Copyright (C) 2005-2006 Joshua D. Abraham ( jabra@ccs.neu.edu ) | |
| # | |
| # This config file is released under the terms of the GNU General | |
| # Public License (GPL), which is distributed with this software in the | |
| # file "COPYING". The GPL specifies the terms under which users | |
| # may copy and use this software. | |
| # | |
| # PBNJ 2.0 | |
| # (P)orts (B)anners N' (J)unk | |
| # | |
| # Author: Joshua D. Abraham | |
| # Date: March 15, 2006 | |
| # Updated: November 15, 2006 | |
| # Version: 2.04 | |
| # | |
| # Configuration file for PBNJ 2.0 | |
| # YAML:1.0 | |
| # | |
| # Config for connecting to a DBI database | |
| # SQLite, mysql etc | |
| db: SQLite | |
| # for SQLite the name of the file. For mysql the name of the database | |
| database: data.dbl | |
| # Username for the database. For SQLite no username is needed. | |
| user: "" | |
| # Password for the database. For SQLite no password is needed. | |
| passwd: "" | |
| # Password for the database. For SQLite no host is needed. | |
| host: "" | |
| # Port for the database. For SQLite no port is needed. | |
| port: "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment