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 + -
显示快捷键?