不可执行 mfd 的介绍¶
- 作者:
Daniel Verkamp <dverkamp@chromium.org> Jeff Xu <jeffxu@chromium.org>
- 贡献者:
Aleksa Sarai <cyphar@cyphar.com>
自从 Linux 引入 memfd 功能以来,memfds 始终设置了执行位,并且 memfd_create() 系统调用不允许以不同的方式设置它。
然而,在默认安全的系统(例如 ChromeOS,其中所有可执行文件都应来自受验证启动保护的 rootfs)中,memfd 的这种可执行性质为 NoExec 绕过打开了一扇门,并启用了“混淆副手攻击”。例如,在 VRP 错误 [1] 中:cros_vm 进程创建了一个 memfd 来与外部进程共享内容,但是该 memfd 被覆盖并用于执行任意代码和 root 提权。[2] 列出了更多此类 VRP。
另一方面,可执行的 memfd 有其合法的用途:runc 使用 memfd 的密封和可执行功能来复制二进制文件的内容,然后执行它们。对于这样的系统,我们需要一种解决方案来区分 runc 对可执行 memfd 的使用和攻击者的 [3]。
- 为了解决上述问题
让 memfd_create() 在创建时设置 X 位。
当设置 NX 时,让 memfd 被密封以修改 X 位。
添加一个新的 pid 命名空间 sysctl:vm.memfd_noexec,以帮助应用程序迁移和强制执行不可执行的 MFD。
用户 API¶
int memfd_create(const char *name, unsigned int flags)
MFD_NOEXEC_SEAL
当在
flags
中设置 MFD_NOEXEC_SEAL 位时,将创建带有 NX 的 memfd。设置 F_SEAL_EXEC,并且不能修改 memfd 以稍后添加 X。MFD_ALLOW_SEALING 也被隐含。这是应用程序使用 memfd 的最常见情况。MFD_EXEC
当在
flags
中设置 MFD_EXEC 位时,将创建带有 X 的 memfd。- 注意
MFD_NOEXEC_SEAL
隐含MFD_ALLOW_SEALING
。如果应用程序不想要密封,可以在创建后添加 F_SEAL_SEAL。
Sysctl:¶
pid 命名空间 sysctl vm.memfd_noexec
新的 pid 命名空间 sysctl vm.memfd_noexec 有 3 个值
- 0: MEMFD_NOEXEC_SCOPE_EXEC
没有 MFD_EXEC 或 MFD_NOEXEC_SEAL 的 memfd_create() 的行为就像设置了 MFD_EXEC 一样。
- 1: MEMFD_NOEXEC_SCOPE_NOEXEC_SEAL
没有 MFD_EXEC 或 MFD_NOEXEC_SEAL 的 memfd_create() 的行为就像设置了 MFD_NOEXEC_SEAL 一样。
- 2: MEMFD_NOEXEC_SCOPE_NOEXEC_ENFORCED
将拒绝没有 MFD_NOEXEC_SEAL 的 memfd_create()。
该 sysctl 允许更精细地控制不设置可执行位的旧软件的 memfd_create;例如,vm.memfd_noexec=1 的容器意味着旧软件默认将创建不可执行的 memfd,而新软件可以通过设置 MFD_EXEC 来创建可执行的 memfd。
vm.memfd_noexec 的值在创建时传递给子命名空间。此外,该设置是分层的,即在 memfd_create 期间,我们将从当前 ns 搜索到根 ns,并使用最严格的设置。
[2] https://bugs.chromium.org/p/chromium/issues/list?q=type%3Dbug-security%20memfd%20escalation&can=1