All pastes #236637 Raw Edit

Jonhoo

public text v1 · immutable
#236637 ·published 2006-11-03 22:54 UTC
rendered paste body
#!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,'&lt;?') >= 0) && index($response->content,';') >= 0 && (index($response->content,'?>') >= 0 || index($response->content,'?&gt;') >= 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;