CVE2024-1086 nftables UAF漏洞

具作者介绍,该漏洞影响从 v5.14(包括)到 v6.6(包括)的版本,但不包括已修补的分支 v5.15.149>、v6.1.76>、v6.6.15>。这些版本的修补程序已于 2024 年 2 月发布。底层漏洞影响从 v3.15 到 v6.8-rc1 的所有版本(不包括已修补的稳定分支)。- - - 漏洞细节由于内容太深,看起来也是十分费劲,像是看天书一样。有兴趣的可以浏览作者发布的漏洞介绍[1]。我这里就力所能及的给一些修复方案的介绍。

个人理解

首先这个漏洞来自于netfilter子系统nf_tables组件中存在 UAF漏洞 。nf_tables据我之前了解,他作用类似于iptables,都是用于防火墙。据网上不可靠消息说是iptables面对大量策略配置时候,性能堪忧,所以才诞生出了nf_tables。再一个该漏洞是需要用到用户命名空间,命名空间是用于隔离虚拟环境的。

实验目的

  • 关闭nf_tables模块实现漏洞封堵 echo "blacklist nf_tables" >>/etc/modprobe.d/nf_tables-blacklist.conf
  • 关闭用户命名空间实现漏洞封堵,以及测试对docker容器的影响 echo "user.max_user_namespaces=0" > /etc/sysctl.d/userns.conf;sysctl -p /etc/sysctl.d/userns.conf

历程记录

失败样例: 本来是准备在ubuntu22.04 内核版本从原来的5.15.0 升级至6.3.13(按照kernel-exploit-factory[2]样例用的6.3.13复现该漏洞的提权。但由于个人并不是从事于内核开发,能力不足。到了最后一步也无法复现提权。(USERMODEHELPER 内核参数已编译入内核,但提权时候报此参数是关闭的)。

# 编译linux内核
root@josiahserver:~# wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v6.x/linux-6.3.13.tar.xz #下载内核源码
root@josiahserver:~# tar -xf linux-6.3.13.tar.xz;cd linux-6.3.13 #解压并进入目录
root@josiahserver:~# apt install make gcc fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison dwarves zstd  #安装编译需要的工具
root@josiahserver:~# scripts/config --disable SYSTEM_TRUSTED_KEYS  #关闭系统证书的编译参数
root@josiahserver:~# scripts/config --disable SYSTEM_REVOCATION_KEYS  #关闭系统证书的编译参数
root@josiahserver:~# make menuconfig     #进入编译选项菜单
root@josiahserver:~# make -j4            #使用4线程编译
root@josiahserver:~# make modules_install  #安装模块
root@josiahserver:~# make install          #安装内核,安装结束后重启服务器

# 执行提权,我们这里可以看到,user namespace(用户命名空间),network namespace(网络命名空间)及 nftables是必须的参数,其他一些的话按照作者介绍都是绕过一些内核机制,可以理解成提权辅助吧。 
root@josiahserver:~# modprobe nf_tables  #启用nftables内核模块
root@josiahserver:~# su wxy
$ ./exploit
[*] creating user namespace (CLONE_NEWUSER)...
[*] creating network namespace (CLONE_NEWNET)...
[*] setting up UID namespace...
[*] configuring localhost in namespace...
[*] setting up nftables...
[+] running normal privesc
[*] waiting for the calm before the storm...
[*] sending double free buffer packet...
[*] spraying 16000 pte's...
[*] checking 16000 sprayed pte'
for overlap...
[+] confirmed double alloc PMD/PTE
[!] failed to find kernel code segment... CONFIG_STATIC_USERMODEHELPER disabled?
$ id
uid=1001(wxy) gid=1001(wxy) groups=1001(wxy)

# 切换至root 查看内核编译参数,m是编译成内核模块,编译安装重启系统后可以进行modprobe加载模块,y是编译在内核中的,无需额外加载模块。
root@josiahserver:~# grep -i nf_tables /boot/config-6.3.13
CONFIG_NF_TABLES=m
CONFIG_NF_TABLES_INET=y
CONFIG_NF_TABLES_NETDEV=y
CONFIG_NF_TABLES_IPV4=y
CONFIG_NF_TABLES_ARP=y
CONFIG_NF_TABLES_IPV6=y
CONFIG_NF_TABLES_BRIDGE=m
root@josiahserver:~# grep -i CONFIG_STATIC_USERMODEHELPER /boot/config-6.3.13
CONFIG_STATIC_USERMODEHELPER=y
CONFIG_STATIC_USERMODEHELPER_PATH="/sbin/usermode-helper"

项目样例: 以下是用现成的kvm镜像验证提权。但是该方式由于都是在KernelCTF环境中使用syzkaller内核fuzz工具。内核模块都是直接编译到内核中,无法实现指定模块关闭,来论证个人的理解。

  1. 克隆项目 kernel-exploit-factory
  2. 进入目录 cd kernel-exploit-factory/CVE-2024-1086
  3. 查看README下载镜像到当前目录
  4. 运行 ./start.sh启动并进入测试的虚机。
root@syzkaller:~# su hi
$ id
uid=1000(hi) gid=1000(hi) groups=1000(hi)
$ ./exploit
[*] creating user namespace (CLONE_NEWUSER)...
[*] creating network namespace (CLONE_NEWNET)...
[*] setting up UID namespace...
[*] configuring localhost in namespace...
[*] setting up nftables...
[+] running normal privesc
[*] waiting for the calm before the storm...
[*] sending double free buffer packet...
[*] spraying 16000 pte's...
[*] checking 16000 sprayed pte'
for overlap...
[+] confirmed double alloc PMD/PTE
[+] found possible physical kernel base: 0000000214000000
[+] verified modprobe_path/usermodehelper_path: 0000000215933e33 ('/sbin/usermode-helper')...
[*] overwriting path with PIDs in range 0->4194304...
# id  #提权成功
uid=0(root) gid=0(root) groups=0(root)

成功样例: 翻看CVE-2024-1086漏洞的作者代码[3],在项目中作者使用的linux内核版本是6.1.72,我也就直接去ubuntu官网下载内核包[4]用dpkg安装软件包方式来升级内核(编译安装花的时间实在太长,那就直接安装人家编译好的)。

# 安装6.1.72内核
root@josiahserver:~# wget https://kernel.ubuntu.com/mainline/v6.1.72/amd64/linux-image-unsigned-6.1.72-060172-generic_6.1.72-060172.202401101633_amd64.deb
root@josiahserver:~# wget https://kernel.ubuntu.com/mainline/v6.1.72/amd64/linux-modules-6.1.72-060172-generic_6.1.72-060172.202401101633_amd64.deb`
root@josiahserver:~# wget https://kernel.ubuntu.com/mainline/v6.1.72/amd64/linux-headers-6.1.72-060172-generic_6.1.72-060172.202401101633_amd64.deb
root@josiahserver:~# dpkg --install *.deb
root@josiahserver:~# reboot

# 编译CVE2024-1086提权exploit
root@josiahserver:~# git clone https://github.com/Notselwyn/CVE-2024-1086
root@josiahserver:~# cd CVE-2024-1086
root@josiahserver:~# make
# 复制到普通用户目录并授权,切换账户后运行提权成功。

实验论证1:关闭nf_tables模块实现漏洞封堵

root@josiahserver:~# echo "blacklist nf_tables" >>/etc/modprobe.d/nf_tables-blacklist.conf
root@josiahserver:~# update-initramfs -u -k 6.1.72-060172-generic #跟新指定内核的启动模块
root@josiahserver:~# reboot
root@josiahserver:~# lsmod |grep nf_tables #查看内核模块无输出说明已禁用

# 实验验证
root@josiahserver:~# su wxy
$ ./exploit
[*] creating user namespace (CLONE_NEWUSER)...
[*] creating network namespace (CLONE_NEWNET)...
[*] setting up UID namespace...
[*] configuring localhost in namespace...
[*] setting up nftables...
mnl_cb_run: Invalid argument   #发现已无法进行nftables的内核调用

实验论证2:关闭用户命名空间实现漏洞封堵

root@josiahserver:~# echo "user.max_user_namespaces=0" > /etc/sysctl.d/userns.conf;sysctl -p /etc/sysctl.d/userns.conf
user.max_user_namespaces = 0
root@josiahserver:~
root@josiahserver:~# lsmod |grep nf_tables
nf_tables             294912  15 nft_chain_nat
nfnetlink              20480  3 nf_conntrack_netlink,nf_tables
libcrc32c              16384  5 nf_conntrack,nf_nat,btrfs,nf_tables,raid456

# 实验验证
root@josiahserver:~# su wxy
$ ./exploit
[*] creating user namespace (CLONE_NEWUSER)...
unshare(CLONE_NEWUSER): No space left on device #无法创建用户命名空间

# 用root账户查看docker情况
root@josiahserver:~# cat /lib/systemd/system/docker.service
...
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --userns-remap=wxy #默认docker中使用的是root用户权限,我们设置使用docker权限映射,这样代表容器root用户映射到宿主机docker指定的用户uid上。这里的区别详见下面<<docker用户命名空间>>
...
root@josiahserver:~# docker run --name nginx-test3 -p 8082:80 -d nginx
32290608b5b5ff8d65e7c95a7844801205f2691d0b580fcd73997b9ab6481284
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: can't get final child's PID from pipe: EOF: unknown.  #这里由于无法创建用户命名空间导致启动容器失败。

docker用户命名空间:重新开启用户命名空间

root@josiahserver:~# cat /etc/subuid
wxy:165536:65536  #用户wxy 在当前的 user namespace 中具有65536个从属用户。这些从属用户被映射成ID为0-65535的用户

root@josiahserver:~# docker exec -it nginx-test3 bash  #进入容器
root@32290608b5b5:/# cat /proc/self/uid_map
         0     165536      65536   #0代表容器中当前账户UID,165536是宿主机中对应账户的UID,说明容器中root用户对应的是宿主机uid 165536 的用户
root@32290608b5b5:/# ls -l /proc/self/ns/user  #查看容器进程所在哪个命名空间
lrwxrwxrwx 1 root root 0 Jun 12 13:42 /proc/self/ns/user -> 'user:[4026532655]'

个人总结

虽然这次论证失败,但还是有点收获。发现现在的内核编译都支持只编译修改部分,大大缩短编译时间。如果整个内核编译的话4线程也得编译1个小时样子。也就是如果有内核编译参数修改,只需要重新再make 一次,而不需要``make clean`.另外编译新内核时候不要使用其他人分享的内核的config编译参数文件,可能会直接系统开不了机,尽量使用内核自带的config编辑。个人对C语言和内核工作原理和内核编译参数的认知不足,也是一开始论证失败的原因。后续还得多去了解了解。

从实验可以发现,关闭用户命名空间对容器以及k8s的影响非常大,直接会导致服务不可用。关闭nf_tables模块只要使用的还是iptables基本就没啥影响,只是新的类似于iptables防火墙功能的nft无法使用而已。毕竟在Linux3.10内核中还没nf_tables内核模块,iptables不还是用的好好地么。


公众号二维码
公众号二维码
个人博客二维码
个人博客二维码

客官,欢迎光临本站!路途遥远,常进来看看!

参考资料
[1]

作者漏洞介绍: https://pwning.tech/nftables/

[2]

kernel-exploit-factory漏洞复现项目: https://github.com/bsauce/kernel-exploit-factory.git

[3]

作者提权代码: https://github.com/Notselwyn/CVE-2024-1086

[4]

ubuntu内核官网: https://kernel.ubuntu.com/mainline/v6.1.72/

本文由 mdnice 多平台发布

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值