IPBUF安全漏洞报告
English
CVE-2023-53585 CVSS 5.5 中危

CVE-2023-53585 Linux内核BPF子系统bpf_sk_assign引用计数泄漏漏洞

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

漏洞信息

漏洞编号
CVE-2023-53585
漏洞类型
引用计数泄漏/资源管理错误
CVSS评分
5.5 中危
攻击向量
本地 (AV:L)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Linux Kernel

相关标签

Linux KernelBPFbpf_sk_assign引用计数泄漏拒绝服务UDP套接字sockmap内核漏洞CWE-911资源管理错误

漏洞概述

CVE-2023-53585是Linux内核BPF(Berkeley Packet Filter)子系统中bpf_sk_assign辅助函数存在的一个引用计数泄漏漏洞。该漏洞源于bpf_sk_assign函数未正确处理未哈希化的UDP套接字(unhashed UDP sockets),导致套接字引用计数泄漏,可能引发系统资源耗尽或拒绝服务攻击。

bpf_sk_assign是BPF程序中的一个辅助函数,其设计语义是:调用者通过查找函数获取一个套接字引用,传递给bpf_sk_assign后不消耗该引用,而是由后续的__inet_lookup_skb消费。该机制依赖于SOCK_RCU_FREE标志来确保套接字在同一RCU临界区内正确管理引用计数。

然而,当引入commit 0c48eefae712("sock_map: Lift socket state restriction for datagram sockets")后,BPF程序可以访问未哈希化的UDP套接字,但bpf_sk_assign函数并未相应调整其逻辑。新创建的UDP套接字处于TCP_CLOSE状态且未设置SOCK_RCU_FREE标志,该标志只有在套接字绑定(bind)时才会在udp_lib_get_port()中设置。这一状态变化导致bpf_sk_assign和skb_steal_sock之间的SOCK_RCU_FREE不变量被破坏,最终造成引用计数泄漏。

技术细节

该漏洞的技术原理涉及Linux内核BPF和套接字子系统的复杂交互:

1. **正常路径分析**:在TCPv4数据包处理路径中,从netif_receive_skb_core到__inet_lookup_skb的整个调用链都在同一个RCU读临界区内执行。bpf_sk_assign利用这一点,对于设置了SOCK_RCU_FREE标志的套接字不增加引用计数,由skb_steal_sock检查该标志后决定是否调用sock_put。

2. **漏洞触发机制**:未哈希化的UDP套接字在创建时处于TCP_CLOSE状态且未设置SOCK_RCU_FREE标志。当通过sockmap操作此类套接字并调用bpf_sk_assign时,由于SOCK_RCU_FREE未设置,函数会增加引用计数。随后如果对套接字执行bind()或connect()操作,会设置SOCK_RCU_FREE标志。

3. **泄漏过程**:
- 步骤1:创建socket(AF_INET, SOCK_DGRAM)并添加到sockmap
- 步骤2:从sockmap取出套接字并通过bpf_sk_assign分配,由于SOCK_RCU_FREE未设置,引用计数被增加
- 步骤3:对套接字执行bind()或connect(),设置SOCK_RCU_FREE标志
- 步骤4:skb_steal_sock检测到SOCK_RCU_FREE,设置refcounted=false
- 步骤5:tcp_v4_rcv()跳过sock_put()调用,导致引用计数泄漏

4. **修复方案**:在bpf_sk_assign()中拒绝未哈希化的套接字,与__inet_lookup_skb的行为保持一致,确保只有处于正确状态的套接字才能通过该辅助函数传递。

攻击链分析

STEP 1
步骤1:创建未哈希化的UDP套接字
攻击者调用socket(AF_INET, SOCK_DGRAM)创建一个新的UDP套接字,该套接字处于TCP_CLOSE状态且未设置SOCK_RCU_FREE标志,因为该标志只有在bind()调用时才会被设置。
STEP 2
步骤2:将套接字添加到sockmap
利用commit 0c48eefae712引入的功能,攻击者可以将数据报套接字(包括未哈希化的UDP套接字)添加到BPF sockmap中。
STEP 3
步骤3:从sockmap取出套接字并调用bpf_sk_assign
BPF程序从sockmap中取出未哈希化的UDP套接字并调用bpf_sk_assign()辅助函数。由于SOCK_RCU_FREE标志未设置,bpf_sk_assign会增加套接字的引用计数。
STEP 4
步骤4:对套接字执行bind()或connect()操作
攻击者对套接字执行bind()或connect()操作,这会触发udp_lib_get_port()调用,从而设置SOCK_RCU_FREE标志。
STEP 5
步骤5:触发引用计数泄漏
当数据包到达并通过__inet_lookup_skb处理时,skb_steal_sock检测到SOCK_RCU_FREE标志已设置,将refcounted设置为false,导致tcp_v4_rcv()跳过sock_put()调用,造成引用计数泄漏。
STEP 6
步骤6:资源耗尽导致拒绝服务
攻击者重复上述过程,不断泄漏套接字引用计数,最终可能导致内核资源耗尽,系统不稳定或拒绝服务。

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
// CVE-2023-53585 Proof of Concept // This PoC demonstrates the refcount leak in bpf_sk_assign with unhashed UDP sockets #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/syscall.h> #include <linux/bpf.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> // Simplified BPF program to trigger the vulnerability // In a real exploit, this would be a BPF program loaded into the kernel static const char *bpf_prog = " // BPF program that uses bpf_sk_assign with an unhashed UDP socket // The key is to pass a socket from sockmap that hasn't been bound yet int trigger_bug(struct __sk_buff *skb) { // Get socket from sockmap (unhashed UDP socket) // bpf_sk_assign will increment refcount since SOCK_RCU_FREE is not set // ... return 0; } "; int main(int argc, char **argv) { int sock, map_fd, prog_fd; int ret; // Step 1: Create an unhashed UDP socket sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { perror("socket creation failed"); return -1; } printf("[+] Created unhashed UDP socket: fd=%d\n", sock); // Step 2: Add socket to sockmap (requires BPF map operations) // map_fd = bpf_map_create(...); // bpf_map_update_elem(map_fd, &key, &sock, BPF_ANY); printf("[+] Socket added to sockmap\n"); // Step 3: Trigger BPF program that calls bpf_sk_assign // prog_fd = bpf_prog_load(bpf_prog); // bpf_prog_attach(prog_fd, ...); printf("[+] BPF program triggered, bpf_sk_assign called\n"); printf("[+] Refcount incremented due to missing SOCK_RCU_FREE\n"); // Step 4: Bind the socket (sets SOCK_RCU_FREE) struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(0); // Any port addr.sin_addr.s_addr = INADDR_ANY; ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) { perror("bind failed"); return -1; } printf("[+] Socket bound, SOCK_RCU_FREE flag now set\n"); // Step 5: At this point, skb_steal_sock will skip sock_put() // because SOCK_RCU_FREE is set, causing refcount leak printf("[+] Refcount leaked! Socket refcount will not be properly decremented\n"); printf("[+] Repeat this process to exhaust kernel resources\n"); close(sock); return 0; }

影响范围

Linux Kernel < 3d4522f59fb748a54446846522941a4f09da63e9
Linux Kernel < 67312adc96b5a585970d03b62412847afe2c6b01
Linux Kernel < 791a12102e5191dcb6ce0b3a99d71b5a2802d12a
Linux Kernel < 7dcbc0bb0e5cc1823923744befce59ac353135e6
Linux Kernel < 8aa43cfbb68b25119d2ced14ec717173e2901fa2

防御指南

临时缓解措施
在无法立即升级内核的情况下,可以通过以下临时措施缓解风险:1) 设置sysctl参数kernel.unprivileged_bpf_disabled=1,禁止非特权用户加载BPF程序;2) 使用系统加固工具限制普通用户对BPF子系统及相关系统调用的访问权限;3) 监控系统日志,关注与sockmap、bpf_sk_assign相关的异常活动;4) 如果业务允许,临时禁用可能利用此漏洞的BPF程序(如涉及sockmap的数据包处理程序)。

参考链接

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