前言
Linux Security Modules(LSM)是一个钩子的基于框架,用于在Linux内核中实现安全策略和强制访问控制。直到现在,能够实现实施安全策略目标的方式只有两种选择,配置现有的LSM模块(如AppArmor、SELinux),或编写自定义内核模块。
Linux Kernel 5.7引入了第三种方式: LSM扩展伯克利包过滤器(eBPF)(简称BPF LSM)。LSM BPF允许开发人员编写自定义策略,而无需配置或加载内核模块。LSM BPF程序在加载时被验证,然后在调用路径中,到达LSM钩子时被执行。
实践出真知
Namespaces 命名空间
现代操作系统提供了允许对内核资源进行partitioning
的工具。例如FreeBSD有jails
,Solaris有zones
。Linux不一样,提供了一组看似独立的工具,每个进程都允许隔离特定的资源。他就是Namespaces
,经过多年来不停迭代,孕育了Docker
、lxc
、firejail
应用。大部分Namespaces
是没有争议的,如UTS命名空间,它允许主机系统隐藏主机名和时间。其他的则比较复杂但简单明了————众所周知,NET和NS(mount)命名空间很难让人理解。最后,还有一个非常特殊、非常有趣的USER Namespaces
。
USER Namespaces
很特别,因为它允许所有者作为root
操作。其工作原理超出了本文的范围,但是,可以说它是让Docker
等工具不作为真正的root操作,或者说是rootless
容器。
由于其特性,允许未授权用户访问USER Namespaces
总是会带来很大的安全风险。其中最大的风险是提权
。
提权原理
提权
是操作系统的常见攻击面。user获得权限的一种方法是通过unshare syscall将其命名空间映射到root
空间,并指定CLONE_NEWUSER
标志。这会告诉unshare
创建一个具有完全权限的新用户命名空间,并将新用户和Group ID映射到以前的命名空间。即使用unshare(1)程序将root映射到原始命名空间:
$ id
uid=1000(fred) gid=1000(fred) groups=1000(fred) …
$ unshare -rU
# id
uid=0(root) gid=0(root) groups=0(root),65534(nogroup)
# cat /proc/self/uid_map
0 1000 1
多数情况下,使用unshare
是没有风险的,都是以较低的权限运行。但是,已经被用于提权了,比如CVE-2022-0492,那么本文就重点以这个场景为例。
Syscalls clone
和clone3
也很值得研究,都有CLONE_NEWUSER
的功能。但在这篇文章中,我们将重点关注unshare
。
Debian用add sysctl to disallow unprivileged CLONE_NEWUSER by default[8]补丁解决了这个问题,但它没有被合并到源码mainline主线中。另一个类似的补丁sysctl: allow CLONE_NEWUSER to be disabled 尝试合并到mainline,但被拒绝了。理由是在某些特定应用中无法切换到该特性。在Controlling access to user namespaces一文中,作者写道:
... 目前的补丁似乎没有一条通往mainline主线的捷径。
如你所示,补丁最终没有包含到vanilla内核中。