Код:
#!/usr/bin/perl # # Runs a number of concurrent nmap processes and stores the results in # a specified directory in xml, human and machine formats. Optionally runs # amap using the nmap output as input. Writes the results to an HTML file # in the results directory. # Make sure you have the latest nmap and amap (www.thc.org) and that they play # nicely together. Tested with nmap 3.27 and amap 4.2. # # NOTE: Change the values below to match your environment and requirements! # # by # Stephen de Vries - stephen at twisteddelight dot org # www.twisteddelight.org/security # ############################################################################# use strict; my $NMAP_COMMAND="/usr/local/bin/nmap"; my $NMAP_OPTIONS="--max_rtt_timeout 2000"; # -oX -oN -oM are set automatically my $AMAP_COMMAND="/usr/local/bin/amap"; my $AMAP_OPTIONS="-b -1"; # -o -m are set automatically my $BAN_WIDTH=50; ############################################################################# my @hosts, my $wdir, my $hostFile, my $procs, my $run_amap; my @pids; # Store the currently running pids my %activehosts; # hosts currently being scanned, indexed according to pid my %banners; # store banner info from amap my @HTML; # temporary storage for the output use POSIX qw(:signal_h :errno_h :sys_wait_h); use Getopt::Std; $SIG{CHLD} = \&REAPER; sub REAPER { my $pid; $pid = waitpid(-1, &WNOHANG); if ($pid == -1) { # no child waiting. Ignore it. } elsif (WIFEXITED($?)) { my $found=0; my $i=0; # Check to see if the pid that exited belongs to one of the nmaps while ((!$found) && ($i <= $#pids)) { if ($pid == $pids[$i]) { splice(@pids,$i,1); $found=1; wlog(0,"Nmap completed on: $activehosts{$pid}"); if ($run_amap) { launch_amap($activehosts{$pid}); } delete $activehosts{$pid}; } $i++; } } else { print "False alarm on $pid.\n"; } $SIG{CHLD} = \&REAPER; # in case of unreliable signals } sub getDate { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); return "$year-$mon-$mday $hour:$min:$sec"; } sub wlog { my ($code, @msg) = @_; if ($code ==0) { # Print to screen and log file print "@msg\n"; my $date = getDate();; print LOG "$date|@msg\n"; } elsif ($code ==1) { # Just print to stdout print "@msg\n"; } elsif ($code ==2) { # Just print to the log file print "@msg\n"; } else { print "Unknown error code: $code\n"; print "@msg\n"; } } sub launch { my ($host) = @_; my $OUTPUT="-oX $wdir/xml/$host.nmap.xml -oN $wdir/human/$host.nmap.human -oM $wdir/machine/$host.nmap.machine"; my $RUNSTR="$NMAP_COMMAND $NMAP_OPTIONS $OUTPUT $host"; wlog(0, "Launching: $RUNSTR"); exec($RUNSTR); } sub launch_amap { my ($host) = @_; my $OUTPUT=""; my $RUNSTR="$AMAP_COMMAND $AMAP_OPTIONS -m -i $wdir/machine/$host.nmap.machine -o $wdir/amap/$host.amap"; wlog(0, "Launching: $RUNSTR"); if (!fork()) { exec($RUNSTR); } } sub confirmSettings { print "\nLaunching with the following options:\n"; print "\nReading hosts from:\t\t$hostFile"; print "\nConcurrent nmap processes:\t$procs"; print "\nResults directory:\t\t$wdir"; open (D, $wdir); # Just so we can use the -X operators if (-d D) {print " already exists, CLOBBERING existing data!";} close D; print "\nLaunching nmap:\t\t\t$NMAP_COMMAND $NMAP_OPTIONS"; if (defined($run_amap)) { print "\nLaunching amap:\t\t\t$AMAP_COMMAND $AMAP_OPTIONS"; } print "\n\nConfirm (y,n)?\n"; my $conf = getc; if ($conf ne "y") { print "Aborted.\n"; exit 1; } } sub init { $wdir =~ s/\/$//g; my $line; wlog (0,"Creating directories in $wdir"); if (mkdir($wdir,0750) == 1) { wlog(2,"Directory $wdir created"); } else { wlog(2,"Error creating directory $wdir: $!"); } if (mkdir("$wdir/xml") == 1) { wlog(2,"Created xml directory"); } if (mkdir("$wdir/machine") == 1) { wlog(2,"Created machine readable directory"); } if (mkdir("$wdir/human") == 1) { wlog(2,"Created human readable directory"); } if (mkdir("$wdir/amap") == 1) { wlog(2,"Created amap directory"); } open LOG, ">$wdir/multiscan.log" || die "Fatal Error: could not open log file $wdir/multiscan.log"; open F, $hostFile or die "Fatal error opening file: $hostFile\n"; while ($line=<F>) { chomp $line; wlog(0, "Adding host [$line] to scan list"); push @hosts, $line; } close F; } sub getbanners { my ($host) = @_; my $line, my @vals; open AMAPFILE, "$wdir/amap/$host.amap" || wlog(0, "Error: Can't open file $wdir/amap/$host.amap for reading"); while ($line = <AMAPFILE>) { if ($line =~ /^$host:([0-9]+)([a-z0-9A-Z\-_]*:){5}(.*)/) { my $port = $1; my $ban = $3; $ban =~ s/:$/No banner found/; $banners{$port} = $ban; print ">> $port = $ban\n"; } } close AMAPFILE; } sub convertToHTML { my $dirname = "$wdir/machine/"; my $line, my $file; my $ip, my $hostname, my $os, my $seq, my @ports, my $port, my $tcpseq, my $portslist; opendir(DIR, $dirname) or die "can't open dir $dirname: $!"; open OUTFILE, ">$wdir/machine/allresults.machine.nmap" || die "Can't open output file: $wdir/machine/allresults.machine.nmap"; while (defined($file = readdir(DIR))) { if (($file =~ /^\./) || ($file =~ /^allresults/)) {next;} open INFILE, "$wdir/machine/$file" || wlog(0,"Error: Can't open results file: $wdir/machine/$file\n"); my @lines = <INFILE>; close INFILE; print OUTFILE @lines; } close OUTFILE; closedir(DIR); #### Put a header on the html file push @HTML, "<HTML><HEAD></HEAD><BODY>\n<H3>Nmap results</H3>\n"; #### Process the file open FILE, "$wdir/machine/allresults.machine.nmap" || die "Error opening $wdir/machine/allresults.nmap"; my @lines = <FILE>; close FILE; foreach $line (@lines) { if ($line =~ /^Host:\s+([0-9\.]+)\s+\((\S*)\)\s+Ports:\s+(.*)\s+(?=Ignored)Ignored\ State\:(.*)/) { my ($ip, $hostname, $portslist,$rest) = ($1,$2,$3,$4); getbanners($ip); my ($os, $seq) = ("Unknown", "Unknown"); push @HTML,"\n<table width=\"80%\" border=1><tr>\n"; push @HTML," <td width=120><b>IP Address</b></td><td>$ip</td></tr>\n"; if (length($hostname > 1)) { push @HTML," <tr><td><b>Hostname</b></b></td><td>$hostname</td></tr>\n"; } if ($rest =~ /(.*)(?=OS)OS\:(.*)(?=IPID)IPID\s+Seq\:(.*)/) { ($rest,$os, $seq) = ($1,$2,$3); push @HTML," <tr><td><b>OS Guess</b></td><td>$os</td></tr>\n"; #push @HTML," <tr><td><b>TCP Sequence</b></td><td>$tcpseq</td></tr>\n"; push @HTML," <tr><td><b>IPID Sequence</b></td><td>$seq</td></tr>\n"; } push @HTML," <tr><td valign=top><b>Ports</b></td><td><table width=\"100%\" border=1>\n"; push @HTML," <tr><td><b>Num</b></td><td><b>Status</b></td><td><b>Proto</b></td><td><b>Name</b></td>"; if ($run_amap) { push @HTML, "<td><b>Banner</b></td>"; } push @HTML, "</tr>\n"; my @ports = split(/\,/, $portslist); foreach $port (@ports) { $port =~ s/^\s+//; my @list = split(/\//, $port); push @HTML," <tr><td valign=top>$list[0]</td>\n"; push @HTML," <td valign=top>$list[1]</td>\n"; push @HTML," <td valign=top>$list[2]</td>\n"; push @HTML," <td valign=top>$list[4]</td>\n"; if ($run_amap) { my $banner = $banners{$list[0]}; $banner =~ s/\</\</g; $banner =~ s/\>/\>/g; $banner =~ s/\\n/\\n<br>/g; push @HTML," <td valign=top>$banner</td>"; } push @HTML, "</tr>\n"; } push @HTML," </table></td></tr></table>\n<br><br>"; } } #### Put a footer on the HTML push @HTML, "</BODY></HTML>\n"; open OUT, ">$wdir/multimap.html"; print OUT @HTML; close OUT; } sub usage { print STDERR << "EOF"; Usage: $0 -f file -d dir -p processes [-n "options"] [-a] [-h] -f file : text file containing IP addresses to scan -d dir : directory to store output files -p processes : the number of concurrent nmap scans to run -n "options" : additional nmap options -a : run amap -h : this help Notes: Change the values of $NMAP_OPTIONS and $AMAP_OPTIONS in the code to save from typing your favourite options on the cmd line. EOF } ############################################################################### # Start main program my %options; getopts("f:d:p:n:ah", \%options); $hostFile = $options{"f"}; $wdir = $options{"d"}; $procs = $options{"p"}; my $temp = $options{"n"}; $run_amap = $options{"a"}; $temp =~ s/\"//g; $NMAP_OPTIONS = $NMAP_OPTIONS . " " . $temp; my $error=0; if (length($hostFile) == 0) { print "Error: Must specify a host file with -f file\n"; $error=1; } unless (open(F, $hostFile)) { print "Error: can't open file $hostFile for reading\n"; } close F; if (length($wdir) == 0) { print "Error: Must specify a working directory with -d dir\n"; $error=1; } if (length($procs) == 0){ print "Error: Must specify the number of processes to run with -p processes\n"; $error=1; } if ($error == 1) { usage(); exit(1); } confirmSettings(); init(); my $i=0, my $host, my $currentpid; while ($#hosts+1 > 0) { if ($i < $procs ) { $i++; $host = pop @hosts; $currentpid = fork(); push @pids, $currentpid; if ($currentpid == 0) { launch($host); exit(0); } else { $activehosts{$currentpid}=$host; } } my $total=0; for (my $n=0;$n <= $#pids;$n++) { if ($pids[$n] != 0) {$total++;} } $i = $total; sleep 1; } wait; convertToHTML; close LOG;