#!C:\Perl\bin
#Including various needed stuff
use LWP::UserAgent;
use strict;
use Net::Ping;
use LWP::ConnCache;
#Function for printing help
sub usage()
{
print "\nWelcome to Hostchecker v0.7!\n";
print "These are the valid options:\n";
print "\t-h <host>\n";
print "\t-p <port>\n";
print "\t-x <extensions file>\n";
print "\t-d <directory file>\n";
print "\t-h <filelist file>\n";
print "\t-cookie <name>=<value>\n";
print "\t-browser <browsername>\n";
print "\t-n - Turn of extension blacklisting\n";
print "\t-l <logtofile>\n";
print "\t-m <logile> - Minimize output (Implies logging)\n";
exit;
}
# Variables needed for the showper function...
my $divider;
my $tottries;
my $sum;
# Fuction to show percent finished
sub showper($$)
{
my $downer = shift;
my $tottries = shift;
glob $divider;
glob $sum;
# The following bit ensures that we get an equal length of all the progress meters
my $div1 = int($downer/$divider);
my $div2 = int(($tottries-$downer)/$divider);
my $sumcurr = int($downer/$divider) + int(($tottries-$downer)/$divider);
if ($sumcurr != $sum)
{$div2++;}
for (my $iii = 0; $iii < $div1; $iii++)
{
print STDOUT '#';
}
for (my $aaa = 0; $aaa < $div2; $aaa++)
{
print STDOUT '.';
}
print STDOUT " [".int(($downer/$tottries)*100)."%]\n";
}
#Prints help if program is run without parameters
if(!@ARGV)
{
usage();
}
#Constructs the "Browser" to use
my $connector = LWP::UserAgent->new;
$connector->conn_cache(LWP::ConnCache->new()); #Enables cache, faster loading of multiple pages
#Various initializers
my $args = @ARGV;
my $customextfile = 0;
my $customdirfile = 0;
my $customfilefile = 0;
my $cookie = 0;
my $browser = 'Hostchecker v0.7b';
my $host = 0;
my $url = '';
my $dir = '';
my $ext = '';
my $file = '';
my $response;
my $blacklisting = 1;
my $count = 0;
my $found = 0;
my $thisfake = 0;
my $prev200 = '';
my $next;
my $logging = 0;
my $logtofile = '';
my $countsources = 0;
my $current = '';
my $min = 0;
my $sumthisfar = 0;
#Initializing the arrays holding the combinations of folders, files and extensions
my @cookies;
my @commondirs = ('.', 'inc', 'admin', 'ext', 'bckup', 'bck', 'includes', 'scripts', 'tmp', 'temp', 'bup', 'php');
my @commonexts = ('.inc', '.txt', '.phps', '.phpsource', '.bup', '.bck', '.bckup', '.php.inc', '.php.bck', '.tmp', '.source', '.sql');
my @commonfiles = ('index', 'login', 'logout', 'functions', 'users', 'shell', 'includes', 'general_functions', 'script', 'admin', 'administration', 'include', 'sql', 'install', 'update', 'upload');
#Sets variables based on user input
for (my $i = 0; $i < $args; $i++)
{
$next = $i +1;
if ($ARGV[$i] eq '-h') # -h means next argument is hostname
{
if ($ARGV[$next] =~ /[\w\d\.]+\.{1}[\w\d\.]+\.{1}[\w\d\.]*\.*/) # IF it is actually formed as a hostname
{
$host = $ARGV[$next];
}
else
{
print "Invalid HOST format!\n";
print $ARGV[$next]."\n";
exit;
}
}
if ($ARGV[$i] eq '-x') # -x means a separate file with file extensions
{
$customextfile = $ARGV[$next];
}
if ($ARGV[$i] eq '-d') # -x means a separate file with folders
{
$customdirfile = $ARGV[$next];
}
if ($ARGV[$i] eq '-f') # -x means a separate file with filenames
{
$customfilefile = $ARGV[$next];
}
if ($ARGV[$i] eq '-cookie') # -cookie is used if a session cookie is wanted
{
$cookie = $ARGV[$next];
@cookies = split/=/, $cookie;
}
if ($ARGV[$i] eq '-browser') # -browser sets the browser name
{
$browser = $ARGV[$next];
}
if ($ARGV[$i] eq '-n') # We should not blacklist extensions
{
$blacklisting = 0;
}
if ($ARGV[$i] eq '-l') # Output should be logged instead
{
$logging = 1;
$logtofile = $ARGV[$next];
}
if ($ARGV[$i] eq '-m') # Minimized output for readability
{
$min = 1;
$logging = 1; # Log all info that is removed from STDOUT
$logtofile = $ARGV[$next];
}
}
if (!$host) # Exit if host not set
{
print "No host specified...\n";
exit;
}
# Load various custom files
if ($customextfile != 0)
{
open (GETEXT, $customextfile);
my @cusxtemp = <GETEXT>;
close GETEXT;
if (grep(',', @cusxtemp))
{
my $cusxser = join//, @cusxtemp;
@cusxtemp = split/,\s*/, $cusxser;
}
@commonexts = @cusxtemp;
}
if ($customdirfile != 0)
{
open (GETDIR, $customdirfile);
my @cusdtemp = <GETDIR>;
close GETDIR;
if (grep(',', @cusdtemp))
{
my $cusdser = join//, @cusdtemp;
@cusdtemp = split/,\s*/, $cusdser;
}
@commondirs = @cusdtemp;
}
if ($customfilefile != 0)
{
open (GETFILE, $customfilefile);
my @cusftemp = <GETFILE>;
close GETFILE;
if (grep(',', @cusftemp))
{
my $cusfser = join//, @cusftemp;
@cusftemp = split/,\s*/, $cusfser;
}
@commonfiles = @cusftemp;
}
# Total number of combinations the script has to run through
# Needed to calculate percentages
my $totaltries = @commonfiles*@commondirs*@commonexts;
for (my $i = 40; $i < 70; $i++)
{
if ($totaltries % $i == 0)
{$divider = $i;}
}
$sum = int($totaltries/$divider);
#Sets default output to logfile if wanted
if ($logging)
{
print "Logging started to $logtofile\n";
open(LOGOUT, ">$logtofile");
select (LOGOUT);
}
#Pings server, exits on no reply
my $p = Net::Ping->new("tcp", 2);
$p->{port_num} = getservbyname("http", "tcp");
print "$host is alive and kicking!\n" if $p->ping($host);
$p->close();
exit unless $p->ping($host); # Exit unless ping succeeds
#Tries to get information about the server by sending a general GET request and checking the returned header
my $req = HTTP::Request->new(GET => "http://".$host);
my $res = $connector->request($req);
my $resser = $res->header('Server');
$resser =~ s/\s{1}/\n\t/gis;
print "\nServer info:\t".$resser."\n\n";
#Sets header stuff
my %header = {
'User-Agent' => $browser,
'Accept' => 'text/*' # We will only accept textfiles
};
if ($cookie != 0) # If user wants cookie set, opens the cookie jar
{
use HTTP::Cookies;
my $cookie_jar = HTTP::Cookies->new;
$cookie_jar->set_cookie(
0, # version
$cookies[0], # key
$cookies[1], # value
'/', # path
$host, # domain
undef, # port
1, # path_spec
undef, # secure
undef, # maxage
0, # discard
);
$connector->cookie_jar($cookie_jar);
}
print STDOUT "Successfully connected to server\n" if $logging; # Print some stuff to user in case he has turned on logging
showper(0, $totaltries) if $min; # Show 0%
# Start looping through directories
for (my $i = 0; $i<@commondirs;$i++)
{
$dir = $commondirs[$i];
#Output stuff to user
print "Entering $dir\n";
print STDOUT "Trying to find vulnerabilities in $dir\n" if ($logging && !$min);
#Loop through filenames
for (my $j = 0; $j<@commonfiles; $j++)
{
$file = $commonfiles[$j];
# More stuff for greedy users
print "\tUsing $file\n";
print STDOUT "\tTrying to use filename $file\n" if ($logging && !$min);
#Finally, loop through all extensions for $file
for (my $k = 0; $k<@commonexts; $k++)
{
$ext = $commonexts[$k];
$url = "http://$host/$dir/$file$ext"; # Construct URL
# Let user know what's going on
print "\t\tTrying $url\n";
print STDOUT "\t\tTrying $url\n" if ($logging && !$min);
$response = $connector->get($url, %header); # Make the actual request
if ($response->status_line =~ /200/) # If server returns a file
{
# Tell the user
print "\t\t\t[*] Got 200 OK (".$response->status_line.") reply\n";
# If the returned output contains PHP start and end tags, we have source
# The reason for including the HTML version of the tags is that some webservers automatically colors sourcecode when on its own...
if ((index($response->content,'<?') >= 0 || index($response->content,'<?') >= 0) && index($response->content,';') >= 0 && (index($response->content,'?>') >= 0 || index($response->content,'?>') >= 0))
{
# Tell user
print "--->\t\t[*] We've got source!\a\n";
# Even if she's logging
print STDOUT "\t\t\t------FOUND SOURCE CODE (in $dir/$file$ext)------\n" if $logging;
$countsources++; # Increment sourcecount
# Print URL to a txt file
open(VALID, ">>Valid.txt") or print "\t\t\t[*] Could not output to txt file!\n";
print VALID $url."\n";
close VALID;
}
else # We don't have source
{
# This is where the fancy blacklisting routine starts...
# Try to keep up!
$current = $response->content; # Retrieve current page response
$thisfake = 0;
if ($found == 1 && $prev200 eq $current) # A common 404 error page has been found ($found = 1), checks if returned page is equal to that
{
#If it is, we have a fake 200 OK, and we act as such
print "\t\t\t[*] Only the fake 200 OK again...\n";
$thisfake = 1;
}
else
{
# Alert user
print "\t\t\t[*] False alarm (Probably not fake 200 OK though...)\n";
}
# If the user has blacklisting turned on and the given page is not a fake, tell the user that you are blacklisting the extension (As it obviously doesn't return any code)
print "\t\t\t[*] Blacklisting extension $ext...\n" unless ($blacklisting == 0 || $thisfake == 1 || $count == 0);
#This actually removes the current extension from the list
splice(@commonexts,$k,1) unless ($blacklisting == 0 || $thisfake == 1 || $count == 0);
if ($found == 0) # If a Fake 200 OK page has not been confirmed
{
# Read the previous Fake 200 OK
open(READ200, "200Redir.txt") or $prev200 = '';
$prev200 = <READ200> if -e "200Redir.txt";
close READ200;
#Write the new Fake 200 OK
open(SAVE200, ">200Redir.txt");
print SAVE200 $current;
close SAVE200;
}
if ($prev200 eq $current && $found != 1) # If the current returned 200 OK page is equal to the last one, and no fake 200 OK page is confirmed
{
# Tell user and set the Fake 200 OK to found
print "\t\t\t[*] Found possible 200 OK redirect, blacklisting fake 200 OK page\n";
$found = 1;
}
}
}
elsif ($response->status_line =~ /404/) # If a 404 code is returned
{
print "\t\t\t[*] Not found...\n";
}
else # Some other error code
{
# Log the error and the url
open(ERRORPRINT, ">>".$response->status_line.".txt");
print ERRORPRINT $url."\n";
close ERRORPRINT;
#Print the error
print "\t\t\t[*] Got error reply ".$response->status_line." -- Logged\n";
if ($response->status_line =~ /401/) # If authorization required
{
$count++; # Increment count of how many of the extensions to the current folder and filename have required authorization
if ($count == int(@commonexts/3)) # If more than a third of the extensions have required authorization, the whole folder is probably password-protected
{
print "\t\t\t[*] Too many extensions in folder $dir have returned 401, blacklisting folder\n";
splice(@commondirs,$i,1); # Removing folder from folder list
#Reset for loops
$j = -1;
$k = -1;
if ($i == @commondirs)
{
goto afterloop; # If no more folders, end all loops
}
else
{
# Else, enter next folder and try the same filename again
$dir = $commondirs[$i];
print "Entering $dir\n";
$file = $commonfiles[$j];
print "\tUsing $file\n";
}
}
}
if ($response->status_line =~ /50/) # If server returns 50x header, server might be getting overloaded
{
print "\t\t\t[*] Server is getting tired... Sleeping for 10 seconds\n";
sleep(10);
}
}
}
# Next line shows the progress bar
showper(((@commonfiles*@commonexts)*$i)+(@commonexts*($j+1)), $totaltries) if $min;
$count = 0; # Reset extension-authorization count
sleep(1);
}
sleep(5);
}
afterloop:
if ($logging)
{
# Close logfile and reset print to STDOUT
close LOGOUT;
select (STDOUT);
}
print STDOUT "Finished checking host, found $countsources pieces of sourcecode, good luck ;-)\n" if $countsources > 0;
print STDOUT "Finished checking host, no sources found..." if $coundsources == 0;