当前位置: 首页 > news >正文

CVE_2026_31431漏洞复现与分析纪实

缘起

上周四早上通勤时,我瞥了一眼兰舍微信群,有兰友提到Linux的提权漏洞(如下图),当时并未太在意。

但到了下午,看雪安全公众号推送了一篇相关文章:《732字节,通杀所有Linux!一个潜伏十年的“隐形杀手”终曝光》,这个推送瞬间勾起了我的兴趣

接下来正好五一假期有空,我便决定深入研究一番。

所谓通杀,其实不然

看雪的文章出于安全考虑没有直接给出相关链接,这时回想起兰舍群里曾有人发过GitHubxint的链接,于是认真翻看起来(顺便也练练英文),

附仓库(https://github.com/rootsecdev/cve_2026_31431)截图如下

看到有PoC脚本,我便想亲自试一下。用什么环境呢?我手边正好有一台“幽兰本(格蠹3588 Linux笔记本),尝试运行检测脚本,结果如下:

并没有成功,这与通杀所有Linux!一个潜伏十年的漏洞的结论不太相符啊。

再试云主机

于是我又想到用云主机试试,但这次栽在了Python版本上——该漏洞的利用脚本要求Python 3.10及以上版本,我云主机上的是3.6.8。

python3 test_cve_2026_31431.pyTraceback (most recent call last): File "test_cve_2026_31431.py", line 65, in <module> def attempt_trigger(target_path: str) -> tuple[bool, bytes]:TypeError: 'type' object is not subscriptablepython3 --versionPython 3.6.8sudo yum install python310Loaded plugins: fastestmirrorLoading mirror speeds from cached hostfilebase | 3.6 kB 00:00:00extras | 2.9 kB 00:00:00updates | 2.9 kB 00:00:00No package python310 available.Error: Nothing to do

反复尝试通过yum安装Python 3.10均告失败,真是考验耐心(欲速则不达)。求助AI后,找到了一种源码编译安装的方法:

wget https://www.python.org/ftp/python/3.10.16/Python-3.10.16.tgztar -xzf Python-3.10.16.tgzcd Python-3.10.16./configure --enable-optimizations --prefix=/usr/localmake -j $(nproc)sudo make altinstall

然而又遇到了编译错误:

反复询问AI,对方给出了一堆安装选项,但都不在点上。后来终于发现关键是要禁用链接优化(--disable-lto):

#如果使用yum (RHEL 7)

sudo yum groupinstall "Development Tools"

sudo yum install gcc openssl-devel bzip2-devel libffi-devel zlib-devel readline-devel sqlite-devel tk-devel

最终瞄到了关键点是禁用链接优化(--disable-lto):

./configure --prefix=/usr/local --disable-optimizations --disable-lto

加上--disable-lto后,Python 3.10终于编译成功。但此时已经过去了好几个小时,兴趣难免有所消退。

在云主机(内核版本5.18.1)上再次尝试检测脚本,结果显示确实存在漏洞

# python3.10 test_cve_2026_31431.py[*] CVE-2026-31431 detector kernel=5.18.1 arch=x86_64[i] Kernel 5.18.1 predates the affected 6.12/6.17/6.18 lines; trigger may not apply even if prerequisites match.[+] AF_ALG + 'authencesn(hmac(sha256),cbc(aes))' loadable - precondition met.[!] VULNERABLE to CVE-2026-31431.[!] Marker b'PWND' (AAD seqno_lo) landed in the spliced page-cache page at offset 0.[!] Surrounding bytes: 50574e444641494c2d53454e (b'PWNDFAIL-SEN')[!] Apply the upstream fix or block algif_aead immediately.接着运行利用脚本(注意必须带上 --shell 参数,否则看不到效果): [steve@aiyun17735 cve_2026_31431]$ python3.10 exploit_cve_2026_31431.py --shell[*] CVE-2026-31431 LPE user=steve uid=1000[*] /etc/passwd: steve UID field at offset 1188 = '1000'[*] Patching '1000' -> '0000' in page cache...[*] Page cache now reads b'0000' at offset 1188[*] getpwnam('steve').pw_uid = 0[+] /etc/passwd page cache now lists steve as UID 0.[+] Run: su steve[+] 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[+] Executing `su steve` now...Password:[root@aiyun177350 cve_2026_31431]#

命令提示符变成了root,提权成功!几小时的折腾总算没有白费。

举一反三

这时我又想起幽兰本上尚未成功,于是在兰舍群里发了一条消息询问群友:

有兰友说内核老,其实不对,这个漏洞号称隐藏十年了啊。我继续与AI聊天,向AI提出了一串问题,让AI给我解答疑惑:

1.该漏洞对云服务器有影响吗?如果云服务器是容器环境呢?

2Android手机手机受影响吗?

3algif_aead是什么,是驱动吗?

4.如何判断是否在容器中?

5.云主机一般是什么环境?

6.这篇文章中提及的Copy Fail是什么术语:https://xint.io/blog/copy-fail-linux-distributions

7.”Copy Fail“这个词背景及来源

其中第3个问题的回答顺带解决了幽兰本不成功的原因::

我在幽兰本上检索编译选项CRYPTO_USER_API_AEAD,确实没有设置(is not set):

看来幽兰本上没有这个漏洞的原因是没有把有漏洞的代码编译进来。


漏洞原理仍难于理解

此时,我对漏洞的原理还是模糊的,下面这样的概括,你能看懂吗(截取自https://github.com/rootsecdev/cve_2026_31431)?

Vulnerability summary

algif_aead runs AEAD operations in-place (req->src == req->dst). When the source data is fed in via splice() from a regular file, the destination scatterlist contains references to the file's page-cache pages — i.e. the kernel will write into them. The authencesn(hmac(sha256), cbc(aes)) algorithm then performs a 4-byte "scratch" write of the AAD's seqno_lo field (bytes 4–7 of the sendmsg-supplied AAD) into that destination, corrupting the page-cache copy of the file.

Because the on-disk file is never modified, there is no on-disk signature; the corruption is observed only by readers that share the page cache. /etc/passwd and /usr/bin/su are both world-readable, so an unprivileged local user can corrupt the running kernel's view of either.

Affected: kernels carrying commit 72548b093ee3 (in-place AEAD, 2017) without the upstream revert. The disclosure confirmed Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 14.3, and SUSE 16, but the underlying primitive predates that range.

scatterlistthe file's page-cache pagessplice这术语超出我的知识,splice是个关键的概念,看雪有篇文章(https://mp.weixin.qq.com/s/HO_kS4OSIMFAMdvNKxxFqA)提供了中文解释。

把漏洞代码编译为内核模块

我深知纸上得来还是不深刻(古人云:纸上得来终觉浅,绝知此事要躬行),我想在幽兰或者gdk8上调试。虽然漏洞代码没有编译进内核,但可以手工编译啊。

我在crypto/Makefile下找到了对应源码algif_aead.oóalgif_aead.c(后来发现在gdk8上,还要另一个模块af_alg.oóaf_alg.c),如下图:

整一个Makefile文件(我参考的是llaolao驱动),内容如下:

WFLAGS := -Wstrict-prototypes -Wno-trigraphs -Wunused-resultLDFLAGS = -Map /var/tmp/algif_aead.txtEXTRA_CFLAGS := $(WFLAGS)# EXTRA_CFLAGS += -g -Wa,-adhln=$(<:.c=.lst)EXTRA_CFLAGS += -D_DEBUG -g3-fno-stack-protectorMODULE = algif_aeadMODULE2 = af_algKERNELDIR?=/lib/modules/$(shell uname -r)/buildobj-m := $(MODULE).oobj-m += $(MODULE2).oall:make -C $(KERNELDIR) M=$(shell pwd) modulesmake $(MODULE).lst $(MODULE2).lst#$(MODULE)-objs := $(MODULE).o%.lst:%.koobjdump --source --line-numbers --all-headers --demangle --disassemble $^ > $@clean:rm -rf *.o *~ .*.cmd *.ko *.mod *.mod.c *.order *.symvers .tmp_versions built-in.o

如果要为gdk8编译ko(我是在幽兰本上为其编译),则需要export KERNELDIR目录,我编写一个mybuildgdk8.sh,内容如下

export KERNELDIR=/gewu/home/geduer/gedulab/gdk8ktime bear --output compile_commands.json -- make KBUILD_VERBOSE=2 V=1 quiet="" -j1 ARCH=arm64

编译成功后,通过insmod ./algif_aead.ko加载,试了下,不出意外exploit是成功的。

gdk8上的python3.6.9,我还是把python3.10源码编译了一番(注意加上--disable-lto)。

gdk8上,insmod ./algif_aead.koinsmod ./af_alg.ko, 执行python3.10 exploit_cve_2026_31431.py –shell竟然出现异外情况,输出su: Cannot determine your user name.

cat /etc/passwd可以看到geduerUID1002变成了0000了,至少内存是被改掉的,那可能su版本有差异。

使用挥码枪调试

这里我选择gdk8作为调试目标,当然选择幽兰本也可以,选择gdk8主要它更轻便些,幽兰用来构建驱动或者看文档。

操作步骤:

1.按上面左图连好挥码枪与gdk8盒子,打开nanocode软件进入调内核调试并设置好符号路径等

2.对文档中提及的函数下断点bp crypto_authenc_esn_decrypt

3.ssh连接上gdk8,输入python3.10 test_cve_2026_31431.py,此时下图内容输出了一半停下

4.此时nanocode断点命中,敲入kn可以看到调用栈,如下图

5.继续敲rdt lk!aead_request 0xffffffc0e30fd300

6.可以看到srcdst如文档所说是同一个,类型为struct scatterlist

7.继续观察dt lk!scatterlist 0xffffffc0`e30fd010

这里page_link字段是多用途字段,它可以指向另一个scatterlist(bit01),注意dst(0xffffffc0`e30fd010)指向数组,通过SG_END标记它是最后一个,当前bit1没有置1,显然它不是最后一个。

#define SG_CHAIN 0x01UL#define SG_END 0x02UL/** We overload the LSB of the page pointer to indicate whether it's* a valid sg entry, or whether it points to the start of a new scatterlist.* Those low bits are there for everyone! (thanks mason :-)*/#define sg_is_chain(sg) ((sg)->page_link & SG_CHAIN)#define sg_is_last(sg) ((sg)->page_link & SG_END)#define sg_chain_ptr(sg) \ ((struct scatterlist *) ((sg)->page_link & ~(SG_CHAIN | SG_END)))

8.继续观察dt lk!scatterlist 0xffffffc0`e30fd010 +0x20(sizeof(lk!scatterlist))第二个scatterlist

9.继续下断bp lk!scatterwalk_map_and_copy,跳过前3次的下断,直接看改写内存那个断点(即这行scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);

10.继续下断lk!scatterwalk_copychunksg,断下我们看到PWND字串,这个要写到目标缓存的内容

11.我们继续对memcpy_dir下断,可惜它是内联函数,我们打开汇编窗口找到lk__memcpy这行,bp/1 ffffff800862442c下断

12.我们观察改写的即将发生,

x1指向4字节将改写x0指向内存。(x0指向的就是文件缓存地址)

13.接下来是关键的splice操作,另起一章吧。

splice系统调用

splice()Linux内核提供的一个高效的零拷贝系统调用,其核心价值在于在内核空间直接移动数据,避免数据在内核缓冲区用户空间缓冲区之间不必要的来回拷贝。但它有一个严格的使用限制:两个文件描述符中至少有一个必须是管道(pipe

函数原型:

ssize_t splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out, size_t len, unsigned int flags);

调用成功时返回实际传输的字节数,失败则返回-1并设置errno。参数看似复杂,但存在固定的配对关系,可以分三组理解:

1.基础I/O与长度控制

  • int fd_in & int fd_out:数据流入和流出的文件描述符(文件、套接字、设备或管道的句柄)。

  • size_t len:想要移动的最大字节数。

2.偏移量操作

文件描述符类型

off_in/off_out 参数值

具体行为
管道必须为 NULL从管道的当前读写位置操作,管道不支持随机访问。
非管道

NULL

使用文件内核维护的当前文件偏移量(操作后自动更新)。
非管道非 NULL使用指针指向的值作为起始偏移量,但不更新文件本身的文件偏移量,实现独立的偏移量控制。

3. 行为控制标志

  • SPLICE_F_MOVE:性能优化提示,指示内核尽可能通过移动内存页面取代复制数据(从Linux 2.6.21起该标志暂时是空操作,但传递它合法)。

  • SPLICE_F_NONBLOCK:使splice的管道操作非阻塞(注意:若fd_infd_out本身没有O_NONBLOCK标志,整个调用仍可能阻塞)。

  • SPLICE_F_MORE:性能优化提示,常用于网络传输,建议内核后续还有更多数据到来。若fd_out是套接字,则与send(2)中的MSG_MORE标志效果类似,有助于减少网络数据包。

  • Pythonsplice封装:

    os.splice(src, dst, count, offset_src=None, offset_dst=None, flags=0)

scatterList布局图

splice系统调用就像一个魔法,把文件缓存引用给传递了下去,在内核端实际是数组scatterlist dst[],有两个元素,AAD占一个元素,剩下的占另一元素。

结语

找资料,看代码,上调试器,五天假期忙得不亦乐乎,感觉时间过得好快,明天又到上班的时间了(时间又不属于自己了)。今天把几天忙碌的过程写下来,作为自己学习的记录,也分享给格友和同行们,写的不好的地方,欢迎大家批评指正。这个五一假期过的好充实。


参考资料:

https://github.com/rootsecdev/cve_2026_31431

https://xint.io/blog/copy-fail-linux-distributions

https://mp.weixin.qq.com/s/HO_kS4OSIMFAMdvNKxxFqA

***

正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生

扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以文章和有声读物

也欢迎关注格友公众号

http://www.jsqmd.com/news/769160/

相关文章:

  • AISMM零售应用实战手册:从数据接入、模型微调到实时决策闭环的7步标准化部署流程
  • Cursor智能体开发:命令行界面
  • Ai2Psd:3分钟完成AI到PSD矢量分层转换的终极解决方案
  • 如何修改ANTSDR U220 的serail
  • [实战] 2026年制造业质量数字化:利用检验计划软件实现从图纸到FAI的高效转化
  • 汽车大灯改装价格,苏州光烁贵不贵? - mypinpai
  • 基于Jetpack Compose与Ktor的Android天气应用POC开发实践
  • 从波形图看懂AHB等待传输:IDLE、BUSY、ERROR响应下的地址与传输类型变化全解析
  • 基于LLM的智能API调用引擎:用自然语言驱动后端服务
  • 深蓝词库转换:3分钟解决你的输入法迁移难题
  • Windows HEIC缩略图终极指南:免费开启iPhone照片预览功能
  • 【日常刷题/动态规划C++]单词拆分,回文子串,分割回文串2
  • Applite:Mac用户的终极软件管理神器,告别复杂命令行
  • 如何用ncmdumpGUI三分钟解锁网易云音乐NCM格式:Windows用户必备的音乐文件转换终极指南
  • 卤鹅品牌哪家强?祥木记靠谱吗 - mypinpai
  • python项目修改目录后pip不能使用的修复~
  • 终极指南:5步掌握KrkrzExtract XP3资源解包工具
  • 大模型为什么越来越“听话”?一文讲透强化学习、SFT、DPO
  • LoongArch架构工业处理器2K1000LA开发与应用指南
  • Prompster:AI聊天提示词快捷指令库,提升跨平台对话效率
  • 智能解放双手:阴阳师自动化脚本SmartOnmyoji完整实战指南
  • 2026 天津财税机构口碑排行|专业评测推荐,优质代办机构优选 - 品牌智鉴榜
  • 苹果 iOS 27 等系统秋季或发布,将允许用户选第三方 AI 模型运行智能功能
  • 别再只刷新了!手把手教你排查Nginx/Apache/IIS网关超时504错误的5个实战场景
  • 2026年南山民宿品牌推荐,山上云下民宿口碑佳 - mypinpai
  • Cortex-R82处理器实时性能优化与中断延迟控制
  • 从数据到预测:手把手拆解STGCN(PyTorch)中的数据处理与模型构建全流程
  • WarcraftHelper:魔兽争霸3现代兼容性修复终极指南
  • AI软件框架概述
  • 坐轮渡有感