| 1 | #!/usr/bin/perl |
|---|
| 2 | use strict; |
|---|
| 3 | |
|---|
| 4 | #VERSION,2.03 |
|---|
| 5 | use Getopt::Long; |
|---|
| 6 | Getopt::Long::Configure('no_ignore_case'); |
|---|
| 7 | |
|---|
| 8 | ############################################################################### |
|---|
| 9 | # Nikto # |
|---|
| 10 | # --------------------------------------------------------------------------- # |
|---|
| 11 | # $Id$ # |
|---|
| 12 | # --------------------------------------------------------------------------- # |
|---|
| 13 | ############################################################################### |
|---|
| 14 | # Copyright (C) 2004-2008 CIRT, Inc. |
|---|
| 15 | # |
|---|
| 16 | # This program is free software; you can redistribute it and/or |
|---|
| 17 | # modify it under the terms of the GNU General Public License |
|---|
| 18 | # as published by the Free Software Foundation; version 2 |
|---|
| 19 | # of the License only. |
|---|
| 20 | # |
|---|
| 21 | # This program is distributed in the hope that it will be useful, |
|---|
| 22 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 23 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 24 | # GNU General Public License for more details. |
|---|
| 25 | # |
|---|
| 26 | # You should have received a copy of the GNU General Public License |
|---|
| 27 | # along with this program; if not, write to the Free Software |
|---|
| 28 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|---|
| 29 | # |
|---|
| 30 | # Contact Information: |
|---|
| 31 | # Sullo (sullo@cirt.net) |
|---|
| 32 | # http://cirt.net/ |
|---|
| 33 | ####################################################################### |
|---|
| 34 | # See the README.txt and/or help files for more information on how to use & config. |
|---|
| 35 | # See the LICENSE.txt file for more information on the License Nikto is distributed under. |
|---|
| 36 | # |
|---|
| 37 | # This program is intended for use in an authorized manner only, and the author |
|---|
| 38 | # can not be held liable for anything done with this program, code, or items discovered |
|---|
| 39 | # with this program's use. |
|---|
| 40 | ####################################################################### |
|---|
| 41 | |
|---|
| 42 | # global var/definitions |
|---|
| 43 | use vars qw/$TEMPLATES %ERRSTRINGS %VERSIONS %CLI %VARIABLES %TESTS $CONTENT %FILES $CURRENT_HOST_ID $CURRENT_PORT/; |
|---|
| 44 | use vars qw/%REALMS %REALMS_TESTED %NIKTOCONFIG %NIKTO %OUTPUT %SERVER %request %result %COUNTERS $STARTTIME/; |
|---|
| 45 | use vars qw/%db_extensions %FoF %UPDATES $DIV %TARGETS @DBFILE @SERVERFILE @BUILDITEMS $PROXYCHECKED $http_eol/; |
|---|
| 46 | use vars qw/@RESULTS/; |
|---|
| 47 | |
|---|
| 48 | # setup |
|---|
| 49 | $STARTTIME = localtime(); |
|---|
| 50 | $DIV = "-" x 75; |
|---|
| 51 | $NIKTO{version} = "2.03"; |
|---|
| 52 | $NIKTO{name} = "Nikto"; |
|---|
| 53 | $NIKTO{configfile} = "config.txt"; ### Change this line if your setup is having trouble finding it |
|---|
| 54 | $http_eol = "\r\n"; |
|---|
| 55 | |
|---|
| 56 | # read the --config option |
|---|
| 57 | { |
|---|
| 58 | my %optcfg; |
|---|
| 59 | Getopt::Long::Configure('pass_through', 'noauto_abbrev'); |
|---|
| 60 | GetOptions(\%optcfg, "config=s"); |
|---|
| 61 | Getopt::Long::Configure('nopass_through', 'auto_abbrev'); |
|---|
| 62 | if (defined $optcfg{'config'}) { $NIKTO{configfile} = $optcfg{'config'}; } |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | load_configs(); |
|---|
| 66 | find_plugins(); |
|---|
| 67 | require "$NIKTO{plugindir}/nikto_core.plugin"; ### Change this line if your setup is having trouble finding it |
|---|
| 68 | nprint("T:$STARTTIME: Starting", "d"); |
|---|
| 69 | require "$NIKTO{plugindir}/nikto_reports.plugin"; ### Change this line if your setup is having trouble finding it |
|---|
| 70 | require "$NIKTO{plugindir}/nikto_single.plugin"; ### Change this line if your setup is having trouble finding it |
|---|
| 71 | require "$NIKTO{plugindir}/LW2.pm"; ### Change this line if your setup is having trouble finding it |
|---|
| 72 | |
|---|
| 73 | # use LW2; ### Change this line to use a different installed version |
|---|
| 74 | |
|---|
| 75 | ($a, $b) = split(/\./, $LW2::VERSION); |
|---|
| 76 | die("- You must use LW2 2.4 or later\n") if ($a != 2 || $b < 4); |
|---|
| 77 | |
|---|
| 78 | general_config(); |
|---|
| 79 | load_databases(); |
|---|
| 80 | load_databases('u'); |
|---|
| 81 | |
|---|
| 82 | LW2::http_init_request(\%request); |
|---|
| 83 | $request{'whisker'}->{'ssl_save_info'} = 1; |
|---|
| 84 | $request{'whisker'}->{'lowercase_incoming_headers'} = 1; |
|---|
| 85 | $request{'whisker'}->{'timeout'} = $CLI{timeout} || 10; |
|---|
| 86 | if (defined $CLI{evasion}) { $request{'whisker'}->{'encode_anti_ids'} = $CLI{evasion}; } |
|---|
| 87 | $request{'User-Agent'} = $NIKTO{useragent}; |
|---|
| 88 | $request{'whisker'}->{'retry'} = 0; |
|---|
| 89 | proxy_setup(); |
|---|
| 90 | |
|---|
| 91 | open_output(); |
|---|
| 92 | nprint($DIV); |
|---|
| 93 | |
|---|
| 94 | set_targets(); |
|---|
| 95 | |
|---|
| 96 | $PROXYCHECKED = 0; # only do proxy_check once |
|---|
| 97 | |
|---|
| 98 | # actual scan for each host/port |
|---|
| 99 | foreach $CURRENT_HOST_ID (sort { $a <=> $b } keys %TARGETS) |
|---|
| 100 | { |
|---|
| 101 | LW2::http_reset(); |
|---|
| 102 | $COUNTERS{hosts_completed}++; |
|---|
| 103 | if (($CLI{findonly}) && ($COUNTERS{hosts_completed} % 10) eq 0) { nprint("($COUNTERS{hosts_completed} of $COUNTERS{hosts_total})"); } |
|---|
| 104 | |
|---|
| 105 | ($TARGETS{$CURRENT_HOST_ID}{hostname}, $TARGETS{$CURRENT_HOST_ID}{ip}, $TARGETS{$CURRENT_HOST_ID}{display_name}) = |
|---|
| 106 | resolve($TARGETS{$CURRENT_HOST_ID}{ident}); |
|---|
| 107 | if ($TARGETS{$CURRENT_HOST_ID}{ident} eq "") { next; } |
|---|
| 108 | port_scan($TARGETS{$CURRENT_HOST_ID}{ports_in}); |
|---|
| 109 | |
|---|
| 110 | # make sure we have open ports on this target |
|---|
| 111 | if (keys(%{ $TARGETS{$CURRENT_HOST_ID}{ports} }) eq 0) |
|---|
| 112 | { |
|---|
| 113 | $CURRENT_PORT = $TARGETS{$CURRENT_HOST_ID}{ports_in}; |
|---|
| 114 | $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{start_time_epoch}=time(); |
|---|
| 115 | $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{start_time_disp}=date_disp($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{start_time_epoch}); |
|---|
| 116 | $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner} |
|---|
| 117 | = "(no identification possible)"; |
|---|
| 118 | $TARGETS{$CURRENT_HOST_ID}{total_vulns} = 0; |
|---|
| 119 | $TARGETS{$CURRENT_HOST_ID}{total_checks} = 0; |
|---|
| 120 | $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{stop_time_epoch}=time(); |
|---|
| 121 | $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{stop_time_disp}=date_disp($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{stop_time_epoch}); |
|---|
| 122 | |
|---|
| 123 | $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{elapsed} = $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{stop_time_epoch} - $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{start_time_epoch}; |
|---|
| 124 | write_output(); |
|---|
| 125 | } |
|---|
| 126 | else { |
|---|
| 127 | $request{'whisker'}->{'host'} = $TARGETS{$CURRENT_HOST_ID}{hostname} || $TARGETS{$CURRENT_HOST_ID}{ip}; |
|---|
| 128 | if (defined $TARGETS{$CURRENT_HOST_ID}{vhost}) { $request{'Host'} = $TARGETS{$CURRENT_HOST_ID}{vhost}; } |
|---|
| 129 | foreach $CURRENT_PORT (keys %{$TARGETS{$CURRENT_HOST_ID}{ports}}) |
|---|
| 130 | { |
|---|
| 131 | if ($CURRENT_PORT eq "") { next; } |
|---|
| 132 | $request{'whisker'}->{'port'} = $CURRENT_PORT; |
|---|
| 133 | $request{'whisker'}->{'ssl'} = $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{ssl}; |
|---|
| 134 | $request{'whisker'}->{'version'} = $NIKTOCONFIG{DEFAULTHTTPVER}; |
|---|
| 135 | if (defined $NIKTOCONFIG{'STATIC-COOKIE'}) { $request{'Cookie'} = $NIKTOCONFIG{'STATIC-COOKIE'}; } |
|---|
| 136 | $TARGETS{$CURRENT_HOST_ID}{total_vulns} = 0; |
|---|
| 137 | delete $TARGETS{$CURRENT_HOST_ID}{positives}; |
|---|
| 138 | %FoF = (); |
|---|
| 139 | |
|---|
| 140 | get_banner(); |
|---|
| 141 | |
|---|
| 142 | if ($CLI{findonly}) |
|---|
| 143 | { |
|---|
| 144 | my $protocol = "http"; |
|---|
| 145 | if ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner} eq "") |
|---|
| 146 | { |
|---|
| 147 | $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner} = "(no identification possible)"; |
|---|
| 148 | } |
|---|
| 149 | if ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{ssl}) { $protocol .= "s"; } |
|---|
| 150 | nprint("+ Server: $protocol://$TARGETS{$CURRENT_HOST_ID}{display_name}:$CURRENT_PORT\t$TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner}"); |
|---|
| 151 | } else |
|---|
| 152 | { |
|---|
| 153 | dump_target_info(); |
|---|
| 154 | auth_check(); |
|---|
| 155 | set_scan_items(); |
|---|
| 156 | map_codes(); |
|---|
| 157 | run_plugins(); |
|---|
| 158 | test_target(); |
|---|
| 159 | } |
|---|
| 160 | write_output(); |
|---|
| 161 | } |
|---|
| 162 | } |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | nprint("+ $COUNTERS{hosts_total} host(s) tested"); |
|---|
| 166 | send_updates(); |
|---|
| 167 | close_output(); |
|---|
| 168 | nprint("T:" . localtime() . ": Ending", "d"); |
|---|
| 169 | |
|---|
| 170 | exit; |
|---|
| 171 | |
|---|
| 172 | ################################################################################# |
|---|
| 173 | #### Most code is now in nikto_core.plugin #### |
|---|
| 174 | ################################################################################# |
|---|
| 175 | # load config file |
|---|
| 176 | sub load_configs |
|---|
| 177 | { |
|---|
| 178 | open(CONF, "<$NIKTO{configfile}") || print STDERR "- ERROR: Unable to open config file '$NIKTO{configfile}' ($!), only 1 CGI directory defined.\n"; |
|---|
| 179 | my @CONFILE = <CONF>; |
|---|
| 180 | close(CONF); |
|---|
| 181 | |
|---|
| 182 | foreach my $line (@CONFILE) |
|---|
| 183 | { |
|---|
| 184 | $line =~ s/\#.*$//; |
|---|
| 185 | chomp($line); |
|---|
| 186 | $line =~ s/\s+$//; |
|---|
| 187 | $line =~ s/^\s+//; |
|---|
| 188 | if ($line eq "") { next; } |
|---|
| 189 | my @temp = split(/=/, $line, 2); |
|---|
| 190 | if ($temp[0] ne "") { $NIKTOCONFIG{ $temp[0] } = $temp[1]; } |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | # add CONFIG{CLIOPTS} to ARGV if defined... |
|---|
| 194 | if (defined $NIKTOCONFIG{CLIOPTS}) |
|---|
| 195 | { |
|---|
| 196 | my @t = split(/ /, $NIKTOCONFIG{CLIOPTS}); |
|---|
| 197 | foreach my $c (@t) { push(@ARGV, $c); } |
|---|
| 198 | } |
|---|
| 199 | return; |
|---|
| 200 | } |
|---|
| 201 | ################################################################################# |
|---|
| 202 | # find plugins directory |
|---|
| 203 | sub find_plugins |
|---|
| 204 | { |
|---|
| 205 | |
|---|
| 206 | # get the correct path to 'plugins' |
|---|
| 207 | # if defined in config.txt file... most accurate, we hope |
|---|
| 208 | if ((defined $NIKTOCONFIG{EXECDIR}) && (-d "$NIKTOCONFIG{EXECDIR}/plugins")) |
|---|
| 209 | { |
|---|
| 210 | $NIKTO{execdir} = $NIKTOCONFIG{EXECDIR}; |
|---|
| 211 | $NIKTO{plugindir} = "$NIKTO{execdir}/plugins"; |
|---|
| 212 | $NIKTO{templatedir} = "$NIKTO{execdir}/templates"; |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | unless (defined $NIKTO{execdir}) |
|---|
| 216 | { # try pwd |
|---|
| 217 | if (-d "$ENV{PWD}/plugins") |
|---|
| 218 | { |
|---|
| 219 | $NIKTO{execdir} = $ENV{PWD}; |
|---|
| 220 | $NIKTO{plugindir} = "$NIKTO{execdir}/plugins"; |
|---|
| 221 | $NIKTO{templatedir} = "$NIKTO{execdir}/templates"; |
|---|
| 222 | } else |
|---|
| 223 | { # try $0 |
|---|
| 224 | my $EXECDIR = $0; |
|---|
| 225 | chomp($EXECDIR); |
|---|
| 226 | $EXECDIR =~ s/\/nikto.pl$//; |
|---|
| 227 | |
|---|
| 228 | if (-d "$EXECDIR/plugins") |
|---|
| 229 | { |
|---|
| 230 | $NIKTO{execdir} = $EXECDIR; |
|---|
| 231 | $NIKTO{plugindir} = "$NIKTO{execdir}/plugins"; |
|---|
| 232 | $NIKTO{templatedir} = "$NIKTO{execdir}/templates"; |
|---|
| 233 | } |
|---|
| 234 | } |
|---|
| 235 | |
|---|
| 236 | if ($NIKTO{execdir} eq "") |
|---|
| 237 | { # try ./ |
|---|
| 238 | $NIKTO{execdir} = "./"; |
|---|
| 239 | $NIKTO{plugindir} = "$NIKTO{execdir}/plugins"; |
|---|
| 240 | $NIKTO{templatedir} = "$NIKTO{execdir}/templates"; |
|---|
| 241 | } |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | if (!(-d $NIKTO{plugindir})) |
|---|
| 245 | { |
|---|
| 246 | print STDERR "I can't find 'plugins' directory. I looked around:\n"; |
|---|
| 247 | print STDERR "\t$NIKTOCONFIG{EXECDIR}\n\t$ENV{PWD}\n\t$0\n"; |
|---|
| 248 | print STDERR "Try: switch to the 'nikto' base dir, or\n"; |
|---|
| 249 | print STDERR "Try: set EXECDIR in config.txt\n"; |
|---|
| 250 | exit; |
|---|
| 251 | } |
|---|
| 252 | return; |
|---|
| 253 | } |
|---|
| 254 | ################################################################################# |
|---|