nikto_core.plugin
来自「Ubuntu packages of security software。 相」· PLUGIN 代码 · 共 1,842 行 · 第 1/5 页
PLUGIN
1,842 行
} } $PROXYCHECKED=1; return;}################################################################################## directory listing# 'pattern' is an optional regex to match the file names against# written by Thomas Reucker for the SETI-Web project (GPL)#################################################################################sub dirlist{ my $DIR=$_[0] || return; my $PATTERN=$_[1] || ""; my @FILES_TEMP = (); # some basic security checks... REALLY basic # this should be better if ($DIR =~ /etc/) { return; } opendir(DIRECTORY,$DIR) || die print STDERR "+ ERROR: Can't open directory '$DIR': $@"; foreach my $file (readdir(DIRECTORY)) { if ($file =~ /^\./) { next; } # skip hidden files, '.' and '..' if ($PATTERN ne "") { if ($file =~ /$PATTERN/) { push (@FILES_TEMP,$file); } } else { push (@FILES_TEMP,$file); } }closedir(DIRECTORY); return @FILES_TEMP;}######################################################################## dbcheck# checks the standard databases for duplicate entries#######################################################################sub dbcheck { my (@L, @ENTRIES, %ENTRIES)=(); my ($line, $entry) =""; my $ctr=0; #### scan_database.db print "-->\tDB Syntax ($NIKTOFILES{dbfile})\n"; open(IN,"<$NIKTOFILES{dbfile}") || die print STDERR "\tERROR: Unable to open '$NIKTOFILES{dbfile}' for read: $@\n"; @ENTRIES=<IN>; close(IN); foreach $line (@ENTRIES) { if ($line !~ /^\"/) { next; } @L=parse_csv($line); if (($#L < 5) || ($#L > 6)) { print STDERR "\tERROR: Invalid syntax ($#L): $line"; next; } if ($line !~ /^\".*\",\".*\",\".*\",\".*\",\".*\"/) { print STDERR "\tERROR: Invalid syntax ($#L): $line\n"; next; } if (($L[1] =~ /^\@CGI/) && ($L[1] !~ /^\@CGIDIRS/)) { print STDERR "\tERROR: Possible \@CGIDIRS misspelling:$line"; } if ($line =~ /[^\\]\"\"/) { print STDERR "\tERROR: Possible double-quote syntax error:$line"; } # build entry based on all except output message $ENTRIES{"$L[0],$L[1],$L[2],$L[3],$L[5]"}++; } foreach $entry (keys %ENTRIES) { if ($ENTRIES{$entry} > 1) { print STDERR "\tERROR: Duplicate ($ENTRIES{$entry}): $entry\n"; } } $ctr=keys(%ENTRIES); print "\t$ctr entries\n"; #### user_scan_database.db if (-e $NIKTOFILES{userdbfile}) { print "--> DB Syntax ($NIKTOFILES{userdbfile})\n"; %ENTRIES=(); open(IN,"<$NIKTOFILES{userdbfile}") || die print STDERR "\tERROR: Unable to open '$NIKTOFILES{userdbfile}' for read: $@\n"; @ENTRIES=<IN>; close(IN); $ctr=0; foreach $line (@ENTRIES) { if ($line !~ /^\"/) { next; } @L=parse_csv($line); if (($#L < 5) || ($#L > 6)) { print STDERR "\tERROR: User DB: Invalid syntax ($#L): $line"; next; } if ($line !~ /^\".*\",\".*\",\".*\",\".*\",\".*\"/) { print STDERR "\tERROR: User DB: Invalid syntax ($#L): $line\n"; next; } if (($L[1] =~ /^\@CGI/) && ($L[1] !~ /^\@CGIDIRS/)) { print STDERR "\tERROR: User DB: Possible \@CGIDIRS misspelling:$line"; } if ($line =~ /[^\\]\"\"/) { print STDERR "\tERROR: User DB: Possible double-quote syntax error:$line"; } # build entry based on all except output message $ENTRIES{"$L[0],$L[1],$L[2],$L[3],$L[5]"}++; } foreach $entry (keys %ENTRIES) { if ($ENTRIES{$entry} > 1) { print STDERR "\tERROR: Duplicate ($ENTRIES{$entry}): $entry\n"; } } $ctr=keys(%ENTRIES); print "\t$ctr entries\n"; } #### outdated.db $ctr=0; print "-->\tDB Syntax ($NIKTO{plugindir}/outdated.db)\n"; %ENTRIES=(); open(IN,"<$NIKTO{plugindir}/outdated.db") || die print STDERR "\tERROR: Unable to open '$NIKTO{plugindir}/outdated.db' for read: $@\n"; @ENTRIES=<IN>; close(IN); foreach $line (@ENTRIES) { $line =~ s/^\s+//; if ($line =~ /^\#/) { next; } chomp($line); if ($line eq "") { next; } @L=parse_csv($line); if ($line !~ /^\".*\"\,\".*\"\,\".*\"$/) { print STDERR "\tERROR: Invalid syntax ($#L): $line\n"; next; } if ($#L ne 2) { print STDERR "\tERROR: Invalid syntax ($#L): $line\n"; next; } $ENTRIES{"$L[0]"}++; } foreach $entry (keys %ENTRIES) { if ($ENTRIES{$entry} > 1) { print STDERR "\tERROR: Duplicate ($ENTRIES{$entry}): $entry\n"; } } $ctr=keys(%ENTRIES); print "\t$ctr entries\n"; #### server_msgs.db $ctr=0; print "-->\tDB Syntax ($NIKTO{plugindir}/server_messages.db)\n"; %ENTRIES=(); open(IN,"<$NIKTO{plugindir}/server_msgs.db") || die print STDERR "\tERROR: Unable to open '$NIKTO{plugindir}/server_msgs.db' for read: $@\n"; @ENTRIES=<IN>; close(IN); foreach $line (@ENTRIES) { $line =~ s/^\s+//; if ($line =~ /^\#/) { next; } chomp($line); if ($line eq "") { next; } @L=parse_csv($line); if ($line !~ /^\".*\"\,\".*\"$/) { print STDERR "\tERROR: Invalid syntax ($#L): $line\n"; next; } if ($#L ne 1) { print STDERR "\tERROR: Invalid syntax ($#L): $line\n"; next; } # test regex "test" =~ /$L[0]/; $ENTRIES{"$L[0]"}++; } foreach $entry (keys %ENTRIES) { if ($ENTRIES{$entry} > 1) { print STDERR "\tERROR: Duplicate ($ENTRIES{$entry}): $entry\n"; } } $ctr=keys(%ENTRIES); print "\t$ctr entries\n"; #### check that all plugins are in nikto_plugin_order.txt print "-->\tPlugin order ($NIKTO{plugindir}/nikto_plugin_order.txt)\n"; my @NIKTOFILES=dirlist($NIKTO{plugindir},"(\.plugin\$)"); my %PLUGS; foreach my $pluginf (@NIKTOFILES) { chomp($pluginf); $pluginf =~ s/\#.*$//; $pluginf =~ s/\..*$//; $pluginf =~ s/\s+//; if (($pluginf eq "") || ($pluginf eq "nikto_core")) { next; } $PLUGS{$pluginf}=0; } open(ORDERFILE,"<$NIKTO{plugindir}/nikto_plugin_order.txt") || die print STDERR "\tERROR: Unable to open '$NIKTO{plugindir}/nikto_plugin_order.txt' for read: $@\n"; foreach my $line (<ORDERFILE>) { chomp($line); $line =~ s/\#.*$//; $line =~ s/\s+/ /; if (($line eq "") || ($line eq " ")) { next; } $PLUGS{$line}=1; } close(ORDERFILE); my $bad=0; foreach my $p (sort keys %PLUGS) { if ($PLUGS{$p} eq 0) { $bad=1; print STDERR "\tERROR: plugin '$p' not in nikto_plugin_order.txt\n"; } } if (!$bad) { print "\tOrder file okay\n"; } #### check that all plugins are named properly print "-->\tPlugin conventions ($NIKTO{plugindir}/*.plugin)\n"; my $bad=0; foreach my $pluginf (@NIKTOFILES) { chomp($pluginf); $pluginf =~ s/\#.*$//; $pluginf =~ s/\..*$//; $pluginf =~ s/\s+//; if (($pluginf eq "") || ($pluginf eq "nikto_core")) { next; } open(IN,"<$NIKTO{plugindir}/$pluginf.plugin") || die print STDERR "\tERROR: Unable to open '$NIKTO{plugindir}/$pluginf.plugin' for read: $@\n"; my @F=<IN>; close(IN); my $CT=grep(/sub $pluginf/,@F); if ($CT < 1) { print STDERR "\tERROR: file '$pluginf\.plugin' does not have 'sub $pluginf' defined.\n"; $bad++; } } if (!$bad) { print "\tPlugin syntax okay\n"; } exit;}######################################################################## spit out all the details#######################################################################sub dump_result_hash{ if (!$OUTPUT{debug}) { return; } # quick return nprint("- Result Hash:","d"); foreach my $item (sort keys %result) { if ($item eq "whisker") { next; } nprint("- $item \t\t$result{$item}","d"); } foreach my $item (sort keys %{$result{'whisker'}}) { nprint("- \$whisker-\>$item \t$result{'whisker'}->{$item}","d"); } return;}######################################################################## spit out all the details#######################################################################sub dump_request_hash{ if (!$OUTPUT{debug}) { return; } # quick return nprint("- Request Hash:","d"); foreach my $item (sort keys %request) { if ($item eq "whisker") { next; } nprint("- $item: $request{$item}","d"); } foreach my $item (sort keys %{$request{'whisker'}}) { if ($item eq "http_eol") { next; } nprint("- \$whisker-\>$item: $request{'whisker'}->{$item}","d"); } return;}######################################################################## check_responses# check what the 200/404 messages are...#######################################################################sub check_responses{ # get NOT FOUND response (404) # check for compliant 404 message # check for common strings to use as 'not found' matches from content ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound}, $CONTENT)=fetch("$NIKTO{fingerprint}","GET"); if (($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound} eq "400") || ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound} eq "")) # may need to use HTTP/1.? { my $old=$request{'whisker'}->{'http_ver'}; if ($request{'whisker'}->{'http_ver'} eq "1.1") { $request{'whisker'}->{'http_ver'}="1.0"; } else { $request{'whisker'}->{'http_ver'}="1.1"; } nprint("- Server did not understand HTTP $old, switching to HTTP $request{'whisker'}->{'http_ver'}"); ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound}, $CONTENT)=fetch("$NIKTO{fingerprint}","GET"); } if (($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound} ne "404") && ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound} ne "401")) { nprint("+ Server does not respond with '404' for error messages (uses '$TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound}')."); nprint("+ This may increase false-positives."); if ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound} eq "302") { nprint("+ Not found files redirect to: $result{'location'}"); } elsif ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound} eq "301") { nprint("+ Not found files redirect to: $result{'location'}"); } if ($CONTENT =~ /not found/i) { $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound}="not found"; } # shorten it, content has "not found" in it elsif ($CONTENT =~ /404/i) { $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound}="404"; } # shorten it, content has "404" in it else { $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound} = $CONTENT; } } # get OK response (200) ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found}, $CONTENT)=fetch("/","GET"); if ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found} eq 404) # assume server does not actually have a / & set it to 200 { $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found}=200; nprint("+ No root document found, assuming 200 is OK response.","v"); } elsif ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found} != 200) { if (($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found} eq "302") || ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found} eq 301)) { nprint("+ The root file (/) redirects to: $result{'location'}"); # try to get redirected location to see if 200 is actually the valid response ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found}, $CONTENT)=fetch($result{'location'},"GET"); #if ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found} ne 200) # still no good... just a 302, stop going in circles # { $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found}=302; } } } if ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found} eq "401") { auth_check(); } # if they're the same, something is amiss... just pick a 404/200 scheme, nothing better to do # except 403... which will cut down false positives if ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound} eq $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found}) { if ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound} ne "401") { nprint("+ The found & not found messages appear to be the same, be skeptical of positives."); } $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound}=404 unless $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{notfound} eq 403; $TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{found}=200; }return;}######################################################################## figure out CGI directories# check_cgi#######################################################################sub check_cgi{ my ($gotvalid,$gotinvalid)=0; my @POSSIBLECGI=(); my @CFGCGI=(split(/ /,$VARIABLES{"\@CGIDIRS"})); my ($res, $possiblecgidir) =""; if ($CLI{forcecgi} eq "all") # all possible CGI dirs to be "true" { nprint("Using 'all' CGI directories\n","d"); $VARIABLES{"\@CGIDIRS"} = join(" ",@CFGCGI); } elsif ($CLI{forcecgi} eq "none") # scan no CGI directories { nprint("Using no CGI directories\n","d"); $VARIABLES{"\@CGIDIRS"} = ""; } elsif ($CLI{forcecgi} =~ /[a-zA-Z0-9]/) # scan a specific directory { nprint("Using CGI dir '$CLI{forcecgi}'\n","d"); $VARIABLES{"\@CGIDIRS"} = $CLI{forcecgi}; } else # or normal testing of each dir { foreach $possiblecgidir (@CFGCGI) { ($res, $CONTENT)=fetch($possiblecgidir,"GET"); nprint("Checked for CGI dir\t$possiblecgidir\tgot:$res","d"); if (($res eq 302) || ($res eq 200) || ($res eq 403) || ($res eq 301)) { push(@POSSIBLECGI,$possiblecgidir); $gotvalid++; } } if ($gotvalid eq 0) { nprint("+ No CGI Directories found (use '-C all' to force check all possible dirs)"); $VARIABLES{"\@CGIDIRS"} = ""; } elsif ($#CFGCGI eq $#POSSIBLECGI) { nprint("+ All CGI directories 'found', use '-C none' to test none"); $VARIABLES{"\@CGIDIRS"} = @CFGCGI; } else { $VARIABLES{"\@CGIDIRS"} = join(" ",@POSSIBLECGI); } } # end !$CLI{forcecgi} nprint("- Checking for CGI in: $VARIABLES{\"\@CGIDIRS\"}","v"); return;}######################################################################## get a page# fetch URI, METHOD#######################################################################sub fetch{ if ($_[0] eq "") { return; } $request{'whisker'}->{'uri'} = "$CLI{root}$_[0]"; # Append -root option value $request{'whisker'}->{'method'} = $_[1]; LW::http_reset(); $request{'whisker'}->{'data'}=""; if ($_[2] ne "" && $_[2] ne " ") {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?