从CVE-2024-1086漏洞复现失败到成功:一次内核安全实践的技术复盘
1. 漏洞背景与环境准备
CVE-2024-1086是Linux内核netfilter子系统中nf_tables组件的一个高危漏洞,属于典型的UAF(Use-After-Free)类型。这个漏洞影响范围相当广,从v3.15到v6.8-rc1的内核版本都可能中招,特别是v5.14到v6.6之间的版本风险最高。我在第一次尝试复现时,就因为这个版本范围判断失误浪费了不少时间。
搭建实验环境时,我强烈建议使用虚拟机。我选择了Ubuntu 22.04作为基础系统,主要考虑两点:一是这个LTS版本稳定性好,二是官方仓库提供了丰富的内核版本选择。最开始我图省事直接用了系统默认的5.15内核,结果发现根本不在漏洞影响范围内,只能重头再来。
准备工具链时,这几个包必不可少:
sudo apt install make gcc fakeroot build-essential \ ncurses-dev xz-utils libssl-dev bc flex libelf-dev \ bison dwarves zstd特别是dwarves这个包,很多教程里都没提,但在编译新版本内核时缺了它就会报错。我在这里栽过跟头,编译到一半突然失败,排查半天才发现是这个依赖没装。
2. 内核编译的坑与技巧
2.1 源码获取与配置
从清华镜像站下载内核源码确实快很多:
wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v6.x/linux-6.3.13.tar.xz tar -xf linux-6.3.13.tar.xz cd linux-6.3.13关键的一步是内核配置。新手最容易犯的错误就是直接复制别人的.config文件。我有次偷懒用了网上找的配置,结果编译出来的内核连网卡驱动都没带,虚拟机直接断网。现在我的做法是:
make defconfig # 生成默认配置 make menuconfig # 手动调整在menuconfig界面里,要特别注意这几个选项:
- CONFIG_NF_TABLES 必须启用(默认是模块形式)
- CONFIG_USER_NS 要打开
- CONFIG_STATIC_USERMODEHELPER 建议编译进内核
2.2 编译加速技巧
第一次完整编译内核花了我将近两小时,后来发现几个提速诀窍:
- 使用
-j$(nproc)参数充分利用CPU核心 - 先
make -j$(nproc)编译内核,再单独make modules - 如果只是修改配置重新编译,可以跳过
make clean
实测下来,8核机器上完整编译大约40分钟,增量编译只需5-10分钟。记得编译前先free -h看看内存,小于8GB容易爆内存。
3. 漏洞复现的曲折历程
3.1 第一次失败尝试
按照网上的教程,我下载了漏洞利用代码:
git clone https://github.com/Notselwyn/CVE-2024-1086 cd CVE-2024-1086 make运行后却卡在了这一步:
[!] failed to find kernel code segment... CONFIG_STATIC_USERMODEHELPER disabled?检查发现确实是内核配置问题。这里有个细节:即使.config文件里设置了CONFIG_STATIC_USERMODEHELPER=y,实际编译时也可能因为依赖项不满足而被自动禁用。正确的验证方式是:
grep CONFIG_STATIC_USERMODEHELPER /boot/config-$(uname -r)3.2 成功复现的关键
换了6.1.72内核后终于成功。这个版本有个优势:Ubuntu官方提供了预编译包,省去编译时间:
wget https://kernel.ubuntu.com/mainline/v6.1.72/amd64/linux-*.deb dpkg -i linux-*.deb reboot提权成功的瞬间,终端里跳出那个梦寐以求的uid=0(root)时,真是有种通关打BOSS的成就感。完整的漏洞利用过程其实分几步:
- 创建用户命名空间(CLONE_NEWUSER)
- 创建网络命名空间(CLONE_NEWNET)
- 设置nftables规则触发UAF
- 通过内存喷射(spray)控制执行流
4. 漏洞缓解方案实测
4.1 禁用nf_tables模块
最直接的防护方法就是禁用问题模块:
echo "blacklist nf_tables" >> /etc/modprobe.d/blacklist.conf update-initramfs -u reboot验证是否生效:
lsmod | grep nf_tables # 应该无输出注意:这会导致基于nftables的防火墙规则全部失效。如果系统在用firewalld,记得先切回iptables模式:
firewall-cmd --set-backend=iptables4.2 限制用户命名空间
对于容器环境,完全禁用用户命名空间可能影响太大。折中的方案是限制普通用户使用:
echo "kernel.unprivileged_userns_clone=0" >> /etc/sysctl.conf sysctl -p我在Docker环境中测试发现,这会导致容器启动失败:
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed...解决方案是给Docker授权:
sudo chmod u+s /usr/bin/docker5. 对容器环境的影响评估
在K8s集群中直接禁用用户命名空间显然不现实。我的测试环境用kubeadm搭建,发现两个关键点:
Containerd配置:需要在
/etc/containerd/config.toml中启用userns[plugins."io.containerd.grpc.v1.cri".containerd] userns_enable = truePodSecurityPolicy:需要允许privileged容器或者明确授权:
securityContext: runAsUser: 0 usernsOptions: mode: "host"
性能方面,启用用户命名空间会导致约5-10%的IO性能下降,这点在数据库类容器中特别明显。我的测试数据显示,MySQL容器在启用userns后,TPS下降了约8%。
6. 内核调试技巧分享
在复现过程中,这几个调试方法帮了大忙:
查看内核日志:
dmesg -wH | grep -i nft动态调试nf_tables:
echo 'file nf_tables* +p' > /sys/kernel/debug/dynamic_debug/control崩溃分析:安装crash工具
apt install crash crash /usr/lib/debug/boot/vmlinux-$(uname -r) /var/crash/xxx.crash有一次漏洞利用导致内核panic,通过crash工具的bt命令查看调用栈,很快就定位到是内存释放后又被引用的位置。
7. 给安全研究新手的建议
- 版本选择要谨慎:我建议用6.1.x系列内核,这个版本段资料多且稳定
- 善用QEMU调试:比纯虚拟机更方便观察内核行为
qemu-system-x86_64 -kernel bzImage -append "console=ttyS0" -nographic - 保持实验记录:我养成了用asciinema录屏的习惯,能完整复现操作过程
- 理解原理比复现更重要:花时间研究漏洞的root cause,比单纯提权更有价值
记得第一次看到漏洞利用代码时,那些内存操作看得我头皮发麻。后来把PoC拆解成小段单独测试,才慢慢理解其中的精妙之处。比如这个spray技巧:
for (int i = 0; i < 16000; i++) { pipe2(pipefd, O_DIRECT); write(pipefd[1], buf, PAGE_SIZE); }其实就是通过大量管道占用内存,提高UAF后控制目标内存的概率。
