一、漏洞描述
2024年3月28日, Linux kernel权限提升漏洞(CVE-2024-1086)的PoC/EXP在互联网上公开,该漏洞的CVSS评分为7.8,目前漏洞细节已经公开披露,美国网络安全与基础设施安全局(CISA)6月1日也更新了其已知漏洞(KEV)目录,要求联邦机构在 2024 年 6 月 20 日之前打上补丁,修复追踪编号为 CVE-2024-1086 的 Linux 内核权限提升漏洞。CVE-2024-1086 是一个高危 use-after-free 漏洞,于 2024 年 1 月 31 日首次披露,存在于 netfilter: nf_tables 组件中,相关漏洞代码于 2014 年 2 月的一项提交并入。攻击者可利用该漏洞可在本地进行提权操作,最高可获取目标服务器的root管理权限。该漏洞于2024年3月28日被公开披露,并在互联网上出现了相关的概念验证(PoC)和攻击工具。由于该漏洞的公开性,攻击者可能会利用这个漏洞进行恶意攻击,因此建议尽快采取修复措施。
漏洞原理:Linux内核版本v5.14 – v6.6的netfilter 子系统nf_tables组件中存在释放后使用漏洞,由于在nft_verdict_init()函数中,允许正值作为hook判决中的丢弃错误,因此当 “nf_hook_slow ()” 函数在NF_DROP一起发出类似于NF_ACCEPT的丢弃错误时,nf_hook_slow()函数可能会导致双重释放漏洞,本地低权限威胁者可利用该漏洞将权限提升为root。Netfilter 是 Linux 内核提供的一个框架,允许进行各种与网络有关的操作,如数据包过滤、网络地址转换(NAT)和数据包混淆。
影响范围: 该漏洞影响了使用Linux内核版本v5.14 – v6.6 版本(不包括分支修补版本 v5.15.149、v6.1.76和v6.6.15)的大多数Linux系统/内核:
3.15<= Linux kernel < 6.1.76
5.2<= Linux kernel < 6.6.15
6.7<= Linux kernel < 6.7.3
6.8:rc1 = Linux kernel
修复建议:
- 升级Linux内核版本修复漏洞。Linux 多个稳定版目前已经修复,参考如下:
v5.4.269 及更高版本;v5.10.210 及更高版本;v6.6.15 及更高版本;v4.19.307 及更高版本;v6.1.76 及更高版本;v5.15.149 及更高版本;v6.7.3 及更高版本- 若相关用户暂时无法进行更新,如果业务不需要,可以通过阻止加载受影响的 netfilter (nf_tables) 内核模块来缓解。
- 如果无法禁用该模块,可在非容器化部署中,对用户命名空间进行限制。
关联资源:Notselwyn公告、nftables、How do I prevent a kernel module from loading automatically?、How to modify the kernel command-line in Red Hat Enterprise Linux 9、内核官网、openeuler安全中心、统信公告、麒麟公告、龙蜥公告及修复、华为欧拉、src-openEuler、nvd.nist
二、复现验证及修复
1、漏洞复现过程(来源Github)
2、复现脚本
复现脚本exploit可检测内核v5.14 到(including) v6.6版,完成权限提升复现,但不包括v5.15.149>, v6.1.76>, v6.6.15>这些分支,它们已经完成补丁修复;
git clone https://github.com/Notselwyn/CVE-2024-1086
Cloning into 'CVE-2024-1086'...
remote: Enumerating objects: 2196, done.
remote: Counting objects: 100% (169/169), done.
remote: Compressing objects: 100% (88/88), done.
remote: Total 2196 (delta 87), reused 158 (delta 77), pack-reused 2027
Receiving objects: 100% (2196/2196), 2.10 MiB | 4.42 MiB/s, done.
Resolving deltas: 100% (762/762), done.
#安装
cd CVE-2024-1086
make #报错
musl-gcc -I./include -I./include/linux-lts-6.1.72 -Wall -Wno-deprecated-declarations src/main.c src/env.c src/net.c src/nftnl.c src/file.c -o ./exploit -static ./lib/libnftnl.a ./lib/libmnl.a
make: musl-gcc: No such file or directory
make: *** [Makefile:25: _compile_static] Error 127
#安装
yum install musl-gcc musl-devel -y
CVE-2024-1086/exploit #完成后会生成二进制文件
./exploit #输出如下
[ygcg@zq-fdfs-25 CVE-2024-1086]$ ./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: 00000006f1600000
[+] verified modprobe_path/usermodehelper_path: 00000006f2e56580 ('/sanitycheck')...
[*] overwriting path with PIDs in range 0->4194304...
[!] verified modprobe_path address does not work... CONFIG_STATIC_USERMODEHELPER enabled? #执行完就跳出了,并未继续越权成功,可能对于内核:Linux 5.10.0-60.70.0.94.oe2203 并不管用
#对于内核版本 > 6.4,还需要检查内核配置 CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y时,用该漏洞不影响
zcat /proc/config.gz|grep CONFIG_INIT_ON_ALLOC #检查发现输出如下
\# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set
#检查LInux是否启用了CONFIG_USER_NS和CONFIG_NF_TABLES
zcat /proc/config.gz|grep CONFIG_USER_NS #输出如下
CONFIG_USER_NS=y
zcat /proc/config.gz|grep CONFIG_NF_TABLES #输出如下
#=y:表示该功能被编译进内核中,始终可用。
#=m:表示该功能被编译为模块,可以根据需要加载或卸载。
#=n:表示该功能被禁用,不会编译进内核或作为模块。
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
#启用nf_tables模块
modprobe nf_tables
#永久加载这个模块,可以将其添加到 /etc/modules-load.d/ 目录下的某个文件中:
echo nf_tables | sudo tee /etc/modules-load.d/nf_tables.conf #或写入modules.conf
#查看
lsmod|grep -i nf_tables
nf_tables 274432 25 #其中,274432:这是模块的大小,单位是字节。表示 nf_tables 模块占用了大约 274432 字节的内存空间。25表示有25个其他模块依赖于 nf_tables 模块。该行表示nf_tables 模块已加载
nfnetlink 20480 3 nf_conntrack_netlink,nf_tables
libcrc32c 16384 3 nf_conntrack,nf_nat,nf_tables
#查看模块是否属于LInux启动初始化映像文件系统加载的一部分。一般不是
lsinitrd /boot/initramfs-$(uname -r).img|grep nf_tables.ko
3、现场处理
现场使用的是云能的Euler V22.10 U2版 ,它基于**openEuler 22.03 LTS SP2**版本发布,内核版本:Linux 5.10.0-60.70.0.94.oe2203;现场用poc脚本测试并未越权成功,但是据网络相关经验表明,openEuler 22.03 LTS的内核5.10.0-60.18.0.50.oe2203.x86_64版本是可以越权成功的,案例中内核升级到了5.10.0-60.139.0.166.oe2203.x86_64,比现场版本高;现场内核验证结果如下所示:
#可用内核版本
yum check-update |grep kernel-* #或yum check-update kernel,yum list available kernel
kernel.x86_64 5.10.0-136.49.0.127.10.oe2203.bclinux update
kernel-devel.x86_64 5.10.0-136.49.0.127.10.oe2203.bclinux update
kernel-headers.x86_64 5.10.0-136.49.0.127.10.oe2203.bclinux update
kernel-tools.x86_64 5.10.0-136.49.0.127.10.oe2203.bclinux update
#或
yum list kernel --showduplicates
#查看可用补丁
yum search linux-kernel
#非容器化部署中,可以通过将 user.max_user_namespaces 设置为 0 来禁用用户命名空间
echo "user.max_user_namespaces=0" > /etc/sysctl.d/userns.conf
sysctl -w kernel.unprivileged_userns_clone=0 #禁用非特权用户创建命名空间的能力
sysctl -p /etc/sysctl.d/userns.conf
#禁止nf_tables模块
vi /etc/modprobe.d/nf_tables-blacklist.conf #新增如下内容:
blacklist nf_tables
#验证
insmod nf_tables.ko.xz #加载/usr/lib/modules/5.10.0-60.70.0.94.oe2203.bclinux.x86_64/kernel/net/netfilter/nf_tables.ko.xz
#卸载已加载的目标模块
modprobe -r module_name
echo "blacklist nf_tables" >> /etc/modprobe.d/nf_tables-blacklist.conf ##同上,将模块加入blacklist,避免自动再次载入
echo "install nf_tables /bin/false" >> /etc/modprobe.d/nf_tables-blacklist.conf #这样,当OS下次尝试加载模块时,将执行/bin/false;这将防止模块按需加载
cp /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.$(date +%m-%d-%H%M%S).bak #备份 initramfs(Initial RAM Filesystem),如果目标模块属于initramfs的一部分,需rebuild initramfs,执行如下
dracut --omit-drivers module_name -f #重建
#持久化配置
MODNAME="module_name"; echo "omit_dracutmodules+=\" $MODNAME \"" >> /etc/dracut.conf.d/omit-$MODNAME.conf #不需要的时候,删除该文件即可
grubby --info ALL (To view changes in all available kernels)
内核升级方式:内核的更换主要有三重方式,第一种是下载源码进行编译;第二种是通过添加内核源,然后使用yum进行安装;第三种是通过下载rpm包,然后通过rpm来进行安装。推荐第三中方式。
#查看内核启动顺序
awk -F\' '$1=="menuentry " {print $2}' /etc/grub2.cfg #启动顺序默认降序排列,从0开始
grub2-set-default 0 #设置最新的内核为默认启动
现场欧拉系统,通过POC测试脚本暂无法确定,官网查看,详情如下:
受影响版本:
1.openEuler-20.03-LTS-SP1(4.19.90):受影响
2.openEuler-20.03-LTS-SP3(4.19.90):受影响
3.openEuler-20.03-LTS-SP4(4.19.90):受影响
4.openEuler-22.03-LTS(5.10.0):受影响
5.openEuler-22.03-LTS-SP1(5.10.0):受影响
6.openEuler-22.03-LTS-SP2(5.10.0):受影响
7.openEuler-22.03-LTS-SP3(5.10.0):受影响
8.openEuler-22.03-LTS-Next(5.10.0):受影响
9.master(6.1.0):不受影响
漏洞修复:penEuler-HotPatchSA-2024-1008、官方补丁
补丁说明:
//net/netfilter/nf_tables_api.c
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 0d0b76a5ddfaa9..f586e8b3c6cfac 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9340,16 +9340,10 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
data->verdict.code = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE]));
switch (data->verdict.code) {
- default:
- switch (data->verdict.code & NF_VERDICT_MASK) {
- case NF_ACCEPT:
- case NF_DROP:
- case NF_QUEUE:
- break;
- default:
- return -EINVAL;
- }
- fallthrough;
+ case NF_ACCEPT:
+ case NF_DROP:
+ case NF_QUEUE:
+ break;
case NFT_CONTINUE:
case NFT_BREAK:
case NFT_RETURN:
@@ -9384,6 +9378,8 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
data->verdict.chain = chain;
break;
+ default:
+ return -EINVAL;
}
desc->len = sizeof(data->verdict);
现场处理:
# 新增openEuler-22.03-LTS-SP2 repo源
vi /etc/yum.repos.d/openEuler-22.03-LTS-SP2.repo
[SP2_OS]
name=SP2_OS
baseurl=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/OS/$basearch/
metalink=https://mirrors.openeuler.org/metalink?repo=$releasever/OS&arch=$basearch
metadata_expire=1h
enabled=1
gpgcheck=1
gpgkey=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/OS/$basearch/RPM-GPG-KEY-openEuler
[SP2_everything]
name=SP2_everything
baseurl=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/everything/$basearch/
metalink=https://mirrors.openeuler.org/metalink?repo=$releasever/everything&arch=$basearch
metadata_expire=1h
enabled=1
gpgcheck=1
gpgkey=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/everything/$basearch/RPM-GPG-KEY-openEuler
[SP2_EPOL]
name=EPOL
baseurl=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/EPOL/main/$basearch/
metalink=https://mirrors.openeuler.org/metalink?repo=$releasever/EPOL/main&arch=$basearch
metadata_expire=1h
enabled=1
gpgcheck=1
gpgkey=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/OS/$basearch/RPM-GPG-KEY-openEuler
[SP2_debuginfo]
name=debuginfo
baseurl=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/debuginfo/$basearch/
metalink=https://mirrors.openeuler.org/metalink?repo=$releasever/debuginfo&arch=$basearch
metadata_expire=1h
enabled=1
gpgcheck=1
gpgkey=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/debuginfo/$basearch/RPM-GPG-KEY-openEuler
[SP2_source]
name=source
baseurl=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/source/
metalink=https://mirrors.openeuler.org/metalink?repo=$releasever&arch=source
metadata_expire=1h
enabled=1
gpgcheck=1
gpgkey=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/source/RPM-GPG-KEY-openEuler
[SP2_update]
name=SP2_update
baseurl=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/update/$basearch/
metalink=https://mirrors.openeuler.org/metalink?repo=$releasever/update&arch=$basearch
metadata_expire=1h
enabled=1
gpgcheck=1
gpgkey=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/OS/$basearch/RPM-GPG-KEY-openEuler
[SP2_update-source]
name=SP2_update-source
baseurl=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/update/source/
metalink=https://mirrors.openeuler.org/metalink?repo=$releasever/update&arch=source
metadata_expire=1h
enabled=1
gpgcheck=1
gpgkey=http://repo.openeuler.org/openEuler-22.03-LTS-SP2/source/RPM-GPG-KEY-openEuler
#参考上问yum升级内核到:kernel-5.10.0-153.43.0.121.oe2203sp2.x86_64
yum --disablerepo=\* --enablerepo=SP2_update list kernel --showduplicate|grep 5.10.0-153.43.0.121 #输出
kernel.x86_64 5.10.0-153.43.0.121.oe2203sp2 SP2_update
#升级
yum --disablerepo=\* --enablerepo=SP2_update --nogpgcheck update kernel-5.10.0-153.43.0.121.oe2203sp2.x86_64
#验证
awk -F\' '$1=="menuentry " {print $2}' /etc/grub2.cfg #输出如下
BigCloud Enterprise Linux (5.10.0-153.43.0.121.oe2203sp2.x86_64) 22.10 LTS
BigCloud Enterprise Linux (5.10.0-60.70.0.94.oe2203.bclinux.x86_64) 22.10 LTS
#设置第一个为默认,实际不设置也是优先第一个
grub2-set-default 0
grub2-editenv list #输出,这里也证明默认的启动一变成新的内核版本,与cat /etc/default/grub的GRUB_DEFAULT对应
saved_entry=BigCloud Enterprise Linux (5.10.0-153.43.0.121.oe2203sp2.x86_64) 22.10 LTS
boot_success=0
#备份旧镜像,之后就会找到新镜像来启动
mv /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.bak #备份呢当前启动镜像
reboot