#VERSION,2.1 # $Id: nikto_user_enum_apache.plugin 152 2009-08-13 19:37:44Z deity $ ############################################################################### # Copyright (C) 2004 CIRT, Inc. # # 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; version 2 # of the License only. # # 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. ############################################################################### ############################################################################### # PURPOSE # Enumeration of users and directories in system (as Apache's ~username) ############################################################################### # NOTES # This plugin tries to enumerate all the users and directories # in the system (of course the bruteforce attack is limited to a given range). # In some Apache/UNIX systems this might give out many local users # (which could later on be used for a ssh brute-force attack). # This plugin was originally written by Javier Fernandez-Sanguino Pe–a ############################################################################### sub nikto_user_enum_apache_init { my $id = { name => "user_enum_apache", full_name => "Apache User Enumeration", author => "Javier Fernandez-Sanguinoi Pena", description => "Attempts to enumerate usernames by guessing usernames.", scan_method => \&nikto_user_enum_apache, scan_cond => '$CLI{mutate} =~ /3/ || $CLI{mutate} =~ /4/', copyright => "2008 CIRT Inc." }; return $id; } sub nikto_user_enum_apache { my ($mark) = @_; my $url; my @cgiwraps, @cfgcgi=split(/ /, $VARIABLES{"\@CGIDIRS"}); # Set the URL according to the mutate version my @mutates=split(//,$CLI{mutate}); foreach my $option (@mutates) { if ($option eq "3") { $url="/~"; # First check whether we use a dictionary attack of brute force it if (defined $CLI{mutate-options}) { # We have options - assume it is a dictionary attack nikto_user_enum_apache_dictionary($url); } else { nikto_user_enum_apache_brute($url); } } if ($option eq "4") { # Check for existence of cgiwrap foreach my $cgidir (@CFGCGI) { my $curl = "$cgidir" . "cgiwrap"; (my $result, $content) = nfetch($mark,"$curl", "GET"); if ($content =~ /check your URL/i) { push(@cgiwraps, "$curl"); $valid++; } } foreach my $cgiwrap (@cgiwraps) { $url="$cgiwrap/~"; # First check whether we use a dictionary attack of brute force it if (defined $CLI{mutate-options}) { # We have options - assume it is a dictionary attack nikto_user_enum_apache_dictionary($url); } else { nikto_user_enum_apache_brute($url); } } } } } sub nikto_user_enum_apache_brute { nprint("- Enumerating Apache users (1 to 5 characters).", "v"); # Note1: This script only generates names with letters A-Z (no numbers) # # Note2: this script will generate SUM(26^n)(n=$min to $max) # it's probably faster to write this to a file than to generate it # on the fly BTW. # # Of course, it could be optimized to skip some "strange" # combinations of usernames, but hey, then it wouldn't # be 'brute force' would it? (jfs) my $text = "a"; my $ctr = 0; my $message="Valid users found via Apache enumeration: "; my $result, $content; my @foundusers=(); while (length($text) <= 5) { if (($ctr % 500) eq 0) { nprint("- User enumeration guess $ctr ($text)", "v"); } (my $result, $content) = nfetch($mark,"/~" . $text, "HEAD"); my $user = nikto_user_enum_apache_check($result, $text); if (defined $user) { push(@foundusers,$user); } $text++; $ctr++; } if ($found) { add_vulnerability($mark, $message . join(' ',@foundusers), 999997, "637", "HEAD", "/"); } } sub nikto_user_enum_apache_dictionary { my $filename=$CLI{mutate-options}; my $message="Valid users found via Apache enumeration: "; my @foundusers=(); my $result, $content; my $ctr=0; nprint("- Enumerating Apache users (using dictionary $filename).", "v"); unless (open(IN, "<$filename")) { nprint("- ERROR: Unable to open dictionary file $filename: $!."); } # Now attempt on each entry while () { chomp; s/\#.*$//; # remove preceding ~ just in case s/^~//; if ($_ eq "" ) { next }; if (($ctr % 500) == 0) { nprint("- User enumeration guess $ctr ($_)", "v"); } (my $result, $content) = nfetch($mark,"/~" . $_, "HEAD"); my $user = nikto_user_enum_apache_check($result, $_); if ($user) { push(@foundusers,$user); } $ctr++; } close(IN); if (scalar(@foundusers)) { add_vulnerability($mark, $message . join(' ',@foundusers), 999997, "637", "HEAD", "/"); } } sub nikto_user_enum_apache_check { (my $code, $user) = @_; my $result=""; foreach my $found (split(/ /, $VARIABLES{"\@HTTPFOUND"})) { if ($code eq $found) { $result=$user; last; } } return $result; } 1;