Changeset 387


Ignore:
Timestamp:
04/10/2010 02:26:45 PM (3 years ago)
Author:
deity
Message:

Mangled apacheusers and user_enum_apache together. Added facility for plugins to describe their options

Location:
trunk
Files:
1 deleted
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/docs/CHANGES.txt

    r386 r387  
     12010-04-10 
     2        - Merged apacheusers and apache_enum_users together 
     3        - Add facillity for a plugin to inform which options it can take 
    142010-04-09 
    25        - Ticket 137: Added -ask to override nikto.conf's UPDATES value (same options) 
  • trunk/plugins/nikto_apacheusers.plugin

    r344 r387  
    2121# Apache user enumeration 
    2222############################################################################### 
     23 
    2324sub nikto_apacheusers_init { 
    2425    my $id = { 
    2526            name        => "apacheusers", 
    2627            full_name   => "Apache Users", 
    27             author      => "Sullo", 
     28            author      => "Javier Fernandez-Sanguinoi Pena", 
    2829            description => "Checks whether we can enumerate usernames directly from the web server", 
    2930            scan_method => \&nikto_apacheusers, 
    30             copyright   => "2008 CIRT Inc." 
     31            copyright   => "2008 CIRT Inc.", 
     32            options     =>  { 
     33                                enumerate   => "Flag to indicate whether to attempt to enumerate users", 
     34                                dictionary  => "Filename for a dictionary file of users", 
     35                                size        => "Maximum size of username if bruteforcing" 
     36                            } 
    3137            }; 
    3238    return $id; 
     
    3440 
    3541sub nikto_apacheusers { 
    36     my ($mark) = @_; 
    37     (my $RES, $CONTENT) = nfetch($mark, "/~root", "GET", "", "", "", "apacheusers: known user"); 
    38  
    39     $CONTENT = char_escape($CONTENT); 
    40     if ($CONTENT =~ /forbidden/i)    # good on "root" 
     42    my ($mark, $parameters) = @_; 
     43    my $apacheusers=0; 
     44     
     45    # First ensure that the server is vulnerable 
     46    (my $result, $content) = nfetch($mark, "/~root", "GET", "", "", "", "apacheusers: known user"); 
     47 
     48    $content = char_escape($content); 
     49    if ($content =~ /forbidden/i)    # good on "root" 
    4150    { 
    42         (my $RES, $CONTENT) = nfetch($mark, "/~" . LW2::utils_randstr(8), 
    43                                      "GET", "", "", "", "apacheusers: invalid user"); 
    44  
    45         $CONTENT = char_escape($CONTENT); 
    46         if ($CONTENT !~ /forbidden/i)    # Good, it gave an error instead of forbidden 
     51        (my $result, $content) = nfetch($mark, "/~" . LW2::utils_randstr(8), 
     52                                        "GET", "", "", "", "apacheusers: invalid user"); 
     53 
     54        $content = char_escape($content); 
     55        if ($content !~ /forbidden/i)    # Good, it gave an error instead of forbidden 
    4756        { 
    4857            add_vulnerability( 
     
    5564                ); 
    5665        } 
    57     } 
     66        $apacheusers=1; 
     67    } 
     68     
     69    # If we can't enumerate users then return 
     70    return unless ($apacheusers == 1); 
     71    # If we haven't been asked to enumerate users then return 
     72    return unless (defined $parameters->{'enumerate'} && 
     73                   $parameters->{'enumerate'} == 1); 
     74     
     75    # Now we can attempt to enumerate the users 
     76    my ($url, $dictfile, $size); 
     77    my @cgiwraps; 
     78    my @cfgcgi = split(/ /, $VARIABLES{"\@CGIDIRS"}); 
     79 
     80    if (defined $parameters->{'dictionary'}) { 
     81        $dictfile = $parameters->{'dictionary'}; 
     82    } 
     83    if (defined $parameters->{'size'}) { 
     84        $size = $parameters->{'size'}; 
     85    } 
     86 
     87    # Set the URL according to the parameters 
     88    if (defined $parameters->{'cgiwrap'}) { 
     89 
     90        # Check for existence of cgiwrap 
     91        foreach my $cgidir (@CFGCGI) { 
     92            my $curl = "$cgidir" . "cgiwrap"; 
     93            (my $result, $content) = 
     94                nfetch($mark, $curl, "GET", "", "", "", "user_enum_apache: cgiwrap"); 
     95            if ($content =~ /check your URL/i) { 
     96                push(@cgiwraps, "$curl"); 
     97            } 
     98        } 
     99 
     100        foreach my $cgiwrap (@cgiwraps) { 
     101            $url = "$cgiwrap/~"; 
     102 
     103            # First check whether we use a dictionary attack of brute force it 
     104            if (defined $dictfile) { 
     105 
     106                # We have options - assume it is a dictionary attack 
     107                nikto_user_enum_apache_dictionary($url, $mark, $dictfile); 
     108            } 
     109            else { 
     110                nikto_user_enum_apache_brute($url, $mark, $size); 
     111            } 
     112        } 
     113    } 
     114    else { 
     115        # Assume we're using ~ if not told to use cgiwrap 
     116        $url = "/~"; 
     117 
     118        # First check whether we use a dictionary attack of brute force it 
     119        if (defined $dictfile) { 
     120 
     121            # We have options - assume it is a dictionary attack 
     122            nikto_user_enum_apache_dictionary($url, $mark, $dictfile); 
     123        } 
     124        else { 
     125            nikto_user_enum_apache_brute($url, $mark, $size); 
     126        } 
     127    } 
     128} 
     129 
     130sub nikto_user_enum_apache_brute { 
     131 
     132    # Note1: This script only generates names with letters A-Z (no numbers) 
     133    # 
     134    # Note2: this script will generate SUM(26^n)(n=$min to $max) 
     135    # it's probably faster to write this to a file than to generate it 
     136    # on the fly BTW. 
     137    # 
     138    # Of course, it could be optimized to skip some "strange" 
     139    # combinations of usernames, but hey, then it wouldn't 
     140    # be 'brute force' would it? (jfs) 
     141 
     142    my ($url, $mark, $size) = @_; 
     143    $size = 5 if ($size eq ""); 
     144    nprint("- Enumerating Apache users (1 to $size characters).", "v"); 
     145 
     146    my $text    = "a"; 
     147    my $ctr     = 0; 
     148    my $message = "Valid users found via Apache enumeration: "; 
     149    my ($result, $content); 
     150    my @foundusers = (); 
     151    while (length($text) <= $size) { 
     152        if (($ctr % 500) eq 0) { nprint("- User enumeration guess $ctr ($text)", "v"); } 
     153        ($result, $content) = 
     154          nfetch($mark, $url . $text, "HEAD", "", "", "", "user_enum_apache: enumeration"); 
     155        my $user = nikto_user_enum_apache_check($result, $text); 
     156        if (defined $user && $user ne "") { 
     157            push(@foundusers, $user); 
     158        } 
     159        $text++; 
     160        $ctr++; 
     161    } 
     162    if (scalar(@foundusers)) { 
     163        add_vulnerability($mark, $message . join(', ', @foundusers), 999997, "637", "HEAD", "/"); 
     164    } 
     165 
     166} 
     167 
     168sub nikto_user_enum_apache_dictionary { 
     169    my ($url, $mark, $filename) = @_; 
     170    my $message    = "Valid users found via Apache enumeration: "; 
     171    my @foundusers = (); 
     172    my ($result, $content); 
     173    my $ctr = 0; 
     174 
     175    nprint("- Enumerating Apache users (using dictionary $filename).", "v"); 
     176    unless (open(IN, "<$filename")) { 
     177        nprint("+ ERROR: Unable to open dictionary file $filename: $!."); 
     178    } 
     179 
     180    # Now attempt on each entry 
     181    while (<IN>) { 
     182        chomp; 
     183        s/\#.*$//; 
     184 
     185        # remove preceding ~ just in case 
     186        s/^~//; 
     187        if ($_ eq "") { next } 
     188        if (($ctr % 500) == 0) { nprint("- User enumeration guess $ctr ($_)", "v"); } 
     189        ($result, $content) = 
     190          nfetch($mark, $url . $_, "HEAD", "", "", "", "user_enum_apache: dictionary"); 
     191        my $user = nikto_user_enum_apache_check($result, $_); 
     192        if ($user) { 
     193            push(@foundusers, $user); 
     194        } 
     195        $ctr++; 
     196    } 
     197    close(IN); 
     198    if (scalar(@foundusers)) { 
     199        add_vulnerability($mark, $message . join(', ', @foundusers), 999997, "637", "HEAD", "/"); 
     200    } 
     201} 
     202 
     203sub nikto_user_enum_apache_check { 
     204    (my $code, $user) = @_; 
     205    my $result = ""; 
     206 
     207    foreach my $found (split(/ /, $VARIABLES{"\@HTTPFOUND"})) { 
     208        if ($code eq $found) { 
     209            $result = $user; 
     210            last; 
     211        } 
     212    } 
     213 
     214    return $result; 
    58215} 
    59216 
    602171; 
     218 
  • trunk/plugins/nikto_core.plugin

    r385 r387  
    25792579        nprint(" $plugin->{'full_name'} - $plugin->{'description'}"); 
    25802580        nprint(" Written by $plugin->{'author'}, Copyright (C) $plugin->{'copyright'}"); 
     2581        if (defined $plugin->{'options'}) { 
     2582            nprint(" Options:"); 
     2583            while (my ($option, $description) = each(%{$plugin->{'options'}})) { 
     2584                nprint("  $option: $description"); 
     2585            } 
     2586        } 
    25812587        nprint("\n"); 
    25822588    } 
  • trunk/plugins/nikto_tests.plugin

    r361 r387  
    2222############################################################################### 
    2323sub nikto_tests_init { 
    24     my $id = { name        => "Tests", 
     24    my $id = { name        => "tests", 
    2525               full_name   => "Nikto Tests", 
    2626               author      => "Sullo, Deity", 
     
    2929               scan_method => \&nikto_tests, 
    3030               scan_weight => 99, 
     31               options     =>   {   passfiles   => "Flag to indicate whether to check for common password files",   
     32                                    all         => "Flag to indicate whether to check all files with all directories" 
     33                                } 
    3134               }; 
    3235    return $id; 
Note: See TracChangeset for help on using the changeset viewer.