DF-3911 / trigger_analysis.c
/* * DF-3911 trigger/analysis — unvalidated IOC-authored firmware image walk in * mpt_download_fw() (sys/dev/disk/mpt/mpt.c:2467-2485) * * Kernel path (hardware-gated: LSI MPT Fusion IOC — malicious or faulty): * When IOCFACTS sets MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT, mpt_upload_fw() * lets the IOC DMA an "image" of ioc_facts.FWImageSize bytes into * mpt->fw_image (mpt.c:2751-2784). Every field of MpiFwHeader_t and * MpiExtImageHeader_t is therefore IOC-controlled. After a hard reset, * mpt_download_fw() walks it with zero validation: * - mpt_diag_outsl(mpt, fw_hdr->LoadStartAddress, (uint32_t*)fw_hdr, * fw_hdr->ImageSize) [mpt.c:2469] * ImageSize is NOT checked against mpt->fw_image_size -> reads up to * 4 GiB past the DMA buffer, streaming kernel memory to the PCI diag * data port until an unmapped page faults -> panic. * - ext = (MpiExtImageHeader_t *)((uintptr_t)fw_hdr + ext_offset) * with ext_offset = fw_hdr/ext->NextImageHeaderOffset unvalidated * [mpt.c:2474-2482] -> wild pointer reads at attacker-chosen offsets * (including loops that never terminate). * * This harness reproduces the pointer arithmetic in userspace with a * malicious ext_offset and ImageSize: it segfaults on the wild read, the * userspace analogue of the kernel page fault. */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> typedef struct { uint32_t ImageSize, NextImageHeaderOffset, LoadStartAddress; } Hdr; int main(void) { uint8_t buf[4096]; /* stands in for mpt->fw_image */ uint32_t fw_image_size = 4096; Hdr *fw_hdr = (Hdr *)buf; /* IOC authors: huge ImageSize, wild next-image offset */ fw_hdr->ImageSize = 0x40000000; /* 1 GiB >> 4096 allocated */ fw_hdr->NextImageHeaderOffset = 0xfffff000; printf("fw_image_size=%u, IOC ImageSize=%u, IOC NextImageHeaderOffset=%#x\n", fw_image_size, fw_hdr->ImageSize, fw_hdr->NextImageHeaderOffset); if (fw_hdr->ImageSize > fw_image_size) printf("UNFIXED KERNEL: walks %u bytes from a %u-byte buffer -> OOB read\n", fw_hdr->ImageSize, fw_image_size); /* the unfixed walk, verbatim shape: */ Hdr *ext = (Hdr *)((uintptr_t)fw_hdr + fw_hdr->NextImageHeaderOffset); printf("reading ext header at fw_image+%#x ...\n", fw_hdr->NextImageHeaderOffset); fflush(stdout); printf("ext->NextImageHeaderOffset=%#x\n", ext->NextImageHeaderOffset); /* wild read -> SIGSEGV */ return 0; } |