// https://syzkaller.appspot.com/bug?id=f56661b43ef1019d28054d259f87f02486757c95 #include #include #include #include #include #include #include #include #include #include #include #include #define REBOOT_MAGIC1 0xfee1dead #define REBOOT_MAGIC2 672274793 #define REBOOT_CMD_RESTART 0x01234567 char udc_name[128] = {0}; int find_udc() { DIR *dir = opendir("/sys/class/udc"); if (!dir) { perror("opendir /sys/class/udc"); return -1; } struct dirent *ent; while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] == '.') continue; strncpy(udc_name, ent->d_name, sizeof(udc_name) - 1); closedir(dir); return 0; } closedir(dir); fprintf(stderr, "No UDC found\n"); return -1; } char dev_desc[] = { 18, // bLength 1, // bDescriptorType (USB_DT_DEVICE) 0x00, 0x02, // bcdUSB (2.00) 0, // bDeviceClass 0, // bDeviceSubClass 0, // bDeviceProtocol 64, // bMaxPacketSize0 0x40, 0x20, // idVendor (0x2040) 0x10, 0x75, // idProduct (0x7510) 0x00, 0x01, // bcdDevice 0, // iManufacturer 0, // iProduct 0, // iSerialNumber 1 // bNumConfigurations }; char config_desc[] = { 9, // bLength 2, // bDescriptorType (USB_DT_CONFIG) 18, 0, // wTotalLength (18 bytes) 1, // bNumInterfaces 1, // bConfigurationValue 0, // iConfiguration 0x80, // bmAttributes 50, // bMaxPower // Interface 0 9, // bLength 4, // bDescriptorType (USB_DT_INTERFACE) 0, // bInterfaceNumber 0, // bAlternateSetting 0, // bNumEndpoints (0 triggers pvrusb2 firmware load!) 0xff, // bInterfaceClass (Vendor Specific) 0, // bInterfaceSubClass 0, // bInterfaceProtocol 0, // iInterface }; void *reboot_thread(void *arg) { printf("[+] Triggering reboot to disable usermodehelper...\n"); syscall(__NR_reboot, REBOOT_MAGIC1, REBOOT_MAGIC2, REBOOT_CMD_RESTART, 0); return NULL; } int main() { if (find_udc() < 0) { return 1; } printf("[+] Found UDC: %s\n", udc_name); int fd = open("/dev/raw-gadget", O_RDWR); if (fd < 0) { perror("open /dev/raw-gadget"); return 1; } struct usb_raw_init init; memset(&init, 0, sizeof(init)); strncpy((char *)init.driver_name, "dummy_udc", sizeof(init.driver_name) - 1); strncpy((char *)init.device_name, udc_name, sizeof(init.device_name) - 1); init.speed = 3; // USB_SPEED_HIGH if (ioctl(fd, USB_RAW_IOCTL_INIT, &init) < 0) { perror("USB_RAW_IOCTL_INIT"); return 1; } if (ioctl(fd, USB_RAW_IOCTL_RUN, 0) < 0) { perror("USB_RAW_IOCTL_RUN"); return 1; } pthread_t tid; int configured = 0; while (1) { char event_buf[sizeof(struct usb_raw_event) + sizeof(struct usb_ctrlrequest)] = {0}; struct usb_raw_event *ev = (struct usb_raw_event *)event_buf; ev->length = sizeof(struct usb_ctrlrequest); if (ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, ev) < 0) { perror("USB_RAW_IOCTL_EVENT_FETCH"); break; } if (ev->type == USB_RAW_EVENT_CONTROL) { struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)ev->data; if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength) { // IN request with data if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) { int desc_type = ctrl->wValue >> 8; if (desc_type == USB_DT_DEVICE) { int len = sizeof(dev_desc); if (len > ctrl->wLength) len = ctrl->wLength; char io_buf[sizeof(struct usb_raw_ep_io) + sizeof(dev_desc)] = {0}; struct usb_raw_ep_io *io_ptr = (struct usb_raw_ep_io *)io_buf; io_ptr->ep = 0; io_ptr->flags = 0; io_ptr->length = len; memcpy(io_ptr->data, dev_desc, len); ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io_ptr); } else if (desc_type == USB_DT_CONFIG) { int len = sizeof(config_desc); if (len > ctrl->wLength) len = ctrl->wLength; char io_buf[sizeof(struct usb_raw_ep_io) + sizeof(config_desc)] = {0}; struct usb_raw_ep_io *io_ptr = (struct usb_raw_ep_io *)io_buf; io_ptr->ep = 0; io_ptr->flags = 0; io_ptr->length = len; memcpy(io_ptr->data, config_desc, len); ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io_ptr); } else { ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0); } } else { ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0); } } else { // OUT request or 0-length request int len = ctrl->wLength; char *io_buf = calloc(1, sizeof(struct usb_raw_ep_io) + len); struct usb_raw_ep_io *io_ptr = (struct usb_raw_ep_io *)io_buf; io_ptr->ep = 0; io_ptr->flags = 0; io_ptr->length = len; if (ioctl(fd, USB_RAW_IOCTL_EP0_READ, io_ptr) < 0) { printf("[-] USB_RAW_IOCTL_EP0_READ failed: %s\n", strerror(errno)); } free(io_buf); if (ctrl->bRequest == USB_REQ_SET_CONFIGURATION) { if (!configured) { configured = 1; pthread_create(&tid, NULL, reboot_thread, NULL); // Wait a bit to let reboot disable usermodehelper before configuring usleep(20000); // 20ms } if (ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0) < 0) { printf("[-] USB_RAW_IOCTL_CONFIGURE failed: %s\n", strerror(errno)); } else { printf("[+] Configured\n"); } } } } } return 0; }