Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| ############################################################################### | |
| # SPDX-License-Identifier: GPL-3.0-only | |
| # PURPOSE: Apache user enumeration | |
| ############################################################################### | |
| sub nikto_apacheusers_init { | |
| my $id = { | |
| name => "apacheusers", | |
| full_name => "Apache Users", | |
| author => "Javier Fernandez-Sanguinoi Pena", | |
| description => "Checks whether we can enumerate usernames directly from the web server", | |
| hooks => { scan => { method => \&nikto_apacheusers, }, }, | |
| copyright => "2008 Chris Sullo", | |
| options => { | |
| enumerate => "Flag to indicate whether to attempt to enumerate users", | |
| dictionary => "Filename for a dictionary file of users", | |
| size => "Maximum size of username if bruteforcing", | |
| home => "Look for ~user to enumerate", | |
| cgiwrap => "User cgi-bin/cgiwrap to enumerate" | |
| } | |
| }; | |
| return $id; | |
| } | |
| sub nikto_apacheusers { | |
| my ($mark, $parameters) = @_; | |
| return if $mark->{'terminate'}; | |
| my $apacheusers = 0; | |
| # If we haven't been asked to enumerate users then return | |
| return unless (defined $parameters->{'enumerate'} | |
| && $parameters->{'enumerate'} == 1); | |
| # First ensure that the server is vulnerable | |
| my ($res, $content, $error, $request, $response) = nfetch($mark, "/~bin", "GET", "", "", "", "apacheusers: known user"); | |
| if ($content =~ /forbidden/i) # good on "bin" | |
| { | |
| my ($res, $content, $errors, $request, $response) = | |
| nfetch($mark, "/~" . LW2::utils_randstr(8), | |
| "GET", "", "", "", "apacheusers: invalid user"); | |
| if ($content !~ /forbidden/i) # Good, it gave an error instead of forbidden | |
| { | |
| add_vulnerability( | |
| $mark, | |
| "/~bin: Enumeration of users is possible by requesting ~username (responds with 'Forbidden' for users and 'not found' for non-existent users).", | |
| 999999, | |
| "CVE-2001-1013", | |
| "GET", | |
| "/~bin", | |
| $request, | |
| $response | |
| ); | |
| } | |
| $apacheusers = 1; | |
| } | |
| # If we can't enumerate users then return | |
| return unless ($apacheusers == 1); | |
| # Now we can attempt to enumerate the users | |
| my ($url, $dictfile, $size); | |
| my @cgiwraps; | |
| my @CFGCGI = split(/ /, $VARIABLES{"\@CGIDIRS"}); | |
| if (defined $parameters->{'dictionary'}) { | |
| $dictfile = $parameters->{'dictionary'}; | |
| } | |
| if (defined $parameters->{'size'}) { | |
| $size = $parameters->{'size'} if $parameters->{'size'} =~ /^\d+$/; | |
| } | |
| # Set the URL according to the parameters | |
| if (defined $parameters->{'cgiwrap'}) { | |
| # Check for existence of cgiwrap | |
| foreach my $cgidir (@CFGCGI) { | |
| my $curl = "$cgidir" . "cgiwrap"; | |
| my ($response, $content) = | |
| nfetch($mark, $curl, "GET", "", "", "", "user_enum_apache: cgiwrap"); | |
| if ($content =~ /check your URL/i) { | |
| push(@cgiwraps, "$curl"); | |
| } | |
| } | |
| foreach my $cgiwrap (@cgiwraps) { | |
| $url = "$cgiwrap/~"; | |
| # First check whether we use a dictionary attack of brute force it | |
| if (defined $dictfile) { | |
| # We have options - assume it is a dictionary attack | |
| nikto_user_enum_apache_dictionary($url, $mark, $dictfile); | |
| } | |
| else { | |
| nikto_user_enum_apache_brute($url, $mark, $size); | |
| } | |
| } | |
| } | |
| if (defined $parameters->{'home'}) { | |
| $url = "/~"; | |
| # First check whether we use a dictionary attack of brute force it | |
| if (defined $dictfile) { | |
| # We have options - assume it is a dictionary attack | |
| nikto_user_enum_apache_dictionary($url, $mark, $dictfile); | |
| } | |
| else { | |
| nikto_user_enum_apache_brute($url, $mark, $size); | |
| } | |
| } | |
| } | |
| sub nikto_user_enum_apache_brute { | |
| # 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 ($url, $mark, $size) = @_; | |
| $size //= 5; | |
| nprint("- Enumerating Apache users (1 to $size characters).", "v", "apacheusers"); | |
| my $text = "a"; | |
| my $ctr = 0; | |
| my $message = "Valid users found via Apache enumeration: "; | |
| my ($response, $content); | |
| my @foundusers = (); | |
| while (length($text) <= $size) { | |
| return if $mark->{'terminate'}; | |
| if (($ctr % 500) == 0) { | |
| nprint("- User enumeration guess $ctr ($text)", "v", "apacheusers"); | |
| } | |
| my ($res, $content, $errors, $request, $response) = | |
| nfetch($mark, $url . $text, "HEAD", "", "", "", "user_enum_apache: enumeration"); | |
| my $user = nikto_user_enum_apache_check($response, $text); | |
| if (defined $user && $user ne "") { | |
| push(@foundusers, $user); | |
| } | |
| $text++; | |
| $ctr++; | |
| } | |
| if (scalar(@foundusers)) { | |
| my $u = join(', ', @foundusers); | |
| add_vulnerability($mark, $message . $u, | |
| "000479", "CVE-2001-1013", "HEAD", "/", $request, $response); | |
| } | |
| } | |
| sub nikto_user_enum_apache_dictionary { | |
| my ($url, $mark, $filename) = @_; | |
| my $message = "Valid users found via Apache enumeration: "; | |
| my @foundusers = (); | |
| my ($response, $content); | |
| my $ctr = 0; | |
| nprint("- Enumerating Apache users (using dictionary $filename).", "v", "apacheusers"); | |
| open(my $fh, "<", $filename) | |
| or do { nprint("+ ERROR: Unable to open dictionary file $filename: $!."); return; }; | |
| # Now attempt on each entry | |
| while (<$fh>) { | |
| return if $mark->{'terminate'}; | |
| chomp; | |
| s/\#.*$//; | |
| # remove preceding ~ just in case | |
| s/^~//; | |
| if ($_ eq "") { next } | |
| if (($ctr % 500) == 0) { nprint("- User enumeration guess $ctr ($_)", "v", "apacheusers"); } | |
| my($res, $content, $errors, $request, $response) = | |
| nfetch($mark, $url . $_, "HEAD", "", "", "", "user_enum_apache: dictionary"); | |
| my $user = nikto_user_enum_apache_check($response, $_); | |
| if ($user) { | |
| push(@foundusers, $user); | |
| } | |
| $ctr++; | |
| } | |
| close($fh); | |
| if (scalar(@foundusers)) { | |
| my $u = join(', ', @foundusers); | |
| add_vulnerability($mark, $message . $u, | |
| "000478", "CVE-2001-1013", "HEAD", "/", $request, $response); | |
| } | |
| } | |
| sub nikto_user_enum_apache_check { | |
| (my $code, my $user) = @_; | |
| my $response = ""; | |
| foreach my $found (split(/ /, $VARIABLES{"\@HTTPFOUND"})) { | |
| if ($code eq $found) { | |
| $response = $user; | |
| last; | |
| } | |
| } | |
| return $response; | |
| } | |
| 1; | |