漏洞简介
CVE-2018-10000281是存在于Linux内核NFS子系统的访问控制错误漏洞,允许远程用户通过NFS读写权限范围外的文件,CVSS 3.x评分为7.4(高危)。漏洞利用需要确保目标NFS服务器对外暴露的文件系统启用了rootsquash
选项2。
Root squash is a special mapping that maps remote root user (uid 0) to local “nobody” user (uid 65534), which has minimal privileges.
漏洞分析
漏洞的补丁3非常简单,内容如下:
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
index f650e475d8f0d8..fdf2aad7347090 100644
--- a/fs/nfsd/auth.c
+++ b/fs/nfsd/auth.c
@@ -60,10 +60,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
gi->gid[i] = exp->ex_anon_gid;
else
gi->gid[i] = rqgi->gid[i];
-
- /* Each thread allocates its own gi, no race */
- groups_sort(gi);
}
+
+ /* Each thread allocates its own gi, no race */
+ groups_sort(gi);
} else {
gi = get_group_info(rqgi);
}
结合rootsquash
选项的含义来分析补丁,不难理解漏洞成因。开发者的本意是先通过一个for循环将客户端传过来的所有等于root的GID替换为一个低权限GID,然后执行一次groups_sort
来对收到的所有GID排序。漏洞代码实际上在for循环的每一次迭代都执行了一次groups_sort
,这可能导致传过来的root GID没有被全部替换为低权限GID。
例如,考虑这样一种情况:客户端的user用户的主GID为1000,辅助GID有0(root)。漏洞代码逻辑在处理GID 1000后,执行排序操作将GID 0排到了前面,使其躲过后续的低权限替换处理。这样一来,rootsquash
就被绕过了。漏洞代码片段如下所示:
for (i = 0; i < rqgi->ngroups; i++) {
if (gid_eq(GLOBAL_ROOT_GID, rqgi->gid[i]))
gi->gid[i] = exp->ex_anon_gid;
else
gi->gid[i] = rqgi->gid[i];
/* Each thread allocates its own gi, no race */
groups_sort(gi);
}
环境配置
服务端
首先构建并启动一个带有漏洞的Linux内核虚拟机环境作为服务器,安装NFS server并创建共享目录:
sudo apt install nfs-kernel-server
sudo mkdir -p /var/nfs/general
接着在/etc/exports
配置NFS server并更新记录,使其带root_squash
选项对外暴露:
# /var/nfs/general *(rw,sync,no_subtree_check,root_squash)
sudo vim /etc/exports
sudo exportfs -a
客户端
首先安装NFS client:
sudo apt install nfs-common
接着在客户机上创建一个UID为1000的user用户,除了默认GID为1000的组外,为它添加包含root组在内的额外三个组,例如:
uid=1000(user) gid=1000(user) groups=1000(user),0(root),27(sudo),104(kvm)
在客户机上修改/etc/fstab
文件,使user用户能够挂载NFS(我设置了虚拟机本地端口映射,所以下面命令中使用127.0.0.1作为NFS服务地址):
# 127.0.0.1:/var/nfs/general /home/user/ans nfs defaults,user 0 0
sudo vim /etc/fstab
mkdir -p /home/user/ans
在客户机上创建/home/user/ans
目录,然后连接到NFS server:
mount /home/user/ans
复现调试
在客户机上,首先在ans
目录下多次创建文件,发现文件的GID并不是root,也无法写root创建的文件。
准备通过打印调试信息的方式查找原因。在服务端内核源码文件fs/nfsd/auth.c
中加入一些打印语句4,重新编译内核。重新建立连接并执行前述测试后,从内核日志中发现rqgi->ngroups
变量始终为1。也就是说,客户端user用户的辅助用户组没有被服务端内核fs/nfsd/auth.c
处理。现在需要弄清楚问题在客户端(没有发送所有的GID)还是在服务端(只使用了第一个GID)。
在服务端tcpdump抓包分析发现,客户端发送了user用户的所有GID,如下图所示:
因此,问题出在服务端。查找资料56后发现,rpc.mountd
进程的--manage-gids
选项会在服务端查找GIDs,然后用查找结果替换客户端提供的GIDs。于是,在服务端编辑/etc/default/nfs-kernel-server
文件,移除--manage-gids
选项,并重启NFS服务。
再次测试,成功复现漏洞——NFS目录中有一个只有root用户和root用户组可读的文件hello
,我们使用user用户不断去读这个文件,经过几次尝试后可以读取成功。复现效果如下: