[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Wheezy update of sudo available



Hi bdale & LTS folks,

It seems the last upload of sudo only partly fixed CVE-2017-1000367 and
wheezy (and jessie) are therefore still vulnerable to information
disclosure and command execution:

https://security-tracker.debian.org/tracker/CVE-2017-1000368

(I was surprised to find that the vulnerability was added to the LTS
queue but maintainers weren't notified when this was triaged in early
june... Maybe it was because it was marked as "which might well be fixed
once more issues piled up" back then.)

Anyways, I therefore started working on an update without the maintainer
being notified, so apologies on that. I have backported the upstream
patch to wheezy (attached) and uploaded test packages to:

https://people.debian.org/~anarcat/debian/wheezy-lts/
https://people.debian.org/~anarcat/debian/wheezy-lts/sudo_1.8.5p2-1+nmu3+deb7u4_amd64.changes

Let me know how we should handle this from here.

A.
diff -Nru sudo-1.8.5p2/debian/changelog sudo-1.8.5p2/debian/changelog
--- sudo-1.8.5p2/debian/changelog	2017-05-30 17:20:15.000000000 -0400
+++ sudo-1.8.5p2/debian/changelog	2017-06-29 15:50:30.000000000 -0400
@@ -1,3 +1,13 @@
+sudo (1.8.5p2-1+nmu3+deb7u4) UNRELEASED; urgency=high
+
+  * Non-maintainer upload by the LTS Security Team.
+  * CVE-2017-1000368: Todd Miller's sudo version 1.8.20p1 and earlier is
+    vulnerable to an input validation (embedded newlines) in the
+    get_process_ttyname() function resulting in information disclosure and
+    command execution.
+
+ -- Antoine Beaupré <anarcat@debian.org>  Thu, 29 Jun 2017 15:50:30 -0400
+
 sudo (1.8.5p2-1+nmu3+deb7u3) wheezy-security; urgency=high
 
   * Non-maintainer upload
diff -Nru sudo-1.8.5p2/debian/patches/CVE-2017-1000368-15a46f4007dd.patch sudo-1.8.5p2/debian/patches/CVE-2017-1000368-15a46f4007dd.patch
--- sudo-1.8.5p2/debian/patches/CVE-2017-1000368-15a46f4007dd.patch	1969-12-31 19:00:00.000000000 -0500
+++ sudo-1.8.5p2/debian/patches/CVE-2017-1000368-15a46f4007dd.patch	2017-06-29 15:50:30.000000000 -0400
@@ -0,0 +1,78 @@
+
+# HG changeset patch
+# User Todd C. Miller <Todd.Miller@courtesan.com>
+# Date 1496243671 21600
+# Node ID 15a46f4007dde8e819dd2c70e670a529bbb9d312
+# Parent  6f3d9816541ba84055ae5aec6ff9d9523c2a96f3
+A command name may also contain newline characters so read
+/proc/self/stat until EOF.  It is not legal for /proc/self/stat to
+contain embedded NUL bytes so treat the file as corrupt if we see
+any.  With help from Qualys.
+
+This is not exploitable due to the /dev traversal changes in sudo
+1.8.20p1 (thanks Solar!).
+
+--- a/src/ttyname.c
++++ b/src/ttyname.c
+@@ -419,29 +419,39 @@ get_process_ttyname(void)
+ char *
+ get_process_ttyname(void)
+ {
+-    char *line = NULL, *tty = NULL;
+-    size_t linesize = 0;
+-    ssize_t len;
++    char *tty = NULL;
++    char *cp, buf[1024];
++    ssize_t nread;
+     int i;
+     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
+ 
+     /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */
+     for (i = 0; tty == NULL && i < 2; i++) {
+-	FILE *fp;
++        int fd;
+ 	char path[PATH_MAX];
+ 	(void)snprintf(path, sizeof(path), "/proc/%u/stat",
+ 	    i ? (unsigned int)getppid() : (unsigned int)getpid());
+-	if ((fp = fopen(path, "r")) == NULL)
++	if ((fd = open(path, O_RDONLY | O_NOFOLLOW)) == -1)
+ 	    continue;
+-	len = getline(&line, &linesize, fp);
+-	fclose(fp);
+-	if (len != -1) {
++	cp = buf;
++	while ((nread = read(fd, cp, buf + sizeof(buf) - cp)) != 0) {
++	    if (nread == -1) {
++		if (errno == EAGAIN || errno == EINTR)
++		    continue;
++		break;
++	    }
++	    cp += nread;
++	    if (cp >= buf + sizeof(buf))
++		break;
++	}
++	if (nread == 0 && memchr(buf, '\0', cp - buf) == NULL) {
+ 	    /*
+ 	     * Field 7 is the tty dev (0 if no tty).
+-	     * Since the process name at field 2 "(comm)" may include spaces,
+-	     * start at the last ')' found.
++	     * Since the process name at field 2 "(comm)" may include
++	     * whitespace (including newlines), start at the last ')' found.
+ 	     */
+-	    char *cp = strrchr(line, ')');
++            *cp = '\0';
++            cp = strrchr(buf, ')');
+ 	    if (cp != NULL) {
+ 		char *ep = cp;
+ 		int field = 1;
+@@ -460,8 +470,9 @@ get_process_ttyname(void)
+ 		}
+ 	    }
+ 	}
++        if (fd != -1)
++          close(fd);
+     }
+-    efree(line);
+ 
+     /* If all else fails, fall back on ttyname(). */
+     if (tty == NULL) {
diff -Nru sudo-1.8.5p2/debian/patches/CVE-2017-1000368-b5460cbbb11b.patch sudo-1.8.5p2/debian/patches/CVE-2017-1000368-b5460cbbb11b.patch
--- sudo-1.8.5p2/debian/patches/CVE-2017-1000368-b5460cbbb11b.patch	1969-12-31 19:00:00.000000000 -0500
+++ sudo-1.8.5p2/debian/patches/CVE-2017-1000368-b5460cbbb11b.patch	2017-06-29 15:31:22.000000000 -0400
@@ -0,0 +1,262 @@
+
+# HG changeset patch
+# User Todd C. Miller <Todd.Miller@courtesan.com>
+# Date 1496089973 21600
+# Node ID b5460cbbb11bbf9d92ffcc6798a686cf4125efd3
+# Parent  c303e6eecc7841e2f891d70613e80fcf27fa6e86
+Fix for CVE-2017-1000367, parsing of /proc/pid/stat on Linux when
+the process name contains spaces.  Since the user has control over
+the command name this could be used by a user with sudo access to
+overwrite an arbitrary file.
+Thanks to Qualys for investigating and reporting this bug.
+
+Also stop performing a breadth-first traversal of /dev when looking
+for the device.  Only the directories specified in search_devs[]
+are checked.
+
+diff -r c303e6eecc78 -r b5460cbbb11b src/ttyname.c
+--- a/src/ttyname.c	Tue May 23 13:26:54 2017 -0600
++++ b/src/ttyname.c	Mon May 29 14:32:53 2017 -0600
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (c) 2012-2016 Todd C. Miller <Todd.Miller@courtesan.com>
++ * Copyright (c) 2012-2017 Todd C. Miller <Todd.Miller@courtesan.com>
+  *
+  * Permission to use, copy, modify, and distribute this software for any
+  * purpose with or without fee is hereby granted, provided that the above
+@@ -145,20 +145,22 @@
+ }
+ #elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV) || defined(HAVE_PSTAT_GETPROC) || defined(__linux__)
+ /*
+- * Devices to search before doing a breadth-first scan.
++ * Device nodes and directories to search before searching all of /dev
+  */
+ static char *search_devs[] = {
+     "/dev/console",
+-    "/dev/wscons",
+-    "/dev/pts/",
+-    "/dev/vt/",
+-    "/dev/term/",
+-    "/dev/zcons/",
++    "/dev/pts/",	/* POSIX pty */
++    "/dev/vt/",		/* Solaris virtual console */
++    "/dev/term/",	/* Solaris serial ports */
++    "/dev/zcons/",	/* Solaris zone console */
++    "/dev/pty/",	/* HP-UX old-style pty */
+     NULL
+ };
+ 
++/*
++ * Device nodes to ignore when searching all of /dev
++ */
+ static char *ignore_devs[] = {
+-    "/dev/fd/",
+     "/dev/stdin",
+     "/dev/stdout",
+     "/dev/stderr",
+@@ -166,16 +168,18 @@
+ };
+ 
+ /*
+- * Do a breadth-first scan of dir looking for the specified device.
++ * Do a scan of a directory looking for the specified device.
++ * Does not descend into subdirectories.
+  * Returns name on success and NULL on failure, setting errno.
+  */
+ static char *
+-sudo_ttyname_scan(const char *dir, dev_t rdev, bool builtin, char *name, size_t namelen)
++sudo_ttyname_scan(const char *dir, dev_t rdev, char *name, size_t namelen)
+ {
+-    size_t sdlen, num_subdirs = 0, max_subdirs = 0;
+-    char pathbuf[PATH_MAX], **subdirs = NULL;
++    size_t sdlen;
++    char pathbuf[PATH_MAX];
+     char *ret = NULL;
+     struct dirent *dp;
++    struct stat sb;
+     unsigned int i;
+     DIR *d = NULL;
+     debug_decl(sudo_ttyname_scan, SUDO_DEBUG_UTIL)
+@@ -187,6 +191,18 @@
+     if ((d = opendir(dir)) == NULL)
+ 	goto done;
+ 
++    if (fstat(dirfd(d), &sb) == -1) {
++	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
++	    "unable to fstat %s", dir);
++	goto done;
++    }
++    if ((sb.st_mode & S_IWOTH) != 0) {
++	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
++	    "ignoring world-writable directory %s", dir);
++	errno = ENOENT;
++	goto done;
++    }
++
+     sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ 	"scanning for dev %u in %s", (unsigned int)rdev, dir);
+ 
+@@ -224,18 +240,6 @@
+ 	}
+ 	if (ignore_devs[i] != NULL)
+ 	    continue;
+-	if (!builtin) {
+-	    /* Skip entries in search_devs; we already checked them. */
+-	    for (i = 0; search_devs[i] != NULL; i++) {
+-		len = strlen(search_devs[i]);
+-		if (search_devs[i][len - 1] == '/')
+-		    len--;
+-		if (d_len == len && strncmp(pathbuf, search_devs[i], len) == 0)
+-		    break;
+-	    }
+-	    if (search_devs[i] != NULL)
+-		continue;
+-	}
+ # if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DTTOIF)
+ 	/*
+ 	 * Avoid excessive stat() calls by checking dp->d_type.
+@@ -248,39 +252,14 @@
+ 		if (stat(pathbuf, &sb) == -1)
+ 		    continue;
+ 		break;
+-	    case DT_DIR:
+-		/* Directory, no need to stat() it. */
+-		sb.st_mode = DTTOIF(dp->d_type);
+-		sb.st_rdev = 0;		/* quiet ccc-analyzer false positive */
+-		break;
+ 	    default:
+-		/* Not a character device, link or directory, skip it. */
++		/* Not a character device or link, skip it. */
+ 		continue;
+ 	}
+ # else
+ 	if (stat(pathbuf, &sb) == -1)
+ 	    continue;
+ # endif
+-	if (S_ISDIR(sb.st_mode)) {
+-	    if (!builtin) {
+-		/* Add to list of subdirs to search. */
+-		if (num_subdirs + 1 > max_subdirs) {
+-		    char **new_subdirs;
+-
+-		    new_subdirs = reallocarray(subdirs, max_subdirs + 64,
+-			sizeof(char *));
+-		    if (new_subdirs == NULL)
+-			goto done;
+-		    subdirs = new_subdirs;
+-		    max_subdirs += 64;
+-		}
+-		subdirs[num_subdirs] = strdup(pathbuf);
+-		if (subdirs[num_subdirs] == NULL)
+-		    goto done;
+-		num_subdirs++;
+-	    }
+-	    continue;
+-	}
+ 	if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
+ 	    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ 		"resolved dev %u as %s", (unsigned int)rdev, pathbuf);
+@@ -296,16 +275,9 @@
+ 	}
+     }
+ 
+-    /* Search subdirs if we didn't find it in the root level. */
+-    for (i = 0; ret == NULL && i < num_subdirs; i++)
+-	ret = sudo_ttyname_scan(subdirs[i], rdev, false, name, namelen);
+-
+ done:
+     if (d != NULL)
+ 	closedir(d);
+-    for (i = 0; i < num_subdirs; i++)
+-	free(subdirs[i]);
+-    free(subdirs);
+     debug_return_str(ret);
+ }
+ 
+@@ -324,7 +296,7 @@
+     debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
+ 
+     /*
+-     * First check search_devs for common tty devices.
++     * First check search_devs[] for common tty devices.
+      */
+     for (sd = search_devs; (devname = *sd) != NULL; sd++) {
+ 	len = strlen(devname);
+@@ -349,7 +321,7 @@
+ 		    "comparing dev %u to %s: no", (unsigned int)rdev, buf);
+ 	    } else {
+ 		/* Traverse directory */
+-		ret = sudo_ttyname_scan(devname, rdev, true, name, namelen);
++		ret = sudo_ttyname_scan(devname, rdev, name, namelen);
+ 		if (ret != NULL || errno == ENOMEM)
+ 		    goto done;
+ 	    }
+@@ -367,9 +339,9 @@
+     }
+ 
+     /*
+-     * Not found?  Do a breadth-first traversal of /dev/.
++     * Not found?  Check all device nodes in /dev.
+      */
+-    ret = sudo_ttyname_scan(_PATH_DEV, rdev, false, name, namelen);
++    ret = sudo_ttyname_scan(_PATH_DEV, rdev, name, namelen);
+ 
+ done:
+     debug_return_str(ret);
+@@ -493,28 +465,35 @@
+ 	len = getline(&line, &linesize, fp);
+ 	fclose(fp);
+ 	if (len != -1) {
+-	    /* Field 7 is the tty dev (0 if no tty) */
+-	    char *cp = line;
+-	    char *ep = line;
+-	    const char *errstr;
+-	    int field = 0;
+-	    while (*++ep != '\0') {
+-		if (*ep == ' ') {
+-		    *ep = '\0';
+-		    if (++field == 7) {
+-			dev_t tdev = strtonum(cp, INT_MIN, INT_MAX, &errstr);
+-			if (errstr) {
+-			    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+-				"%s: tty device %s: %s", path, cp, errstr);
++	    /*
++	     * Field 7 is the tty dev (0 if no tty).
++	     * Since the process name at field 2 "(comm)" may include spaces,
++	     * start at the last ')' found.
++	     */
++	    char *cp = strrchr(line, ')');
++	    if (cp != NULL) {
++		char *ep = cp;
++		const char *errstr;
++		int field = 1;
++
++		while (*++ep != '\0') {
++		    if (*ep == ' ') {
++			*ep = '\0';
++			if (++field == 7) {
++			    dev_t tdev = strtonum(cp, INT_MIN, INT_MAX, &errstr);
++			    if (errstr) {
++				sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
++				    "%s: tty device %s: %s", path, cp, errstr);
++			    }
++			    if (tdev > 0) {
++				errno = serrno;
++				ret = sudo_ttyname_dev(tdev, name, namelen);
++				goto done;
++			    }
++			    break;
+ 			}
+-			if (tdev > 0) {
+-			    errno = serrno;
+-			    ret = sudo_ttyname_dev(tdev, name, namelen);
+-			    goto done;
+-			}
+-			break;
++			cp = ep + 1;
+ 		    }
+-		    cp = ep + 1;
+ 		}
+ 	    }
+ 	}
+
diff -Nru sudo-1.8.5p2/debian/patches/series sudo-1.8.5p2/debian/patches/series
--- sudo-1.8.5p2/debian/patches/series	2017-05-30 17:20:15.000000000 -0400
+++ sudo-1.8.5p2/debian/patches/series	2017-06-29 15:35:53.000000000 -0400
@@ -21,3 +21,4 @@
 use_ldl_for_sudo_noexec.so.patch
 ttyname-fixes.patch
 CVE-2017-1000367.patch
+CVE-2017-1000368-15a46f4007dd.patch

Attachment: signature.asc
Description: PGP signature


Reply to: