source: trunk/plugins/nikto_headers.plugin @ 284

Revision 284, 8.3 KB checked in by sullo, 3 years ago (diff)

Fix perl warnings

  • Property svn:keywords set to Id
RevLine 
[215]1#VERSION,2.05
[41]2# $Id$
[26]3###############################################################################
4#  Copyright (C) 2007 CIRT, Inc.
5#
6#  This program is free software; you can redistribute it and/or
7#  modify it under the terms of the GNU General Public License
8#  as published by the Free Software Foundation; version 2
9#  of the License only.
10#
11#  This program is distributed in the hope that it will be useful,
12#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14#  GNU General Public License for more details.
15#
16#  You should have received a copy of the GNU General Public License
17#  along with this program; if not, write to the Free Software
18#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19###############################################################################
[240]20# PURPOSE:
[26]21# General HTTP headers checks
22###############################################################################
[79]23sub nikto_headers_init
24{
25   my $id =
26   {
27      name         => "headers",
28      full_name    => "HTTP Headers",
29      author       => "Sullo",
30      description  => "Performs various checks against the headers returned from a HTTP request.",
31      scan_method  => \&nikto_headers,
32      copyright    => "2008 CIRT Inc."
33   };
34   return $id;
35}
[26]36
37sub nikto_headers
38{
[130]39   my ($mark)=@_;
[219]40   my $dbarray = init_db("db_headers");
[173]41   my @interesting_headers = qw /microsoftofficewebserver ms-author-via dasl dav daap-server x-aspnet-version/;
[151]42   my %headers;
[73]43   # Standard headers, whisker is added to stop false positives
44   # Host Pragma
[26]45
[73]46   #######################################################################
47   # look for a powered-by header...could require a valid file, maybe not
48   my %xpb;
49   foreach my $f (qw/\/index.php \/junk999.php \/ \/index.php3 \/ \/junk999.php3 \/index.cfm \/junk999.cfm \/index.asp \/junk999.asp \/index.aspx \/junk988.aspx/ )
50   {
[158]51      (my $res, $content) = nfetch($mark,$f, "GET", "", \%headers);
[284]52      if (defined $headers{'x-powered-by'}) { $xpb{ $headers{'x-powered-by'} } = 1; }
[73]53   }
[26]54
[73]55   foreach my $x (sort keys %xpb)
56   {
57      my $message;
[26]58
[73]59      # push version to BUILDITEMS so it can be evaluated later
60      push(@BUILDITEMS, $x);
[26]61
[73]62      $message .= " $x";
[130]63      add_vulnerability($mark,"Retrieved X-Powered-By header:$message", 999992, 0);
[73]64   }
[158]65   
66   #######################################################################
67   # look for to see whether its vulnerable to the Translate: f
68   my %transheaders;
69   foreach my $f (qw/\/index.asp \/junk999.asp \/index.aspx \/junk988.aspx \/login.asp \/login.aspx/ )
70   {
71      (my $res, $content) = nfetch($mark, $f , "GET", "", \%transheaders);
72      if ($res eq "200")
73      {
[284]74         $transheaders{'Translate'}="f";
[158]75         ($res, $content) = nfetch($mark,$f . "\\", "GET", "", \%transheaders);
76         if ($res eq "200")
77         {
78            if ($content =~ /<asp:/ || $content =~ /<\/asp:/)
79            {
[214]80               add_vulnerability($mark, "Host may be vulnerable to a source disclosure using the Translate: header", 999983, 390, "GET", $f, $content);
[158]81               last;
82            }
83         }
84      }
85   }
[26]86
[73]87   #######################################################################
88   # Servlet-Engine info
[284]89   if (defined $headers{'servlet-engine'})
[73]90   {
[284]91      my $x = $headers{'servlet-engine'};
[73]92      $x = ~s/\(.*$//;
93      $x =~ s/\s+//g;
94      push(@BUILDITEMS, $x);
[26]95
[130]96      add_vulnerability($mark, "Retrieved servlet-engine headers: $x",999991,0);
[26]97
[73]98      my @bits = split(/;/, $x);
99      foreach my $bit (@bits)
100      {
101         push(@BUILDITEMS, $bit);
102      }
103   }
[26]104
[73]105   #######################################################################
106   # Content-Location header in IIS
107   LW2::http_close(\%request);    # force-close any old connections
108   LW2::http_fixup_request(\%request);
109   LW2::http_reset();
110   my $wh = $request{'whisker'}{'Host'};
111   my $h  = $request{'Host'};
112   delete $request{'whisker'}{'Host'};
113   delete $request{'Host'};
114   $request{'whisker'}->{'uri'}    = "/";
115   $request{'whisker'}->{'method'} = "GET";
116   $request{'whisker'}{'version'}  = "1.0";
117   
118   LW2::http_do_request_timeout(\%request, \%result);
[269]119   $NIKTO{'totalrequests'}++;
[73]120   if (   ($result{'content-location'} ne "")
121      && ($result{'content-location'} =~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/)
[130]122      && ($result{'content-location'} !~ /$mark->{ip}/))
[73]123   {
[130]124      add_vulnerability($mark,"IIS may reveal its internal IP in the Content-Location header via a request to the root file. The value is \"$result{'content-location'}\".", 999989, 630);
[73]125   }
126   
127   LW2::http_close(\%request);    # force-close any old connections
128   LW2::http_fixup_request(\%request);
129   LW2::http_reset();
130   $request{'whisker'}->{'uri'}    = "/images";
131   $request{'whisker'}->{'method'} = "GET";
132   $request{'whisker'}{'version'}  = "1.0";
133   delete $request{'whisker'}{'Host'};
134   delete $request{'Host'};
[268]135   if ($CLI{'pause'} > 0) { sleep $CLI{'pause'}; }
[73]136   LW2::http_do_request_timeout(\%request, \%result);
[269]137   $NIKTO{'totalrequests'}++;
[26]138
[73]139   if (($result{'location'} ne "")
140      && ($result{'location'} =~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/)
[130]141      && ($result{'location'} !~ /$mark->{ip}/))
[73]142   {
[214]143      add_vulnerability($mark,"IIS may reveal its internal IP in the Location header via a request to the /images directory. The value is \"$result{'location'}\".",999988,630,"GET","/images");
[73]144   }
[26]145
[73]146   $request{'whisker'}{'Host'} = $wh;
147   $request{'Host'} = $h;
[26]148
[73]149   #######################################################################
150   # Location header in WebLogic
151   LW2::http_close(\%request);    # force-close any old connections
152   LW2::http_fixup_request(\%request);
153   LW2::http_reset();
154   $request{'whisker'}->{'uri'}    = ".";
155   $request{'whisker'}->{'method'} = "GET";
156   $request{'whisker'}{'version'}  = "1.0";
[268]157   if ($CLI{'pause'} > 0) { sleep $CLI{'pause'}; }
[73]158   LW2::http_do_request_timeout(\%request, \%result);
[269]159   $NIKTO{'totalrequests'}++;
[26]160
[73]161   if (($result{'location'} ne "") && ($result{'location'} =~ /http:\/\//))
162   {
[214]163      add_vulnerability($mark,"WebLogic may reveal its internal IP or hostname in the Location header. The value is \"$result{'location'}\".",999987,5737,"GET",".");
[73]164   }
[26]165
[73]166   #######################################################################
167   # All other interesting headers
[139]168   
169   # First let's hit something we know should return something
[152]170   my ($res, $content)=nfetch($mark,"/","GET","",\%headers);
[139]171
[73]172   foreach my $header (@interesting_headers)
173   {
[151]174      if ($headers{$header} ne '')
[73]175      {
[151]176         my $x = $headers{$header};
[73]177         $x =~ s/\s+.*$//;
178         push(@BUILDITEMS, $x);
[151]179         add_vulnerability($mark,"Retrieved $header header: $headers{$header}",999986,0);
[73]180      }
181   }
[26]182
[73]183   #######################################################################
[194]184   # Look for any uncommon headers
[151]185   foreach my $header (sort keys %headers)
[73]186   {
187      my $found = 0;
188      my $reportnum = 999100;
[139]189      foreach my $st_header (@$dbarray)
[73]190      {
[139]191         if ($header eq $st_header->{header})
[73]192         {
193            $found=1;
194         }
195      }
196      if ($found == 0)
197      {
[151]198         my $x = $headers{$header};
[73]199         $x =~s/\s+.*$//;
200         push(@BUILDITEMS, $x);
[194]201         add_vulnerability($mark,"Uncommon header '$header' found, with contents: $headers{$header}",$reportnum,0);
[73]202         $reportnum++;
203      }
204   }
[56]205
[73]206   #######################################################################
207   # ETag header
208   
209   # Try to grab a standard file
210   foreach my $f (qw/\/index.html \/index.htm \/robots.txt/)
211   {
[158]212      (my $res, $content) = nfetch($mark,$f, "GET","", \%headers);
[215]213      last if (defined $headers{'etag'});
[73]214   }
[151]215   
[73]216   # Now we have a header, let's check ETag for inode
[284]217   if (defined $headers{'etag'})
[73]218   {
[284]219      my $etag=$headers{'etag'};
[73]220      $etag =~ s/"//g;
221      my @fields = split("-",$etag);
222      my $message = "ETag header found on server";
223      if ($#fields == 2)
224      {
225         my $inode="0x$fields[0]";
226         my $size="0x$fields[1]";
227         my $mtime="0x$fields[2]";
228         # for some reason $mtime is mangled
229         $message .= sprintf(", inode: %d, size: %d, mtime: %s", hex($inode), hex($size),  $mtime);
230      }
231      else
232      {
233         $message .= ", fields: ";
234         foreach my $field (@fields)
235         {
236            $message .= "0x$field ";
237         }
238      }
[130]239      add_vulnerability($mark, $message, 999984, 0);
[73]240   }
[26]241}
242
2431;
Note: See TracBrowser for help on using the repository browser.