📄 nikto_core.plugin
字号:
#VERSION,2.03#LASTMOD,01.09.2008################################################################################ Copyright (C) 2006 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.############################################################################################################################################################### Nikto core functionality###############################################################################sub test_target{ dump_var("Result Hash", \%result); # this is the actual the looped code for all the checks foreach my $CHECKID (sort keys %TESTS) { if ($CHECKID >= 500000) { next; } # skip TESTS added manually during run (for reports) (my $RES, $CONTENT) = fetch($TESTS{$CHECKID}{uri},$TESTS{$CHECKID}{method},$TESTS{$CHECKID}{data}); nprint("- $RES for $TESTS{$CHECKID}{method}:\t$request{whisker}{uri}","v"); $NIKTO{resp_counts}{$RES}{total}++; # do auth/redir first, independent of test pass/fail if ($RES eq 401) { $result{'www-authenticate'} =~ /realm=\"(.+)\"/; my $R = $1; if ($R eq '') { $R = $result{'www-authenticate'} } my $old_creds = $request{'Authorization'}; # store any creds we have so we don't lose them if ($REALMS_TESTED{$R} ne 1) { auth_guess($R, $CHECKID); } if ($old_creds ne '') { $request{'Authorization'} = $old_creds; } # restore creds nprint("+ $TESTS{$CHECKID}{uri} - Requires Authentication for realm '$R'") if $CLI{display} =~ /4/; } elsif (($RES eq 300) || ($RES eq 301) || ($RES eq 302) || ($RES eq 303) || ($RES eq 307)) { nprint("+ $TESTS{$CHECKID}{uri} - Redirects ($RES) to " . $result{'location'} . " , $TESTS{$CHECKID}{message}") if $CLI{display} =~ /1/; } elsif ($RES eq 200) { nprint("+ $TESTS{$CHECKID}{uri} - 200/OK Response could be $TESTS{$CHECKID}{message}") if $CLI{display} =~ /3/; } my $m1_method= my $m1o_method= my $m1a_method= my $f2_method= my $f1_method ="content"; my $positive=0; # how to check each conditional if ($TESTS{$CHECKID}{match_1} =~ /^[0-9]{3}$/) { $m1_method="code"; } if ($TESTS{$CHECKID}{match_1_or} =~ /^[0-9]{3}$/) { $m1o_method="code"; } if ($TESTS{$CHECKID}{match_1_and} =~ /^[0-9]{3}$/) { $m1a_method="code"; } if ($TESTS{$CHECKID}{fail_1} =~ /^[0-9]{3}$/) { $f1_method="code"; } if ($TESTS{$CHECKID}{fail_2} =~ /^[0-9]{3}$/) { $f2_method="code"; } # basic match for positive result if ($m1_method eq "content") { if ($CONTENT =~ /$TESTS{$CHECKID}{match_1}/) { $positive=1; #print "testid:$CHECKID\n"; #print "match_1:$TESTS{$CHECKID}{match_1}:\n"; #exit; } } else { if (($RES eq $TESTS{$CHECKID}{match_1}) || ($RES eq $FoF{okay}{response})) { $positive=1; } } # no match, check optional match if ((!$positive) && ($TESTS{$CHECKID}{match_1_or} ne "")) { if ($m1o_method eq "content") { if ($CONTENT =~ /$TESTS{$CHECKID}{match_1_or}/) { $positive=1; } } else { if (($RES eq $TESTS{$CHECKID}{match_1_or}) || ($RES eq $FoF{okay}{response})) { $positive=1; } } } # matched on something, check fails/ands if ($positive) { if ($TESTS{$CHECKID}{fail_1} ne "") { if ($f1_method eq "content") { if ($CONTENT =~ /$TESTS{$CHECKID}{fail_1}/) { next; } } else { if ($RES eq $TESTS{$CHECKID}{fail_1}) { next; } } } if ($TESTS{$CHECKID}{fail_2} ne "") { if ($f2_method eq "content") { if ($CONTENT =~ /$TESTS{$CHECKID}{fail_2}/) { next; } } else { if ($RES eq $TESTS{$CHECKID}{fail_2}) { next; } } } if ($TESTS{$CHECKID}{match_1_and} ne "") { if ($m1a_method eq "content") { if ($CONTENT !~ /$TESTS{$CHECKID}{match_1_and}/) { next; } } else { if ($RES ne $TESTS{$CHECKID}{match_1_and}) { next; } } } # if it's an index.php, check for normal /index.php to see if it's a FP if ($TESTS{$CHECKID}{uri} =~ /^\/index.php\?/) { my $CONTENT=rm_active_content($CONTENT, $TESTS{$CHECKID}{uri}); if (LW2::md4($CONTENT) eq $FoF{'index.php'}{match}) { next; } } # lastly check for a false positive based on file extension or type if (($m1_method eq "code") || ($m1o_method eq "code")) { if (is_404($request{whisker}{uri},$CONTENT,$RES)) { next; } } $TARGETS{$CURRENT_HOST_ID}{total_vulns}++; $TESTS{$CHECKID}{osvdb} =~ s/\s+/ OSVDB\-/g; nprint("+ OSVDB-$TESTS{$CHECKID}{osvdb}: $TESTS{$CHECKID}{method} $request{whisker}{uri} : $TESTS{$CHECKID}{message}"); $TARGETS{$CURRENT_HOST_ID}{positives}{$CHECKID}=1; } } # end check loop nprint("+ $TARGETS{$CURRENT_HOST_ID}{total_checks} items checked: $TARGETS{$CURRENT_HOST_ID}{total_vulns} item(s) reported on remote host"); $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{stop_time_epoch}=time(); $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{stop_time_disp}=date_disp($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{stop_time_epoch}); $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}; nprint("+ End Time: $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{stop_time_disp} ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{elapsed} seconds)"); nprint($DIV); return;}###############################################################################sub is_404{ my ($uri, $content, $rescode) = @_; $ext=get_ext($uri); if (($FoF{$ext}{mode} eq "STD") && (($rescode eq 401) || ($rescode eq 403) || ($rescode eq 404) || ($rescode eq 410))) { return 1; } elsif ($FoF{$ext}{mode} eq "STD") { return 0; } elsif ($FoF{$ext}{mode} eq "REDIR") { if ($result{location} eq $FoF{$ext}{location}) { return 1; } } elsif (($FoF{$ext}{type} eq "CONTENT") && ($content =~ /$FoF{$ext}{match}/i)) { return 1; } elsif (($FoF{$ext}{type} eq "BLANK") && ($content eq "")) { return 1; } elsif ($FoF{$ext}{type} eq "HASH") { my $content=rm_active_content($content, $uri); if (LW2::md4($content) eq $FoF{$ext}{match}) { return 1; } }return 0;}###############################################################################sub nprint{ my $line=$_[0]; chomp($line); # don't print debug & verbose to output file... if ($_[1] eq "d") { if ($OUTPUT{debug}) { print "D:" . localtime() . " $line\n"; } return; } elsif ($_[1] eq "v") { if ($OUTPUT{verbose}) { print "V:" . localtime() . " $line\n"; } return; } # print to STDOUT if ($line =~ /ERROR\:/) { print STDERR "$line\n"; } else { print "$line\n"; } # if no file saving, return if ($CLI{file} eq "") { return; } # HTML is handled elsewhere, sadly if ($CLI{format} eq "txt") { $line =~ s/((CVE|CAN)\-[0-9]{4}-[0-9]{4})/http:\/\/cve.mitre.org\/cgi-bin\/cvename.cgi?name\=$1/g; $line =~ s/(CA\-[0-9]{4}-[0-9]{2})/http:\/\/www.cert.org\/advisories\/$1.html/g; $line =~ s/BID\-([0-9]{4})/http:\/\/www.securityfocus.com\/bid\/$1/g; $line =~ s/(IN\-[0-9]{4}\-[0-9]{2})/http:\/\/www.cert.org\/incident_notes\/$1.html/gi; $line =~ s/(MS[0-9]{2}\-[0-9]{3})/http:\/\/www.microsoft.com\/technet\/security\/bulletin\/$1.asp/gi; print OUT "$line\n"; } elsif ($CLI{format} eq "csv") { chomp($line); if ($line =~ /-------------/) { $line="\"$DIV\",\"$DIV\""; } if ($line =~ /^\+/) { $line =~ s/^\+ (.*) - (.*)/"$1","$2"/; } $line =~ s/Target IP: (.*)/"Target IP","$1"/; $line =~ s/Target Hostname: (.*)/"Target Hostname","$1"/; $line =~ s/Target Port: (.*)/"Target Port","$1"/; $line =~ s/Start Time: (.*)/"Start Time","$1"/; $line =~ s/End Time: (.*)/"End Time","$1"/; $line =~ s/ERROR: (.*)/"ERROR","$1"/; chomp($line); $line =~ s/\t/ /g; $line =~ s/Server: ([^ ]*)(.*$)/"Server","$1","$2"/; $line =~ s/Test Options: (.*)/"Test Options","$1\n"/; $line =~ s/Allowed HTTP Methods: (.*)/"Allowed HTTP Methods","$1"/; $line =~ s/^\+//; $line =~ s/^\-//; $line =~ s/^\s+//; if ($line !~ /^\"/) { $line = "\"$line\""; } $line =~ s/\n\r//g; $line =~ s/\"\"/\"/g; print OUT "$line\n"; } return;}###############################################################################sub get_ext{ my $uri=$_[0] || return; if ($uri =~ /\/$/) { return "DIRECTORY"; } $uri =~ s/^.*\///; $uri =~ s/(\?|\&|\%).*$//; if ($uri =~ /^\.[^.%]/) { return "DOTFILE"; } if ($uri !~ /\./) { return "NONE"; } $uri =~ s/\".*$//; $uri =~ s/^.*\.//; return $uri;}###############################################################################sub date_disp{ my @time=localtime($_[0]); $time[5]+=1900; $time[4]++; if (length($time[4]) eq 1) { $time[4]="0$time[4]"; } $time[3]++; if (length($time[3]) eq 1) { $time[3]="0$time[3]"; } if (length($time[0]) eq 1) { $time[0]="0$time[0]"; } if (length($time[1]) eq 1) { $time[1]="0$time[1]"; } if (length($time[2]) eq 1) { $time[0]="0$time[2]"; } return "$time[5]-$time[4]-$time[3] $time[2]:$time[1]:$time[0]";}###############################################################################sub map_codes{ my %REQS; my $rs=LW2::utils_randstr(8); # / for OK response $NIKTO{totalrequests}++; $request{'whisker'}->{'uri'} = "/"; delete $request{'whisker'}->{'data'}; $request{'whisker'}->{'method'} = GET; $request{'whisker'}->{'http_eol'}=$http_eol; delete $request{'whisker'}->{'Content-Length'}; LW2::http_close(\%request); # force-close any old connections dump_var("Request Hash", \%request); if ($CLI{pause} > 0) { sleep $CLI{pause}; } LW2::http_do_request_timeout(\%request,\%result); dump_var("Result Hash", \%result); if ($result{location} ne "") { nprint("- Root page / redirects to: $result{location}"); if ($result{location} =~ /^$request{'whisker'}{'host'}/i) # same host { $request{'whisker'}->{'uri'} = $result{location}; $request{'whisker'}->{'uri'} =~ s/^http(s)?\:\/\/$request{'whisker'}{'host'}//i; $request{'whisker'}->{'http_eol'}=$http_eol; LW2::http_close(\%request); # force-close any old connections LW2::http_fixup_request(\%request); dump_var("Request Hash", \%request); if ($CLI{pause} > 0) { sleep $CLI{pause}; } LW2::http_do_request_timeout(\%request,\%result); dump_var("Result Hash", \%result); } else # different host... ugh... guess { $FoF{okay}{response}=200; $FoF{okay}{type}="STD"; } } else { $FoF{okay}{response}=$result{'whisker'}->{'code'}; my $content=rm_active_content($result{'whisker'}->{'data'}); $FoF{okay}{type}="HASH"; $FoF{okay}{match}=LW2::md4($content); } # these are some used in mutate that may not be in the db_tests $db_extensions{bak}=1; $db_extensions{data}=1; $db_extensions{dbc}=1; $db_extensions{dbf}=1; $db_extensions{lst}=1; $db_extensions{htx}=1; foreach my $ext (keys %db_extensions) { if ($ext eq "DIRECTORY") { next; } # don't test generic type holder as real extension (added to db_extensions by get_ext) if ($ext eq "NONE") { next; } # don't test generic type holder as real extension (added to db_extensions by get_ext) if ($ext eq "DOTFILE") { next; } # don't test generic type holder as real extension (added to db_extensions by get_ext) $REQS{"/$rs.$ext"}=$ext; } # add those generic type holders back as real files $REQS{"/$rs/"}="DIRECTORY"; $REQS{"/$rs"}="NONE"; $REQS{"/.$rs"}="DOTFILE"; foreach my $file (keys %REQS) { nprint("- Testing error for file: $file\n","v"); $NIKTO{totalrequests}++; $request{'whisker'}->{'uri'} = $file; $request{'whisker'}->{'method'} = GET; $request{'whisker'}->{'http_eol'}=$http_eol; LW2::http_close(\%request); # force-close any old connections delete $request{'whisker'}->{'data'}; delete $request{'Content-Encoding'}; delete $request{'Content-Length'}; LW2::http_fixup_request(\%request); dump_var("Request Hash", \%request); if ($CLI{pause} > 0) { sleep $CLI{pause}; } LW2::http_do_request_timeout(\%request,\%result); dump_var("Result Hash", \%result); $ext=$REQS{$file}; $FoF{$ext}{response} = $result{'whisker'}->{'code'}; if ($result{location} ne "") { $FoF{$ext}{location} = $result{location}; $file=char_escape($file); $FoF{$ext}{location} =~ s/$file//;# if ($ext eq "php")# {#print "$ext $FoF{$ext}\n";#print "loc is:$FoF{$ext}{location}\n"; exit; #} } # handle .com to .org redirs or whatnot # if it is not specific type, figure out Content or HASH method... if ($FoF{$ext}{response} eq 404) { $FoF{$ext}{mode} = "STD"; next; } elsif ($FoF{$ext}{response} eq 200) { $FoF{$ext}{mode} = "OK"; } elsif ($FoF{$ext}{response} eq 410) { $FoF{$ext}{mode} = "STD"; next; } elsif ($FoF{$ext}{response} eq 401) { $FoF{$ext}{mode} = "STD"; next; } elsif ($FoF{$ext}{response} eq 403) { $FoF{$ext}{mode} = "STD"; next; } elsif ($FoF{$ext}{response} eq 300) { $FoF{$ext}{mode} = "REDIR"; next; } elsif ($FoF{$ext}{response} eq 301) { $FoF{$ext}{mode} = "REDIR"; next; } elsif ($FoF{$ext}{response} eq 302) { $FoF{$ext}{mode} = "REDIR"; next; } elsif ($FoF{$ext}{response} eq 303) { $FoF{$ext}{mode} = "REDIR"; next; } elsif ($FoF{$ext}{response} eq 307) { $FoF{$ext}{mode} = "REDIR"; next; } else { $FoF{$ext}{mode} = "OTHER"; } # if we've got an OK/OTHER response, look at content first my $done=0; foreach my $string (keys %ERRSTRINGS) { nprint ("- Testing error string: $ERRSTRINGS{$string}","d");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -