On Tue, Jul 24, 2018 at 01:12:50PM +0200, Salvatore Bonaccorso wrote:
> I have prepared an update for stretch (not yet released), although as
> said, its a problem only with active SELinux, which is not by default
> in Debian.

Attaching debdiff.

Regards,
Salvatore
diff -Nru fuse-2.9.7/debian/changelog fuse-2.9.7/debian/changelog
--- fuse-2.9.7/debian/changelog 2016-06-23 20:54:56.000000000 +0200
+++ fuse-2.9.7/debian/changelog 2018-07-23 20:50:41.000000000 +0200
@@ -1,3 +1,11 @@
+fuse (2.9.7-1+deb9u1) stretch-security; urgency=high
+
+  * Non-maintainer upload by the Security Team.
+  * Restriction bypass of the "allow_other" option when SELinux is active
+    (CVE-2018-10906)
+
+ -- Salvatore Bonaccorso <[email protected]>  Mon, 23 Jul 2018 20:50:41 +0200
+
 fuse (2.9.7-1) unstable; urgency=low
 
   * New upstream release.
diff -Nru 
fuse-2.9.7/debian/patches/CVE-2018-10906/0001-fusermount-prevent-silent-truncation-of-mount-option.patch
 
fuse-2.9.7/debian/patches/CVE-2018-10906/0001-fusermount-prevent-silent-truncation-of-mount-option.patch
--- 
fuse-2.9.7/debian/patches/CVE-2018-10906/0001-fusermount-prevent-silent-truncation-of-mount-option.patch
    1970-01-01 01:00:00.000000000 +0100
+++ 
fuse-2.9.7/debian/patches/CVE-2018-10906/0001-fusermount-prevent-silent-truncation-of-mount-option.patch
    2018-07-23 20:50:41.000000000 +0200
@@ -0,0 +1,98 @@
+From: Jann Horn <[email protected]>
+Date: Fri, 13 Jul 2018 14:51:17 -0700
+Subject: [1/5] fusermount: prevent silent truncation of mount options
+Origin: 
https://github.com/libfuse/commit/34c62ee90c69b07998629f6b5a06ab0120be681c
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2018-10906
+Bug: https://github.com/libfuse/libfuse/pull/268
+
+Currently, in the kernel, copy_mount_options() copies in one page of
+userspace memory (or less if some of that memory area is not mapped).
+do_mount() then writes a null byte to the last byte of the copied page.
+This means that mount option strings longer than PAGE_SIZE-1 bytes get
+truncated silently.
+
+Therefore, this can happen:
+
+user@d9-ut:~$ _FUSE_COMMFD=10000 fusermount -o "$(perl -e 'print ","x4000')" 
mount
+sending file descriptor: Bad file descriptor
+user@d9-ut:~$ grep /mount /proc/mounts
+/dev/fuse /home/user/mount fuse 
rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0
+user@d9-ut:~$ fusermount -u mount
+user@d9-ut:~$ _FUSE_COMMFD=10000 fusermount -o "$(perl -e 'print ","x4050')" 
mount
+sending file descriptor: Bad file descriptor
+user@d9-ut:~$ grep /mount /proc/mounts
+/dev/fuse /home/user/mount fuse 
rw,nosuid,nodev,relatime,user_id=1000,group_id=100 0 0
+user@d9-ut:~$ fusermount -u mount
+user@d9-ut:~$ _FUSE_COMMFD=10000 fusermount -o "$(perl -e 'print ","x4051')" 
mount
+sending file descriptor: Bad file descriptor
+user@d9-ut:~$ grep /mount /proc/mounts
+/dev/fuse /home/user/mount fuse 
rw,nosuid,nodev,relatime,user_id=1000,group_id=10 0 0
+user@d9-ut:~$ fusermount -u mount
+user@d9-ut:~$ _FUSE_COMMFD=10000 fusermount -o "$(perl -e 'print ","x4052')" 
mount
+sending file descriptor: Bad file descriptor
+user@d9-ut:~$ grep /mount /proc/mounts
+/dev/fuse /home/user/mount fuse 
rw,nosuid,nodev,relatime,user_id=1000,group_id=1 0 0
+user@d9-ut:~$ fusermount -u mount
+
+I'm not aware of any context in which this is actually exploitable - you'd
+still need the UIDs to fit, and you can't do it if the three GIDs of the
+process don't match (in the case of a typical setgid binary), but it does
+look like something that should be fixed.
+
+I also plan to try to get this fixed on the kernel side.
+[carnil: Refresh patch to apply cleanly for context in 2.9.7]
+---
+ util/fusermount.c | 23 ++++++++++++++++++++---
+ 1 file changed, 20 insertions(+), 3 deletions(-)
+
+--- a/util/fusermount.c
++++ b/util/fusermount.c
+@@ -712,6 +712,23 @@ static int get_string_opt(const char *s,
+       return 1;
+ }
+ 
++/* The kernel silently truncates the "data" argument to PAGE_SIZE-1 
characters.
++ * This can be dangerous if it e.g. truncates the option "group_id=1000" to
++ * "group_id=1".
++ * This wrapper detects this case and bails out with an error.
++ */
++static int mount_notrunc(const char *source, const char *target,
++                       const char *filesystemtype, unsigned long mountflags,
++                       const char *data) {
++      if (strlen(data) > sysconf(_SC_PAGESIZE) - 1) {
++              fprintf(stderr, "%s: mount options too long\n", progname);
++              errno = EINVAL;
++              return -1;
++      }
++      return mount(source, target, filesystemtype, mountflags, data);
++}
++
++
+ static int do_mount(const char *mnt, char **typep, mode_t rootmode,
+                   int fd, const char *opts, const char *dev, char **sourcep,
+                   char **mnt_optsp, off_t rootsize)
+@@ -836,7 +853,7 @@ static int do_mount(const char *mnt, cha
+       else
+               strcpy(source, subtype ? subtype : dev);
+ 
+-      res = mount(source, mnt, type, flags, optbuf);
++      res = mount_notrunc(source, mnt, type, flags, optbuf);
+       if (res == -1 && errno == ENODEV && subtype) {
+               /* Probably missing subtype support */
+               strcpy(type, blkdev ? "fuseblk" : "fuse");
+@@ -847,13 +864,13 @@ static int do_mount(const char *mnt, cha
+                       strcpy(source, type);
+               }
+ 
+-              res = mount(source, mnt, type, flags, optbuf);
++              res = mount_notrunc(source, mnt, type, flags, optbuf);
+       }
+       if (res == -1 && errno == EINVAL) {
+               /* It could be an old version not supporting group_id */
+               sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
+                       fd, rootmode, getuid());
+-              res = mount(source, mnt, type, flags, optbuf);
++              res = mount_notrunc(source, mnt, type, flags, optbuf);
+       }
+       if (res == -1) {
+               int errno_save = errno;
diff -Nru 
fuse-2.9.7/debian/patches/CVE-2018-10906/0002-fusermount-don-t-feed-escaped-commas-into-mount-opti.patch
 
fuse-2.9.7/debian/patches/CVE-2018-10906/0002-fusermount-don-t-feed-escaped-commas-into-mount-opti.patch
--- 
fuse-2.9.7/debian/patches/CVE-2018-10906/0002-fusermount-don-t-feed-escaped-commas-into-mount-opti.patch
    1970-01-01 01:00:00.000000000 +0100
+++ 
fuse-2.9.7/debian/patches/CVE-2018-10906/0002-fusermount-don-t-feed-escaped-commas-into-mount-opti.patch
    2018-07-23 20:50:41.000000000 +0200
@@ -0,0 +1,49 @@
+From: Jann Horn <[email protected]>
+Date: Fri, 13 Jul 2018 15:15:36 -0700
+Subject: [2/5] fusermount: don't feed "escaped commas" into mount options
+Origin: 
https://github.com/libfuse/commit/28bdae3d113ef479c1660a581ef720cdc33bf466
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2018-10906
+Bug: https://github.com/libfuse/libfuse/pull/268
+
+The old code permits the following behavior:
+
+$ _FUSE_COMMFD=10000 priv_strace -etrace=mount -s200 fusermount -o 
'foobar=\,allow_other' mount
+mount("/dev/fuse", ".", "fuse", MS_NOSUID|MS_NODEV, 
"foobar=\\,allow_other,fd=3,rootmode=40000,user_id=1000,group_id=1000") = -1 
EINVAL (Invalid argument)
+
+However, backslashes do not have any special meaning for the kernel here.
+
+As it happens, you can't abuse this because there is no FUSE mount option
+that takes a string value that can contain backslashes; but this is very
+brittle. Don't interpret "escape characters" in places where they don't
+work.
+---
+ util/fusermount.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/util/fusermount.c b/util/fusermount.c
+index 0e1d34d..143bd4a 100644
+--- a/util/fusermount.c
++++ b/util/fusermount.c
+@@ -29,6 +29,7 @@
+ #include <sys/socket.h>
+ #include <sys/utsname.h>
+ #include <sched.h>
++#include <stdbool.h>
+ 
+ #define FUSE_COMMFD_ENV               "_FUSE_COMMFD"
+ 
+@@ -754,8 +755,10 @@ static int do_mount(const char *mnt, char **typep, mode_t 
rootmode,
+               unsigned len;
+               const char *fsname_str = "fsname=";
+               const char *subtype_str = "subtype=";
++              bool escape_ok = begins_with(s, fsname_str) ||
++                               begins_with(s, subtype_str);
+               for (len = 0; s[len]; len++) {
+-                      if (s[len] == '\\' && s[len + 1])
++                      if (escape_ok && s[len] == '\\' && s[len + 1])
+                               len++;
+                       else if (s[len] == ',')
+                               break;
+-- 
+2.18.0
+
diff -Nru 
fuse-2.9.7/debian/patches/CVE-2018-10906/0003-fusermount-bail-out-on-transient-config-read-failure.patch
 
fuse-2.9.7/debian/patches/CVE-2018-10906/0003-fusermount-bail-out-on-transient-config-read-failure.patch
--- 
fuse-2.9.7/debian/patches/CVE-2018-10906/0003-fusermount-bail-out-on-transient-config-read-failure.patch
    1970-01-01 01:00:00.000000000 +0100
+++ 
fuse-2.9.7/debian/patches/CVE-2018-10906/0003-fusermount-bail-out-on-transient-config-read-failure.patch
    2018-07-23 20:50:41.000000000 +0200
@@ -0,0 +1,50 @@
+From: Jann Horn <[email protected]>
+Date: Fri, 13 Jul 2018 15:50:50 -0700
+Subject: [3/5] fusermount: bail out on transient config read failure
+Origin: 
https://github.com/libfuse/commit/cc315f5aa7fae04e16dda419859b2995992977cd
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2018-10906
+Bug: https://github.com/libfuse/libfuse/pull/268
+
+If an attacker wishes to use the default configuration instead of the
+system's actual configuration, they can attempt to trigger a failure in
+read_conf(). This only permits increasing mount_max if it is lower than the
+default, so it's not particularly interesting. Still, this should probably
+be prevented robustly; bail out if funny stuff happens when we're trying to
+read the config.
+
+Note that the classic attack trick of opening so many files that the
+system-wide limit is reached won't work here - because fusermount only
+drops the fsuid, not the euid, the process is running with euid=0 and
+CAP_SYS_ADMIN, so it bypasses the number-of-globally-open-files check in
+get_empty_filp() (unless you're inside a user namespace).
+---
+ util/fusermount.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/util/fusermount.c b/util/fusermount.c
+index 143bd4a..4e0f51a 100644
+--- a/util/fusermount.c
++++ b/util/fusermount.c
+@@ -565,10 +565,19 @@ static void read_conf(void)
+                       fprintf(stderr, "%s: reading %s: missing newline at end 
of file\n", progname, FUSE_CONF);
+ 
+               }
++              if (ferror(fp)) {
++                      fprintf(stderr, "%s: reading %s: read failed\n", 
progname, FUSE_CONF);
++                      exit(1);
++              }
+               fclose(fp);
+       } else if (errno != ENOENT) {
++              bool fatal = (errno != EACCES && errno != ELOOP &&
++                            errno != ENAMETOOLONG && errno != ENOTDIR &&
++                            errno != EOVERFLOW);
+               fprintf(stderr, "%s: failed to open %s: %s\n",
+                       progname, FUSE_CONF, strerror(errno));
++              if (fatal)
++                      exit(1);
+       }
+ }
+ 
+-- 
+2.18.0
+
diff -Nru 
fuse-2.9.7/debian/patches/CVE-2018-10906/0004-fusermount-refuse-unknown-options.patch
 
fuse-2.9.7/debian/patches/CVE-2018-10906/0004-fusermount-refuse-unknown-options.patch
--- 
fuse-2.9.7/debian/patches/CVE-2018-10906/0004-fusermount-refuse-unknown-options.patch
       1970-01-01 01:00:00.000000000 +0100
+++ 
fuse-2.9.7/debian/patches/CVE-2018-10906/0004-fusermount-refuse-unknown-options.patch
       2018-07-23 20:50:41.000000000 +0200
@@ -0,0 +1,45 @@
+From: Jann Horn <[email protected]>
+Date: Sat, 14 Jul 2018 03:47:50 -0700
+Subject: [4/5] fusermount: refuse unknown options
+Origin: 
https://github.com/libfuse/commit/5018a0c016495155ee598b7e0167b43d5d902414
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2018-10906
+Bug: https://github.com/libfuse/libfuse/pull/268
+
+Blacklists are notoriously fragile; especially if the kernel wishes to add
+some security-critical mount option at a later date, all existing systems
+with older versions of fusermount installed will suddenly have a security
+problem.
+Additionally, if the kernel's option parsing became a tiny bit laxer, the
+blacklist could probably be bypassed.
+
+Whitelist known-harmless flags instead, even if it's slightly more
+inconvenient.
+---
+ util/fusermount.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/util/fusermount.c b/util/fusermount.c
+index 4e0f51a..2792407 100644
+--- a/util/fusermount.c
++++ b/util/fusermount.c
+@@ -819,10 +819,16 @@ static int do_mount(const char *mnt, char **typep, 
mode_t rootmode,
+                                               flags |= flag;
+                                       else
+                                               flags  &= ~flag;
+-                              } else {
++                              } else if (opt_eq(s, len, 
"default_permissions") ||
++                                         opt_eq(s, len, "allow_other") ||
++                                         begins_with(s, "max_read=") ||
++                                         begins_with(s, "blksize=")) {
+                                       memcpy(d, s, len);
+                                       d += len;
+                                       *d++ = ',';
++                              } else {
++                                      fprintf(stderr, "%s: unknown option 
'%.*s'\n", progname, len, s);
++                                      exit(1);
+                               }
+                       }
+               }
+-- 
+2.18.0
+
diff -Nru 
fuse-2.9.7/debian/patches/CVE-2018-10906/0005-fusermount-whitelist-known-good-filesystems-for-moun.patch
 
fuse-2.9.7/debian/patches/CVE-2018-10906/0005-fusermount-whitelist-known-good-filesystems-for-moun.patch
--- 
fuse-2.9.7/debian/patches/CVE-2018-10906/0005-fusermount-whitelist-known-good-filesystems-for-moun.patch
    1970-01-01 01:00:00.000000000 +0100
+++ 
fuse-2.9.7/debian/patches/CVE-2018-10906/0005-fusermount-whitelist-known-good-filesystems-for-moun.patch
    2018-07-23 20:50:41.000000000 +0200
@@ -0,0 +1,124 @@
+From: Jann Horn <[email protected]>
+Date: Sat, 14 Jul 2018 13:37:41 +0200
+Subject: [5/5] fusermount: whitelist known-good filesystems for mountpoints
+Origin: 
https://github.com/libfuse/commit/795ad5d77434f3502e63a70c8a3fda94fa347e3d
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2018-10906
+Bug: https://github.com/libfuse/libfuse/pull/268
+
+Before:
+
+ $ _FUSE_COMMFD=1 priv_strace -s8000 -e trace=mount util/fusermount3 
/proc/self/fd
+  mount("/dev/fuse", ".", "fuse", MS_NOSUID|MS_NODEV, 
"fd=3,rootmode=40000,user_id=379777,group_id=5001") = 0
+ sending file descriptor: Socket operation on non-socket
+ +++ exited with 1 +++
+
+After:
+
+ $ _FUSE_COMMFD=1 priv_strace -s8000 -e trace=mount util/fusermount3 
/proc/self/fd
+ util/fusermount3: mounting over filesystem type 0x009fa0 is forbidden
+ +++ exited with 1 +++
+
+This patch could potentially have security
+impact on some systems that are configured with allow_other;
+see https://launchpad.net/bugs/1530566 for an example of how a similar
+issue in the ecryptfs mount helper was exploitable. However, the FUSE
+mount helper performs slightly different security checks, so that exact
+attack doesn't work with fusermount; I don't know of any specific attack
+you could perform using this, apart from faking the SELinux context of your
+process when someone's looking at a process listing. Potential targets for
+overwrite are (looking on a system with a 4.9 kernel):
+
+writable only for the current process:
+/proc/self/{fd,map_files}
+(Yes, "ls -l" claims that you don't have write access, but that's not true;
+"find -writable" will show you what access you really have.)
+
+writable also for other owned processes:
+/proc/$pid/{sched,autogroup,comm,mem,clear_refs,attr/*,oom_adj,
+oom_score_adj,loginuid,coredump_filter,uid_map,gid_map,projid_map,
+setgroups,timerslack_ns}
+[carnil: Adapt commit message to indent +++ lines to avoid missapplying/errors
+when applying with quilt]
+---
+ util/fusermount.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 49 insertions(+), 1 deletion(-)
+
+diff --git a/util/fusermount.c b/util/fusermount.c
+index 2792407..c63c50e 100644
+--- a/util/fusermount.c
++++ b/util/fusermount.c
+@@ -30,6 +30,7 @@
+ #include <sys/utsname.h>
+ #include <sched.h>
+ #include <stdbool.h>
++#include <sys/vfs.h>
+ 
+ #define FUSE_COMMFD_ENV               "_FUSE_COMMFD"
+ 
+@@ -915,6 +916,8 @@ static int check_perm(const char **mntp, struct stat 
*stbuf, int *mountpoint_fd)
+       int res;
+       const char *mnt = *mntp;
+       const char *origmnt = mnt;
++      struct statfs fs_buf;
++      size_t i;
+ 
+       res = lstat(mnt, stbuf);
+       if (res == -1) {
+@@ -987,8 +990,53 @@ static int check_perm(const char **mntp, struct stat 
*stbuf, int *mountpoint_fd)
+               return -1;
+       }
+ 
++      /* Do not permit mounting over anything in procfs - it has a couple
++       * places to which we have "write access" without being supposed to be
++       * able to just put anything we want there.
++       * Luckily, without allow_other, we can't get other users to actually
++       * use any fake information we try to put there anyway.
++       * Use a whitelist to be safe. */
++      if (statfs(*mntp, &fs_buf)) {
++              fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
++                      progname, mnt, strerror(errno));
++              return -1;
++      }
+ 
+-      return 0;
++      /* Use the same list of permitted filesystems for the mount target as
++       * the ecryptfs mount helper
++       * 
(https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225).
 */
++      typeof(fs_buf.f_type) f_type_whitelist[] = {
++              0x61756673 /* AUFS_SUPER_MAGIC */,
++              0x9123683E /* BTRFS_SUPER_MAGIC */,
++              0x00C36400 /* CEPH_SUPER_MAGIC */,
++              0xFF534D42 /* CIFS_MAGIC_NUMBER */,
++              0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
++              0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
++              0xF2F52010 /* F2FS_SUPER_MAGIC */,
++              0x65735546 /* FUSE_SUPER_MAGIC */,
++              0x01161970 /* GFS2_MAGIC */,
++              0x3153464A /* JFS_SUPER_MAGIC */,
++              0x000072B6 /* JFFS2_SUPER_MAGIC */,
++              0x0000564C /* NCP_SUPER_MAGIC */,
++              0x00006969 /* NFS_SUPER_MAGIC */,
++              0x00003434 /* NILFS_SUPER_MAGIC */,
++              0x5346544E /* NTFS_SB_MAGIC */,
++              0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
++              0x52654973 /* REISERFS_SUPER_MAGIC */,
++              0x73717368 /* SQUASHFS_MAGIC */,
++              0x01021994 /* TMPFS_MAGIC */,
++              0x24051905 /* UBIFS_SUPER_MAGIC */,
++              0x58465342 /* XFS_SB_MAGIC */,
++              0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
++      };
++      for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); 
i++) {
++              if (f_type_whitelist[i] == fs_buf.f_type)
++                      return 0;
++      }
++
++      fprintf(stderr, "%s: mounting over filesystem type %#010lx is 
forbidden\n",
++              progname, (unsigned long)fs_buf.f_type);
++      return -1;
+ }
+ 
+ static int try_open(const char *dev, char **devp, int silent)
+-- 
+2.18.0
+
diff -Nru fuse-2.9.7/debian/patches/series fuse-2.9.7/debian/patches/series
--- fuse-2.9.7/debian/patches/series    2015-06-09 21:41:34.000000000 +0200
+++ fuse-2.9.7/debian/patches/series    2018-07-23 20:50:41.000000000 +0200
@@ -4,3 +4,8 @@
 0004-fusermount-manpage.patch
 0005-dlsym.patch
 0006-arm64.patch
+CVE-2018-10906/0001-fusermount-prevent-silent-truncation-of-mount-option.patch
+CVE-2018-10906/0002-fusermount-don-t-feed-escaped-commas-into-mount-opti.patch
+CVE-2018-10906/0003-fusermount-bail-out-on-transient-config-read-failure.patch
+CVE-2018-10906/0004-fusermount-refuse-unknown-options.patch
+CVE-2018-10906/0005-fusermount-whitelist-known-good-filesystems-for-moun.patch

Reply via email to