FlawOpen

flawopen.com/Teardowns/cve-2023-2640-ubuntu-overlayfs-privilege-escalation

● CVE-2023-2640 · CVSS 7.8 · 高危
安全研究 · FlawOpen

CVE-2023-2640:Ubuntu OverlayFS 提权

CVE-2023-2640 会绕过特定 Ubuntu 内核构建中的 OverlayFS 权限检查。请核对内核变体并安装供应商修复。

💡 通俗易懂的原理解析 (ELI5)

想象一栋大楼,工作人员会检查访客是否有权在文件上盖官方印章。一个本地快捷路径原本只用于跳过内部维护标签的检查。后续变更却让所有印章请求都经过该路径,包括受限的安全印章。修复恢复了挂载文件系统的 user namespace 中的常规权限检查。

核心概念与专有名词

OverlayFS
OverlayFS 是 Linux 联合文件系统,可呈现 lower 层文件,并将修改写入 upper 层。
Extended attribute (xattr)
扩展属性(xattr)是附加到文件系统对象的元数据。Linux 安全模块和文件 capability 会使用特定 xattr 命名空间。
ovl_do_setxattr()
OverlayFS helper,用于向 upper 层对象写入 xattr。受影响的包装函数过度使用了不执行权限检查的 VFS helper。
Copy-up
OverlayFS 操作:当写入需要可写副本时,将 lower 层对象复制到 upper 层。

框架映射

  • 本指南所述弱点的 CWE 参考: CWE-863

根本原因剖析 (Root Cause)

Ubuntu 的 CVE 记录指出,易受影响配置必须同时包含上游提交 c914c0e27eb0 和 Ubuntu SAUCE 补丁“Skip permission checking for trusted.overlayfs.* xattrs”。该补丁修改 OverlayFS xattr 包装函数,改用 VFS _noperm helper,原意只是跳过内部 trusted.overlayfs.* 元数据的检查。上游包装函数变更后,这个 helper 也用于其他 xattr。因此,本地非特权用户可能在已挂载文件上设置特权扩展属性,并在缺少适当检查的情况下将其复制到 upper 层文件。Canonical 的修复撤销了宽泛的无权限检查路径,并使用 upper mount 的 user namespace 调用 vfs_setxattr()。这是 Ubuntu 内核特有的本地提权问题;是否受影响取决于发行版版本、内核变体以及 namespace 和挂载策略。

攻击执行流程分解

步骤 1

特定 Ubuntu 内核配置

内核必须同时包含 c914c0e27eb0 和跳过 trusted.overlayfs.* xattr 检查的 Ubuntu SAUCE 补丁。仅看上游版本号不能确认是否受影响。

步骤 2

本地用户到达 xattr 路径

本地非特权用户必须能依据主机 namespace 和挂载策略使用受影响的 OverlayFS 路径。Ubuntu 将其描述为本地攻击。

步骤 3

权限绕过扩展到其他 xattr

修改后的包装函数将 xattr 更新交给 _noperm helper,原本该 helper 只用于 OverlayFS 内部元数据。上游改动后,其他 xattr 也可能跳过必要检查。

步骤 4

特权属性进入 upper 对象

Ubuntu 表示,特权扩展属性可能在已挂载文件上设置并复制到 upper 文件,而没有经过必要检查,可能导致本地提权。不同内核和容器配置的可达性不同。

源代码对比:漏洞与安全实现

受影响的 SAUCE 路径在上游变更扩大其适用范围后,会对所有 xattr 调用 __vfs_setxattr_noperm()。修复改用 upper mount 的 user namespace 调用 vfs_setxattr(),恢复常规 VFS 检查。这是 Canonical 补丁的重点代码片段,省略了周边调试日志。

源码摘录: fs/overlayfs/overlayfs.h

存在漏洞的实现
/* 撤销 SAUCE 更改前的 Ubuntu 标准补丁上下文。 */
static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
                                  const char *name, const void *value,
                                  size_t size, int flags)
{
    struct inode *inode = dentry->d_inode;
    int err;

    inode_lock(inode);
    err = __vfs_setxattr_noperm(&init_user_ns, dentry, name,
                                value, size, flags);
    inode_unlock(inode);
    return err;
}
加固后的安全修复
/* Ubuntu 标准修复:恢复执行权限检查的 VFS 包装层。 */
static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
                                  const char *name, const void *value,
                                  size_t size, int flags)
{
    int err = vfs_setxattr(ovl_upper_mnt_userns(ofs), dentry, name,
                           value, size, flags);
    return err;
}

工程与系统安全加固清单

参考来源