Jean Pacal Perei
#!/usr/bin/perl
#
# THC Web Vulnerability Scanner v3.8
#
# $Id: thc.pl, v3.8 2010/20/06 16:15:56 jpp Exp $
#
#
# Description: THC is an effective open source web vulnerability
# scanner for detecting security flaws and bugs in a
# remote network. It also crawls for e-mail addresses
# and sensitive files / directories.
#
# Author: Jean Pascal Pereira <pereira@secbiz.de>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA
use strict;
use threads;
use warnings;
use LWP::UserAgent;
use Getopt::Std;
use IO::Socket;
my %params=();
getopts("hpgqsrfmok:u:a:c:e:",%params);
if (defined $params{h})
{
print "n [ THC WEB VULNERABILITY SCANNER ]nn";
print " -u -- defines the target url (required)n";
print " -c -- defines the cookie for our http requestsn";
print " -a -- defines the user agent for our http requestn";
print " -p -- defines a http proxy for our http requestsn";
print " -e -- custom sql error string (usually not necessary)n";
print " -s -- silent moden";
print " -q -- disable sound alertsn";
print " -r -- do not save the vulnerability reportn";
print " -g -- enable google dorks (powerful, but get a cookie first!)n";
print " -o -- enable file backup scann";
print " -f -- scan for sensitive directories / files (could be noisy)n";
print " -m -- scans the target for e-mail addressesn";
print " -k -- string from charcode encoder/decoder for web attacksrnn";
exit;
}
elsif (defined $params{k})
{
my $str = $params{k};
if($str =~ m/^-?[.|d|,]*Z/)
{
my @strval = split (/,/, $str);
foreach(@strval)
{
print chr($_);
}
}
else
{
foreach(0..length($str)-1)
{
print ord(substr($str,$_, 1));
print "," if ($_ != length($str)-1);
}
}
exit;
}
elsif (!defined $params{u})
{
print "n [ THC WEB VULNERABILITY SCANNER ]nn";
print "a" if(!defined $params{q});
print " ERROR: missing argument(s), use the -h parameter for more informationrnn";
exit;
}
my $verbose = 1;
my $scan_uri = $params{u};
my $emlscan;
my $user_agent;
my $http_cookie;
my $http_proxy;
my $sound_alerts;
my $no_reporting = 0;
my $sqli_error_str = $params{e} || "sql error";
my $xss_error_str = "<XSSTEST>";
my $uriregexp = "<a [^>]*href=.((http://)?((www.)?[-_a-zA-Z0-9/.~?&,=;]*)).[^>]*>(.*)</a>";
$verbose = 0 if(defined $params{s});
$emlscan = 1 if(defined $params{m});
$user_agent = $params{a} if(defined $params{a});
$http_cookie = $params{c} if(defined $params{c});
$http_proxy = $params{p} if(defined $params{p});
if(defined $params{q})
{
$sound_alerts = 0;
}
else
{
$sound_alerts = 1;
}
if(defined $params{r})
{
$no_reporting = 1;
}
$scan_uri =~ s/http:////i;
my $host;
my $banner;
my @data_container;
my @crawled;
my @allowed_filetypes = ("php", "php3", "php4", "php5", "jsp", "html", "htm", "asp", "aspx", "cgi");
my $output_file = "THC_REPORT_".time().".html";
my $active = 0;
sub start_crawling
{
$host = shift;
$| = 1;
if (!$no_reporting)
{
open(OUT,">".$output_file);
print OUT "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"n";
print OUT " "http://www.w3.org/TR/html4/loose.dtd">n";
print OUT "<html>n<head>n<title>THC Scan Report</title>n</head>";
print OUT "<h1><u>THC SCAN REPORT</u></h1>n<br />n<b>Target URI: </b>";
print OUT "<a href="".$host."">".$host."</a>n<br />n<br />n<hr>n";
}
filescan($host) if (defined $params{f});
if (!$verbose)
{
$banner = threads->new(&statusbanner);
$banner->detach;
}
if (defined $params{g})
{
google_attack("site:".$scan_uri." php asc");
google_attack("site:".$scan_uri." php desc");
google_attack("site:".$scan_uri." php id");
google_attack("site:".$scan_uri." php order");
google_attack("site:".$scan_uri." php cat");
google_attack("site:".$scan_uri." php list");
google_attack("site:".$scan_uri." asp asc");
google_attack("site:".$scan_uri." asp desc");
google_attack("site:".$scan_uri." asp id");
google_attack("site:".$scan_uri." asp order");
google_attack("site:".$scan_uri." asp cat");
google_attack("site:".$scan_uri." asp list");
google_attack("site:".$scan_uri." jsp asc");
google_attack("site:".$scan_uri." jsp desc");
google_attack("site:".$scan_uri." jsp id");
google_attack("site:".$scan_uri." jsp order");
google_attack("site:".$scan_uri." jsp cat");
google_attack("site:".$scan_uri." jsp list");
}
crawl($host);
}
sub relative
{
my $uri = shift;
if($uri !~ m/^https?:///)
{
$uri = $host."/".$uri;
}
return $uri;
}
sub get
{
my $uri = shift;
my $agent = $user_agent || "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1)";
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->agent($agent);
$ua->default_header("Cookie" => $http_cookie) if (defined $http_cookie);
$ua->proxy(["http"], "http://".$http_proxy."/") if (defined $http_proxy);
my $response = $ua->get($uri);
if ($response->is_success)
{
return $response->content;
}
else
{
return -1;
}
}
sub check
{
my $target_uri = shift;
my $errormessage = shift;
my $http_response;
my @default_errors = ( "you have an error in your sql syntax", "database error",
"server error in", "oracle error", "mysql_fetch_array",
"mysql_fetch_assoc", "mysql_fetch_num", "mysql_fetch_row",
"mysql_fetch_object", "syntax error", "empty delimiter",
"unclosed quotation mark", "SQLServer JDBC Driver", "ORA-01756" );
$target_uri = relative($target_uri);
$http_response = get($target_uri);
if ((defined $params{o}) && ($target_uri =~ /(.*)?/))
{
if(get($1.".bak") ne "-1")
{
print "a" if ($sound_alerts);
print "Possible backup file found: ".$1.".baknn";
if (!$no_reporting)
{
print OUT "<a href="".$1.".bak">";
print OUT $1.".bak</a>n<br />n";
}
}
elsif(get($1."~") ne "-1")
{
print "a" if ($sound_alerts);
print "Possible backup file found: ".$1."~nn";
if (!$no_reporting)
{
print OUT "<a href="".$1."~">";
print OUT $1."~</a>n<br />n";
}
}
}
if($http_response)
{
if ($http_response =~ m/$errormessage/ig)
{
if($verbose)
{
print "a" if ($sound_alerts);
print "nPossible vulnerability: ".$target_uri."nn";
}
if (!$no_reporting)
{
print OUT "<a href="".$target_uri."">";
$target_uri =~ s/</</g;
$target_uri =~ s/>/>/g;
print OUT $target_uri."</a>n<br />n";
}
}
foreach(@default_errors)
{
if ($http_response =~ m/$_/ig)
{
if($verbose)
{
print "naPossible vulnerability: ".$target_uri."nn";
}
if(!$no_reporting)
{
print OUT " "x4;
print OUT "Possible vulnerability: <a href="".$target_uri."">";
$target_uri =~ s/</</g;
$target_uri =~ s/>/>/g;
print OUT $target_uri."</a>n<br />n";
}
}
}
}
}
sub attack
{
my $uri = shift;
my $mode = shift;
my $response = shift;
my $attack_string;
if($mode == 1)
{
$attack_string = shift || "%27";
}
elsif($mode == 2)
{
$attack_string = shift || "<XSSTEST>";
}
my @split_uri = split (/&/,$uri);
my $current_uri;
while(scalar @split_uri)
{
foreach(@split_uri)
{
$current_uri .= $_."&";
}
pop @split_uri;
$current_uri = substr($current_uri,0,-1).$attack_string;
$current_uri =~ s/&/&/g;
if($verbose)
{
print "Attacking: ".$current_uri."n";
}
if($response)
{
check($current_uri, $response);
}
else
{
check($current_uri);
}
$current_uri = undef;
}
}
sub google_attack
{
my $query = shift;
$query =~ s/ /+/g;
my $resp = get("http://www.google.com/search?q=".$query."&as_qdr=all&num=100&filter=0");
my @content = split(/</h3>/,$resp);
foreach(@content)
{
if($_ =~ m/<li class=g><h3 class=r><ab[^>]*href="(.*?)" b[^>]*>(.*?)</a>/)
{
attack($1, 1, $sqli_error_str);
attack($1, 2, $xss_error_str);
mailscan($_) if (defined ($emlscan));
}
}
}
sub filescan
{
my $host = shift;
my @sensitive_stuff = ("/admin/", "/phpinfo.php", "/server-status", "/old.htaccess", "/htaccess.old", "/icons/",
"/cgi-bin/", "/cgi.cgi/", "/webcgi/", "/cgi-914/", "/cgi-915/", "/bin/", "/cgi/", "/log/",
"/logs/", "/ows-bin/", "/cgi-sys/", "/cgi-local/", "/htbin/", "/scripts/", "/cgi-exe/",
"/~root/", "/robots.txt", "/test/", "/test.php", "/test.asp", "/info.php", "/help/", "/old/",
"/index.php.bak", "/config/", "/conf/", "/config.inc", "/phpmyadmin/", "/global.inc",
"/inc/config.php", "/version.php", "/version.html", "/version.htm", "/README", "/control/",
"/backup/", "/private/", "/stats/", "/traffic/", "/counter.cgi", "/sqladmin/", "/sql/",
"/WS_FTP.LOG", "/Thumbs.db", "/settings/", "/autologon.html?10514", "/administrator/",
"/backup.rar", "/backup.zip", "/backup.tar", "/backup.gz", "/backup.tar.gz", "/backup.bz2",
"/logs.tar", "/logs.rar", "/logs.gz", "/logs.tar.gz", "/logs.bz2", "/logs.zip", "/access.log",
"/guardian.log", "/services/", "/service/", "/sshome/", "/mail/", "/webmail/", "/roundcube/",
"/cgi-perl/", "/php.ini", "/sqldump.sql", "/dump.sql", "/backup.sql", "/structure.sql",
"/logicworks.ini", "/ip.txt", "/ips.txt", "/ip.log", "/ips.log", "/smssend.php", "/sms.php",
"/servlet/", "/_vti_pvt/", "/webalizer/", "/doc/", "/usage/", "/setup/", "/setup.php", "/temp/",
"/mysqldumper/", "/turbodbadmin/", "/tbda/", "/Mem/", "/data/", "/code/", "/boot/", "/pma/",
"/samples/", "/upload/", "/publisher/", "/adserv/", "/incoming/", "/demo/", "/examples/",
"/cert/", "/install.php", "/install/", "/install.php.bak", "/install.php.old", "/CHANGELOG.txt",
"/foo/", "/foo.php", "/foo.txt", "/bla/", "/blah/", "/bleh/", "/default/", "/pdf/", "/_._.html");
foreach(@sensitive_stuff)
{
print "Checking: ".$_."n";
if((get($host.$_) ne "-1") && (get($host.$_) !~ m/404/gi) && (get($host.$_) !~ m/not found/gi))
{
print "a" if ($sound_alerts);
print "Possible sensitive stuff: ".$host.$_."n";
if (!$no_reporting)
{
print OUT "Possible sensitive stuff: <a href="".$host.$_."">".$host.$_."</a>n<br />n";
}
}
}
}
sub mailscan
{
my $uri = shift;
$uri = get($uri);
if($uri)
{
my @uri = split(/n/, $uri);
foreach(@uri)
{
if (get_mail($_))
{
print "a" if ($sound_alerts);
print "Mail address detected: ".get_mail($_)."n";
if (!$no_reporting)
{
print OUT "Mail address detected: ".get_mail($_);
}
}
}
}
}
sub get_mail
{
my $content = shift;
my $mailadd;
my @regexpr;
my @domains = ("ac","ad","ae","aero","af","ag","ai","al","am","an","ao","aq","ar","arpa","as","asia","at","au",
"aw","ax","az","ba","bb","bd","be","bf","bg","bh","bi","biz","bj","bm","bn","bo","br","bs","bt",
"bv","bw","by","bz","ca","cat","cc","cd","cf","cg","ch","ci","ck","cl","cm","cn","co","com","coop",
"cr","cu","cv","cx","cy","cz","de","dj","dk","dm","do","dz","ec","edu","ee","eg","er","es","et",
"eu","fi","fj","fk","fm","fo","fr","ga","gb","gd","ge","gf","gg","gh","gi","gl","gm","gn","gov",
"gp","gq","gr","gs","gt","gu","gw","gy","hk","hm","hn","hr","ht","hu","id","ie","il","im","in",
"info","int","io","iq","ir","is","it","je","jm","jo","jobs","jp","ke","kg","kh","ki","km","kn",
"kp","kr","kw","ky","kz","la","lb","lc","li","lk","lr","ls","lt","lu","lv","ly","ma","mc","md",
"me","mg","mh","mil","mk","ml","mm","mn","mo","mobi","mp","mq","mr","ms","mt","mu","museum","mv",
"mw","mx","my","mz","na","name","nc","ne","net","nf","ng","ni","nl","no","np","nr","nu","nz","om",
"org","pa","pe","pf","pg","ph","pk","pl","pm","pn","pr","pro","ps","pt","pw","py","qa","re","ro",
"rs","ru","rw","sa","sb","sc","sd","se","sg","sh","si","sj","sk","sl","sm","sn","so","sr","st","su",
"sv","sy","sz","tc","td","tel","tf","tg","th","tj","tk","tl","tm","tn","to","tp","tr","travel","tt",
"tv","tw","tz","ua","ug","uk","us","uy","uz","va","vc","ve","vg","vi","vn","vu","wf","ws","ye","yt",
"yu","za","zm","zw");
my $domains = join("|", @domains);
# my black magic mail collection regular expressions ;o)
# they should "bypass" / parse the most mail obfuscations
$regexpr[0] = "(\w+.[a-zA-Z0-9]*\s?_at_\s?\w+.[a-zA-Z0-9]*(.|[dot]|(dot))($domains))";
$regexpr[1] = "(\w+.[a-zA-Z0-9]*\@\w+.[a-zA-Z0-9]*(.|[dot]|(dot))($domains))";
$regexpr[2] = "(\w+.[a-zA-Z0-9]*\s?\[at\]\s?\w+.[a-zA-Z0-9]*(.|[dot]|(dot))($domains))";
$regexpr[3] = "(\w+.[a-zA-Z0-9]*\s?\(at\)\s?\w+.[a-zA-Z0-9]*(.|[dot]|(dot))($domains))";
foreach(@regexpr)
{
if($content =~ m/$_/i)
{
$mailadd = $1;
# some filtering crap
$mailadd =~ s/_at_/@/g;
$mailadd =~ s/[at]/@/g;
$mailadd =~ s/(at)/@/g;
$mailadd =~ s/lt;//g;
$mailadd =~ s/>//g;
$mailadd =~ s/(.*)>//g;
$mailadd =~ s/mailto://g;
return $mailadd;
}
}
}
sub crawl
{
my $uri = shift;
$uri = relative($uri);
if (($uri) && ($uri =~ m/$host/i))
{
$uri = get($uri);
if($uri)
{
my @content = split(/n/,$uri);
foreach(@content)
{
if (($_ =~ m/$uriregexp/i) && (file_match($_, @allowed_filetypes) == 1))
{
if ((array_search($1, @data_container)) == -1)
{
push @data_container, $1;
}
}
}
foreach(@data_container)
{
if ((array_search($_, @crawled)) == -1)
{
attack($_, 1, $sqli_error_str);
attack($_, 2, $xss_error_str);
mailscan($_) if (defined ($emlscan));
push @crawled, $_;
crawl($_);
}
}
}
}
}
sub array_search
{
my $var = shift;
my @array = @_;
my $index = 0;
while ($array[$index])
{
my $v = $array[$index];
if ($v eq $var)
{
last;
}
$index++;
}
if ($index > $#array)
{
return -1;
}
return 1;
}
sub file_match
{
my $var = shift;
my @array = @_;
my $index = 0;
while ($array[$index])
{
my $v = $array[$index];
if ($var =~ m/.$v/i)
{
last;
}
$index++;
}
if ($index > $#array)
{
return -1;
}
return 1;
}
sub delay
{
select(undef, undef, undef, 0.0999999);
}
sub statusbanner
{
$| = 1;
$active = 1;
print "[ ] Crawling... ";
while($active)
{
print "r["."|]";
delay;
print "r["."/]";
delay;
print "r["."".chr(196)."]";
delay;
print "r["."\]";
delay;
}
}
sub finish
{
if(!$no_reporting)
{
print OUT "<hr>n<br />n<br />n";
print OUT "<small>Generated by THC web vulnerability scanner - ";
print OUT "<a href="http://www.secbiz.de" target="_blank">www.secbiz.de</a></small>n";
print OUT "</body>n</html>";
close(OUT);
}
if(!$verbose)
{
$| = 0;
$active = 0;
print "r[#] Scan finished!";
print "n[#] For futher information see "".$output_file.""." if (!$no_reporting);
print "na" if ($sound_alerts);
print "rnn";
}
else
{
print "a" if ($sound_alerts);
print "nnScan finished! ";
print "For futher information see "".$output_file.""." if (!$no_reporting);
print "nrnn";
}
}
start_crawling("http://".$scan_uri);
finish;
» ohne Titel
« ohne Titel

