Код:
#!/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;




