5057682 2000-05-03  00:37  /157 rader/ Postmaster
Mottagare: Bugtraq (import) <10708>
Ärende: Linux knfsd DoS issue
------------------------------------------------------------
Approved-By: aleph1@SECURITYFOCUS.COM
Delivered-To: bugtraq@lists.securityfocus.com
Delivered-To: bugtraq@securityfocus.com
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
Message-ID:  <Pine.LNX.4.21.0005012042550.6419-100000@ferret.lmh.ox.ac.uk>
Date:         Mon, 1 May 2000 21:27:18 +0100
Reply-To: Chris Evans <chris@FERRET.LMH.OX.AC.UK>
Sender: Bugtraq List <BUGTRAQ@SECURITYFOCUS.COM>
From: Chris Evans <chris@FERRET.LMH.OX.AC.UK>
X-To:         bugtraq@securityfocus.com
To: BUGTRAQ@SECURITYFOCUS.COM

Hi,

I was going to wait until a 2.2 kernel incorporating the fix was
released, but that's taking a while, and details have already leaked
by several routes.

ISSUE
=====

A DoS condition exists in the Linux kernel knfsd server. Remote,
unauthenticated users (i.e. those with neither a directory mounted
nor permission to mount one) can OOPS the host kernel. The OOPS does
not bring down the target host, but it is possible to render the NFS
service inoperable until a reboot.

FIX
===

The fix is present in the following kernels
- 2.3.99pre7-pre1
- 2.2.15pre20      [1]
- The kernel update recently release by RedHat (AFAIK)

Fix is also included at the bottom of this post.

[1] Which, AFAIK, is only available as a patch on top of
2.2.15pre19. Please see
ftp://ftp.kernel.org/pub/linux/kernel/people/alan/pre-patch-2.2.15-19to20.bz2


DISCUSSION OF ISSUE
===================

This issue is caused by signed/unsigned issues. A size check is
performed on an "int" (i.e. signed) variable. This size check does
not check for negative values. As a consequence it is possible to
point an internal buffer pointer _way_ before the beginning of the
buffer. The kernel will try to read a memory address in limbo, and
OOPS.

The faulty code is in kernel/net/sunrpc/svcauth.c, svcauth_unix()

int slen;
...
        slen = ntohl(*bufp++);                  /* gids length */
        if (slen > 16 || (len -= slen + 2) < 0)
                goto badcred;
...
        bufp += (slen - i);


As a general note, signed/unsigned errors like the above are _almost_
as prevalent as buffer overflows. Once you start looking for
them. Re-auditing previously audited software with signed/unsigned
issues in mind often yields new problems.

Note that the userland sunrpc implementation suffered from a very
similar flaw a while back. No, it certainly wasn't a copy/paste error.

Some signed/unsigned errors can lead to exploitable conditions rather
than just DoS. The following code fragment should illustrate this:

func()
{
  char name[NAMELEN];

  int len = get_from_network();
  if (len > NAMELEN)
    goto i_dont_think_so;

  memcpy(name, get_from_network(), len); /* -1 implicitly converted to
                                            4Gb */
}

An interesting variant on this (that HAS been observed) is

func()
{
  char* bufp;
  char* bufmax = bufp + 100;    /* Or whatever */

  unsigned int len = get_from_network();
  if (bufp + len > bufmax)
    goto i_dont_think_so;
  ...
  bufp += len;
}

On machines with a small 4Gb address space, e.g. Intel x86, a
suitable large value of len will wrap bufp around the address space,
leading to a position outside of the buffer boundary, but satisfying
the "bufp + len <= bufmax" constraint.

Programs which have in their history been found to include
signed/unsigned issues include
- knfsd
- sunrpc part of glibc
- xfs
- libORBIT
- issues at syscall API of various kernels
- issues in networking stacks

PATCH TO FIX
============

--- net/sunrpc/svcauth.c.old	Tue Apr 18 05:13:47 2000
+++ net/sunrpc/svcauth.c	Tue Apr 18 06:36:20 2000
@@ -4,6 +4,9 @@
  * The generic interface for RPC authentication on the server side.
  *
  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ *
+ * CHANGES
+ * 19-Apr-2000 Chris Evans      - Security fix
  */

 #include <linux/types.h>
@@ -117,7 +120,8 @@
 	struct svc_buf	*resp = &rqstp->rq_resbuf;
 	struct svc_cred	*cred = &rqstp->rq_cred;
 	u32		*bufp = argp->buf;
-	int		len   = argp->len, slen, i;
+	int		len   = argp->len;
+	u32		slen, i;

 	if ((len -= 3) < 0) {
 		*statp = rpc_garbage_args;
@@ -127,7 +131,7 @@
 	bufp++;					/* length */
 	bufp++;					/* time stamp */
 	slen = (ntohl(*bufp++) + 3) >> 2;	/* machname length */
-	if (slen > 64 || (len -= slen) < 0)
+	if (slen > 64 || (len -= slen + 3) < 0)
 		goto badcred;
 	bufp += slen;				/* skip machname */


EXPLOIT
=======

Not today! In fact, don't bother. DoS is very passe... very 90's :-)


Cheers
Chris
(5057682) ------------------------------------------(Ombruten)