All pastes #1933278 Raw Edit

THC Web Vulnerability Scanner

public text v1 · immutable
#1933278 ·published 2010-09-05 13:08 UTC
rendered paste body
#!/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 ]\n\n";

   print "  -u -- defines the target url (required)\n";

   print "  -c -- defines the cookie for our http requests\n";

   print "  -a -- defines the user agent for our http request\n";

   print "  -p -- defines a http proxy for our http requests\n";

   print "  -e -- custom sql error string (usually not necessary)\n";

   print "  -s -- silent mode\n";

   print "  -q -- disable sound alerts\n";

   print "  -r -- do not save the vulnerability report\n";

   print "  -g -- enable google dorks (powerful, but get a cookie first!)\n";

   print "  -o -- enable file backup scan\n";

   print "  -f -- scan for sensitive directories / files (could be noisy)\n";

   print "  -m -- scans the target for e-mail addresses\n";

   print "  -k -- string from charcode encoder/decoder for web attacks\r\n\n";

   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 ]\n\n";

   print "\a" if(!defined $params{q});

   print " ERROR: missing argument(s), use the -h parameter for more information\r\n\n";

   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.".bak\n\n";

         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."~\n\n";

         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."\n\n";

         }



         if (!$no_reporting)

         {

            print OUT "<a href=\"".$target_uri."\">";

 

            $target_uri =~ s/</\&lt;/g;

            $target_uri =~ s/>/\&gt;/g;



            print OUT $target_uri."</a>\n<br />\n";

         }



      }



      foreach(@default_errors)

      {

         if ($http_response =~ m/$_/ig)

         {

            if($verbose)

            {

               print "\n\aPossible vulnerability: ".$target_uri."\n\n";

            }



            if(!$no_reporting)

            {

               print OUT " "x4;

               print OUT "Possible vulnerability: <a href=\"".$target_uri."\">";

 

               $target_uri =~ s/</\&lt;/g;

               $target_uri =~ s/>/\&gt;/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/\&amp;/\&/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><a\b[^>]*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/\&gt//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 "\n\a" if ($sound_alerts);

      print "\r\n\n";

   }

   

   else

   {

      print "\a" if ($sound_alerts);

      print "\n\nScan finished! ";

      print "For futher information see \"".$output_file."\"." if (!$no_reporting);

      print "\n\r\n\n";

   }

   

}



start_crawling("http://".$scan_uri);

finish;