#!/usr/bin/perl use IO::Socket; #htmap - nmap (insecure.org) styled port scanner to determine if open ports are web servers # last updated: 4/15/2001 $VER="1.01"; # Contacts: # http://www.cirt.net/ # webmaster@CIRT.net ################################################################### #(c) 2001 CIRT.net, All Rights Reserved #Permission to use, copy, modify, and distribute this software and its documentation for educational, #research and non-profit purposes, without fee, and without a written agreement is hereby granted, #provided that the above copyright notice, this paragraph and the following two paragraphs appear #in all copies. This software program and documentation are copyrighted by CIRT.net # # THE SOFTWARE PROGRAM AND DOCUMENTATION ARE SUPPLIED "AS IS," # WITHOUT ANY ACCOMPANYING SERVICES FROM CIRT.NET. # FURTHERMORE, CIRT.NET DOES NOT # WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE # UNINTERRUPTED OR ERROR-FREE. THE END-USER UNDERSTANDS THAT # THE PROGRAM WAS DEVELOPED FOR RESEARCH PURPOSES AND IS # ADVISED NOT TO RELY EXCLUSIVELY ON THE PROGRAM FOR ANY # REASON. # # IN NO EVENT SHALL CIRT.NET BE LIABLE TO ANY # PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL # DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS # SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE CIRT.NET # HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. # CIRT.NET SPECIFICALLY DISCLAIMS ANY # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, # AND CIRT.NET HAS NO OBLIGATIONS TO PROVIDE # MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. ################################################################### print "\nStarting htmap V $VER by CIRT.net\n"; $start=time; $VERBOSE=0; for ($i=0;$i<=$#ARGV;$i++) { if (@ARGV[$i]=~/\-p/) { @ARGV[$i]=~s/\-p//; $PORTS=@ARGV[$i]; } elsif (@ARGV[$i]=~/\-h/) { @ARGV[$i]=~s/\-h//; $HOST=@ARGV[$i]; } elsif (@ARGV[$i]=~/\-v/) { $VERBOSE=1; } } # split ports @PORT=split(/\-/,$PORTS); if (@PORT[1] eq "") # check 1 port only { @PORT[1]=@PORT[0]; } # port validation if ((@PORT[0] eq "")||(@PORT[0] < 1)||(@PORT[0] > 65535)||(@PORT[0] =~ /[^0-9]/)) { &usage; } if ((@PORT[1] eq "")||(@PORT[1] < 1)||(@PORT[1] > 65535)||(@PORT[1] =~ /[^0-9]/)) { &usage; } if (@PORT[0] > @PORT[1]) { (@PORT[0], @PORT[1])=(@PORT[1], @PORT[0]); } # hostname/ip validation -- could be better... $HOSTNAME="unknown"; if ($HOST =~ /[^a-z|A-Z]/) # IP Address { if (ver_ip($HOST)) { print "Invalid IP address.\n"; &usage; } $HOSTNAME=gethostbyname($HOST); $HOSTNAME=~s/\s+//; } else # hostname { $HOSTNAME=$HOST; $HOST=gethostbyaddr($HOSTNAME,AF_INET); if (ver_ip($HOST)) { print "Name lookup failed for '$HOSTNAME'.\n"; &usage; } } if ($HOSTNAME eq "") { $HOSTNAME="unknown"; } @OPEN=(); $FAILURES=0; $SUCCESSES=0; $GETSTRING="GET / HTTP/1.0 User-Agent: Mozilla/4.5 [en] (Win95; U) "; # main loop for ($i=@PORT[0];$i<=@PORT[1];$i++) { try($i); } if ($#OPEN >= 0) { print "Interesting ports on $HOSTNAME ($HOST):\n"; print "(The $FAILURES ports scanned but not shown below are in state: closed/non-http)\n"; print "Port \tState \tWS Version\n"; foreach $O (@OPEN) { $SP=""; @T=split(/--=--/,$O); $SPL=10-length(@T[0]); for ($i=0;$i<=$SPL;$i++) { $SP="$SP "; } print "@T[0]\/tcp$SP\topen \t@T[1]\n"; } print "\n"; } else { print "All $FAILURES scanned ports on $HOST are: closed/non-http\n"; } $elapsed=time-$start; if (($elapsed eq 0)||($elapsed eq 1)) { $elapsed="1 second"; } else { $elapsed="$elapsed seconds"; } print "htmap run completed -- $HOST scanned in $elapsed\n"; exit; ########################################################## sub try { # build TCP socket connection attempt if ($VERBOSE) { print "Opening port: $i\n"; } my $FAIL=0; my $FOUND=0; my $VER=""; my $PORT=@_[0]; if ($PORT eq "") { return; } my $socket= IO::Socket::INET->new(PeerAddr => $HOST, PeerPort => $PORT, Proto => "tcp", Timeout => 5, Type => SOCK_STREAM) or $FAIL=1; if ($FAIL eq 0) { eval { local $SIG{ALRM} = sub { die "timeout alarm"}; alarm 5; eval { print $socket $GETSTRING; while (<$socket>) { s/\s*$//; if ($_ =~ /User-Agent/) { next; } # echo or ftp port, look for part of GETSTRING if ($_ =~ /HTTP/i) { $FOUND=1; $SUCCESSES++; } if ($_ =~ /^Server/) { $FOUND=1; chomp($_); $_=~s/^Server: //; $VER=$_; } } if ($FOUND eq 1 ) { push(@OPEN,"$PORT--=--$VER"); if ($VERBOSE) { print "\tHTTP Connect\n"; } } }; # end inner eval alarm 0; }; # end eval alarm 0; die $FAILURES++ if $@ && $@ !~ /timeout alarm/; } if ($FAIL eq 1) { $FAILURES++; } close($socket); } ########################################################## sub usage { print "usage: htmap -h -p (-v)\n"; print " -hhostname/ip (no space after -h)\n"; print " -pport port/range (no space after -p)\n"; print " -v verbose mode\n"; print "example: htmap -h10.10.10.10 -p1-100 -v\n"; print "htmap V $VER by CIRT.net\n"; exit; } ######################################################### sub ver_ip { my $D=@_[0]; if ($D eq "") { return 1; } if (length($D) < 4) { return 1; } if (length($D) > 15) { return 1; } if ($D =~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) { my @TEMP=split(/\./,$D); for ($i=0; $i <= $#TEMP; $i++) { if ($i > 3) { $FAIL=1; } if ((@TEMP[$i] > 255) || (@TEMP[$i] < 0)) { return 1; } } return 0; } else { return 1; } } #########################################################