No New Privileges Flag¶
execve 系统调用可以授予新启动的程序其父进程没有的特权。最明显的例子是 setuid/setgid 程序和文件 capabilities。为了防止父程序也获得这些特权,内核和用户代码必须小心,防止父程序做任何可能破坏子程序的事情。例如
如果程序是 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;文件 capabilities 不会添加到允许的集合中,并且 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 本身危险性小得多。