Summary: you can exploit a single-byte buffer overrun to gain root privs. When, half a day after releasing version 2.2beta37 of the Linux nfs server, I received a message from Larry Doolittle telling me that it was still vulnerable to the root exploit posted to bugtraq, I was ready to quit hacking and start as a carpenter... Tempting as that was, I didn't, and started looking for the bug instead. It turned out that an ancient version of libc was at fault (libc-5.3.x), which has a buffer overrun in realpath(). So, to make sure your mountd is safe, upgrade your libc to a recent version. As far as I can tell, this particular overrun was fixed in libc-4.4 (but see below). The interesting thing about this overrun is that it was by just a single byte. And yes, it not just crashed the process, it provided a root shell. It took me a while to figure out, but what it boils down to is this: At the beginning of the function, realpath copies the argument (1024 bytes) To a local buffer (sized MAXPATHLEN, i.e. 1024 bytes). Thus, the terminating 0 byte of the string gets scribbled over the next byte, which happens to be the lowest byte of %ebp, the frame pointer of the calling function. At function entry, its value was 0xbffff3ec. After the strcpy, it becomes 0xbffff300. During the remainder of realpath(), nothing exciting happens, but when the function returns, %ebp is restored from stack, which effectively shifts down the calling function's stack frame by 0xec bytes. The calling function now does a few things with local data, dereferences some pointers (by sheer dumb luck these pointers contain random but valid addresses), and returns, restoring the %esp and %ebp registers from stack. With the stack having shifted down 0xec bytes, it picks up the return address from the local buffer containing the exploit code... As a side note, the buffer overrun could be modified easily to work on systems using non-execute stacks, because the RPC arguments are decoded into a static buffer. Making the exploit point the fake return address at the static buffer instead of the stack easily defeats the no-exec stack. Also note that even the most recent libc5 contains another buffer overrun in realpath. It is not deadly to mountd unless you start it from a directory whose path name is longer than 40-something bytes. A patch to libc is appended, and I will release an nfs-server update that checks for this bug and replaces the faulty realpath function soon. Cheers Olaf ------------------------------------------------------------------ Concerning the buffer overflow in realpath: The appended patch illustrates the problem. To trigger the overflow, try void main(void) /* hullo Dan Popp:-) */ { char buffer[1024], result[1024]; memset(buffer, 'A', 1021); buffer[1021] = '\0'; chdir("/tmp"); realpath(buffer, result); printf("length = %d\n", strlen(result)); } Glibc uses a different realpath implementation which does not have this bug. --- libc-5.4.38/libc/bsd/realpath.c.orig Sat Oct 3 00:42:48 1998 +++ libc-5.4.38/libc/bsd/realpath.c Sat Oct 3 00:43:09 1998 @@ -76,7 +76,7 @@ } strcpy(copy_path, path); path = copy_path; - max_path = copy_path + PATH_MAX - 2; + max_path = resolved_path + PATH_MAX - 2; /* If it's a relative pathname use getwd for starters. */ if (*path != '/') { /* Ohoo... */ @@ -122,7 +122,7 @@ } /* Safely copy the next pathname component. */ while (*path != '\0' && *path != '/') { - if (path > max_path) { + if (new_path > max_path) { errno = ENAMETOOLONG; return NULL; } ------------------------------------------------------------------ -- Olaf Kirch | --- o --- Nous sommes du soleil we love when we play okir@monad.swb.de | / | \ sol.dhoop.naytheet.ah kin.ir.samse.qurax okir@caldera.de +-------------------- Why Not?! ----------------------- UNIX, n.: Spanish manufacturer of fire extinguishers.