Nmap Announce mailing list archives

nwrap -- nmap stealth wrapper (updated)


From: HD Moore <nlog () ings com>
Date: Thu, 15 Apr 1999 20:46:31 -0500

Hi,

This is an update to the original nwrap prototype script I posted
earlier. Nwrap takes a list of addresses and port numbers to scan, adds
each host/port combination to a list and randomly shuffles it.  Then it
launches a specified amount of parallel nmap processes until it empties
the list.  Currently, the output from each nmap process is just added
into a filename hardcoded into the executable (/tmp/nwrap.log) and the
scan types/source ports/etc has to be changed by editing the script. 
The final version is expected to be released within a week or so, with
the output being in the nlog-style flat-file database format.  To test
this script try something like the following:

# host -l example.com | grep "has address" | awk '{print $4}' | sort -u
| uniq | sort -u | grep -v 127.0.0.1> host.list
# ./nwrap -i host.list -p 21-25,79-80,110-113,139

check out /tmp/nwrap.log for results...

Remember that this script is NOT even beta-quality yet, use a your own
risk and please let me know about any problems you have or features you
want. 


-HD 

http://nlog.ings.com
#!/usr/bin/perl
#
#
#
# Usage: # echo <ip address> | nwrap -i - -p <port list>
#
#



use Getopt::Long;
use POSIX ":sys_wait_h";

sub exitclean {
   my ($msg) = @_;
   print "$msg\n";
   exit 2;
}

sub debugprint {
  my ($msg) = @_;
  print STDERR "[debug]  $msg\n" unless (!$OPTdebug);
}

sub sig_catch {
   my $signame = shift;
   print "\nRecieved a SIG$signame, exiting...\n";
   exit 2;
}



###############################################################################
#
#   Function:   main
#   Purpose:    read in our host configuration file and start the scans
#   To-Do:      Done
#   Status:     Under Development
#    
#   Date: 04/15/99
#               
###############################################################################

# variables...

$nmap       = "/usr/local/bin/nmap";
$scantype   = "-sS";
$sport      = "20";
$syncscans  = "15";
$logfile    = "/tmp/nwrap.log";


# clear the screen...
$cls = `clear`;

# unbuffer STDOUT & STDERR
select(STDERR);
$| = 1;
select(STDOUT);
$| = 1;

# install signal handler for each signal we want to trap
@SIGNALS = ("INT", "HUP", "KILL", "TERM", "QUIT");
foreach $SIGNAL (@SIGNALS)
{ 
    $SIG{$SIGNAL}=\&sig_catch; 
}


# read our command line options
&GetOptions("debug", \$OPTdebug,
            "p:s", \$OPTports,
            "i:s", \$OPTinput);

# open our input files
open (INPUT,"<".$OPTinput) || exitclean("Could not open host input file:  $!");
@targets = (<INPUT>);
close(INPUT) || debugprint("close() failed on INPUT: $!");


# create a host/port list and shuffle it

@targets = shuffle(\@targets);
@ports = parse_ports($OPTports);
@ports = shuffle(\@ports);

foreach $host (@targets)
{
    chomp($host);
    @ports = shuffle(\@ports);
    foreach $port (@ports)
    {
        push @output, "$host $port";
    }
}
@output = shuffle(\@output);

debugprint("My PID = $$");
# now do something with that host/port list
$counter = 0;
$lastindex = $#output;

$startfork = 0;
$endfork = $syncscans;
$forkcount = 0;

$stop = 0;

%pids = ();
%read_buf = ();

#initialize bitmask
$rin = '';

$timeout = 60;
$start = time();

open (LOG, ">>" . $logfile) || die "Could not open log file:  $!";

while ($stop == 0)
{   

    debugprint("Starting spawning loop:  $startfork -> $endfork");
    for ($forkcount = $startfork; $forkcount <= $endfork; $forkcount++)
    {
        if ($forkcount >= ($lastindex + 1))
        {
            $stop = 1;
            
        } else {

            ($nmaptarget,$nmapport) = split(/\s+/,$output[$forkcount]);
            
            $FD = "NMAP" . $forkcount;
            die "Could not fork nmap:  $!" unless defined ($pid = open($FD, "$nmap $scantype  -g $sport -m - -P0 -p 
$nmapport $nmaptarget|"));
            $pids{$pid} = $FD;
            select($FD) || debugprint("Could not select $FD:  $!");
            $| = 1; # unbuffer
            debugprint("New FD = $FD PID = $pid");
            vec($rin,fileno($FD),1) = 1;            
            
            if (!$OPTdebug)
            {
                select STDOUT;
                print $cls;
                ($ctime,$junk) = split(/\./, (int(time - $start) / 60));
                print "Scanning Host $forkcount of " . ($lastindex + 1) . "\n";
                print "Host:\t$nmaptarget\n";
                print "Port:\t$nmapport\n";
                print "Time:\t$ctime minutes.\n";
            }      
        }  
    }

    if (!$OPTdebug)
    {
        $pcount = scalar(keys(%pids));
        print "\n\nLaunched $pcount nmap processes, waiting for them to exit:  ";
    
    }
    
    $startfork  = $startfork + $syncscans;
    $endfork    = $endfork + $syncscans;
    $forkcount  = $startfork;


    debugprint("Completed spawning loop...");
    $n = select($rout = $rin,undef,undef,$timeout);

########################################
# S    
    while ($n != -1)
    {

        $n = select($rout = $rin,undef,undef,$timeout);
        @deleteQ = ();
        foreach $npid (keys(%pids))
        { 
            $n = select($rout = $rin,undef,undef,$timeout);
            
            # hold the pid's of the items to delete from our hash.
            
            debugprint("select = $n");
            $FileNo = fileno($pids{$npid});
            debugprint("FileNo:  $FileNo");
            debugprint("Doing if vec()");

            if ($FileNo >=0  && vec($rout, $FileNo,1) == 1)
            {
                # we have new data
                debugprint("Trying to read data from $pids{$npid}");

                $FH = $pids{$npid};
                my $data = <$FH>;
               
         
                if (length($data) > 0)
                {
                    print LOG $data;
                }
            }

            debugprint("Testing PID $npid for repsonse...");
            if (!kill $npid => 0)
            {
                debugprint("Process $npid has exited with handle: $pids{$npid}");
                close($pids{$npid}); # seems to work better if we close this...
                push @deleteQ, $npid;

                if (!$OPTdebug)
                {
                    print "x ";

                }
            }

        }
        
         
            foreach $deadpid (@deleteQ)
            {
                debugprint("Removing $deadpid from pid hash");
                delete $pids{$deadpid};
            }

      
        }


   debugprint("Finished select()");
   
    if (!$OPTdebug)
    {
        print "  Done!\n";
        sleep 1;

    }

# E
########################################

  
}

close(LOG);
exit(0);


#
# Functions
#



###############################################################################
#
#   Function:   getpppip
#   Purpose:    crude function to get our current ppp device's ip address
#   To-Do:      Done
#   Date: 04/09/99
#               
###############################################################################

sub getpppip {
    my $DATA=`ifconfig | grep P-t-P | awk \'\{ print \$2 \}\'`;
    my $crap;
    my $ip;
    chomp($DATA);
    ($crap,$ip) = split(/\:/,$DATA);
    return $ip;
}


###############################################################################
#
#   Function:   rdecoys
#   Purpose:    generate 6 random ip address in the same subnet as the input address
#   To-Do:      Done
#   Date: 04/09/99
#               
###############################################################################


sub rdecoys {
    my ($ip) = @_;
    my @octets = split(/\./,$ip);
    my $count;
    my @decoys = ();
    my $decoy;
    my $output;
    
    for ($count = 0; $count < 6 ; $count++)
    { $decoys[$count] = int(rand()*255); }

    foreach $decoy (@decoys)
    { 
        $output .= "$octets[0].$octets[1].$octets[2].$decoy,";
    }
    $output .="ME";
    return $output;
}



###############################################################################
#
#   Function:   shuffle
#   Purpose:    Randomize an array
#   To-Do:      Done
#   Date: 04/09/99
#
#   Comments:   This routine was pretty much ripped from 'Perl Cookbook' pg 121-122
#               
###############################################################################


sub shuffle {
    my $array = shift;
    my $i = scalar(@$array);
    my $j;
    foreach $item (@$array )
    {
        --$i;
        $j = int rand ($i+1);
        next if $i == $j;
        @$array [$i,$j] = @$array[$j,$i];
    }
    return @$array;
}



###############################################################################
#
#   Function:   parse_ports
#   Purpose:    Take in an nmap style port list and return an array
#   To-Do:      Add a check to make sure all the ports added are numeric
#   Date: 04/09/99
#
###############################################################################

sub parse_ports {
    my ($portstring) = @_;
    my $splitter = ",";
    my @portlist = ();
    my @portsplit = ();
    my $port;
   
    @portsplit = split($splitter,$portstring);
    foreach $port (@portsplit)
   
    {
        @range = split(/\-/,$port);
        if (scalar(@range) > 1)
        {
            if ($range[0] > $range[1] || $range[0] < 0 || $range[0] > 65535 || $range[1] < 0 || $range[1] > 65535)
            {
                print "Your range of $range[0] -> $range[1] is invalid!\n";
                exit(1);
            }
            for ($i = $range[0]; $i < $range[1] + 1; $i++)
            {
                if ($i > 0 && $i < 65536)
                {
                    push @portlist, $i;   
                }
            }
        
        } else {
            push @portlist, $port;
        }
    }
    
    return @portlist;
}


# EOF

Current thread: