无新特权标志¶
execve 系统调用可以授予新启动的程序其父进程不具有的特权。最明显的例子是 setuid/setgid 程序和文件能力。为了防止父进程也获得这些特权,内核和用户代码必须小心防止父进程执行任何可能破坏子进程的操作。例如:
如果程序是 setuid,动态加载器会以不同的方式处理
LD_*
环境变量。chroot 不允许非特权进程使用,因为它允许从继承 chroot 的进程的角度替换
/etc/passwd
。exec 代码对 ptrace 有特殊处理。
这些都是临时性的修复。no_new_privs
位(自 Linux 3.5 起)是一种新的通用机制,使进程可以安全地修改其执行环境,该环境可以在 execve 中保持不变。任何任务都可以设置 no_new_privs
。一旦设置了该位,它就会在 fork、clone 和 execve 中被继承,并且无法取消设置。设置 no_new_privs
后,execve()
保证不会授予执行任何没有 execve 调用就无法完成的操作的特权。例如,setuid 和 setgid 位将不再更改 uid 或 gid;文件能力不会添加到允许的集合中,并且 LSM 在 execve 之后不会放松约束。
要设置 no_new_privs
,请使用:
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
但请注意:LSM 也可能不会在 no_new_privs
模式下收紧 exec 的约束。(这意味着在执行守护进程之前设置 no_new_privs
来设置通用服务启动器可能会干扰基于 LSM 的沙箱。)
请注意,no_new_privs
不会阻止不涉及 execve()
的特权更改。具有适当特权的任务仍然可以调用 setuid(2)
并接收 SCM_RIGHTS 数据报。
到目前为止,no_new_privs
有两个主要用例:
为 seccomp 模式 2 沙箱安装的过滤器会在 execve 中保持不变,并且可以更改新执行的程序的行为。因此,只有在设置了
no_new_privs
的情况下,才允许非特权用户安装此类过滤器。就其本身而言,
no_new_privs
可以用来减少非特权用户可用的攻击面。如果以给定 uid 运行的所有内容都设置了no_new_privs
,则该 uid 将无法通过直接攻击 setuid、setgid 和使用 fcap 的二进制文件来提升其特权;它需要先破坏一些没有设置no_new_privs
位的东西。
将来,如果设置了 no_new_privs
,其他潜在危险的内核功能可能会对非特权任务可用。原则上,当设置 no_new_privs
时,对 unshare(2)
和 clone(2)
的几个选项是安全的,并且 no_new_privs
+ chroot
比 chroot 本身要危险得多。