如何理解进程的 no_new_privs 属性?

写在前面

在阅读 seccomp manual 的时候了解到进程的 no_new_privs 属性,man 了一下 prctl 发现帮助文档中的描述很难理解,于是通过一些例子研究了下,摸着些头脑,在本文中记录一下。

no_new_privs 的使用示例

将如下代码保存为 test.c,并编译生成 a.out 文件。

#include <errno.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[]) {

        if (argc < 2) {
                printf("%s argument\n", argv[0]);
                exit(-1);
        }

        if (argc == 3) {
                if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
                        perror("prctl(NO_NEW_PRIVS)");
                        return 1;
                }
        }

        return  system(argv[1]);
}

测试示例

no_new_privs 是否设置对 sudo 程序执行的影响

sudo 命令的权限:

[longyu@debian:09:13:05] tmp $ ls -lh /usr/bin/sudo
-rwsr-xr-x 1 root root 179K Feb 27  2021 /usr/bin/sudo

sudo 命令具有 suid root 权限。

1. 设置 no_new_privs 时运行 sudo

[longyu@debian:09:06:51] tmp $ ./a.out sudo 1
sudo: effective uid is not 0, is /usr/bin/sudo on a file system with the 'nosuid' option set or an NFS file system without root privileges?

无法运行!

2. 未设置 no_new_privs 时运行 sudo

[longyu@debian:09:07:29] tmp $ ./a.out 'sudo su'
[sudo] password for longyu:
root@debian:/tmp#

正常运行!

no_new_privs 是否设置对 ping 程序执行的影响

ping 程序的权限:

[longyu@debian:09:11:09] tmp $ ls -lh /usr/bin/ping
-rwxr-xr-x 1 root root 76K Feb  3  2021 /usr/bin/ping
[longyu@debian:09:11:55] tmp $ sudo getcap /usr/bin/ping
/usr/bin/ping cap_net_raw=ep

ping 没有 suid root 权限,有 cap_net_raw 权限。

1 . 设置 no_new_privs 时运行 ping

[longyu@debian:09:09:42] tmp $ ./a.out 'ping www.baidu.com' 1
ping: socket: Operation not permitted

无法运行!
2. 未设置 no_new_privs 时运行 ping

[longyu@debian:09:09:35] tmp $ ./a.out 'ping www.baidu.com'
PING www.baidu.com (39.156.66.18) 56(84) bytes of data.
64 bytes from 39.156.26.18 (39.156.63.18): icmp_seq=1 ttl=52 time=38.7 ms

正常运行!

单个进程设置 no_new_privs 权限后尝试关闭

将如下代码保存为 test-new.c 并编译生成 a.out 文件:

#include <errno.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[]) {

        if (argc < 2) {
                printf("%s argument\n", argv[0]);
                exit(-1);
        }

        if (argc == 3) {
                if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
                        perror("prctl(NO_NEW_PRIVS)");
                        return 1;
                }
                if (prctl(PR_SET_NO_NEW_PRIVS, 0, 0, 0, 0)) {
                        perror("prctl(NO_NEW_PRIVS)");
                        return 1;
                }
        }

        return  system(argv[1]);
}

测试记录如下:

longyu@debian:/tmp$ ./a.out sudo 1
prctl(NO_NEW_PRIVS): Invalid argument

strace 跟踪得到的关键信息:

prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)  = 0
prctl(PR_SET_NO_NEW_PRIVS, 0, 0, 0, 0)  = -1 EINVAL (Invalid argument)

当进程设置了 no_new_privs 后无法关闭!

子进程中尝试关闭 no_new_privs 配置

将如下代码保存为 test1.c 文件后编译生成 test1。

#include <errno.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[]) {
        if (prctl(PR_SET_NO_NEW_PRIVS, 0, 0, 0, 0)) {
                        perror("set failed");
                        return 1;
        }

        return 0;
}

使用第一个示例中的程序运行此程序,测试结果如下:

longyu@debian:/tmp$ ./a.out ./test1 1
set failed: Invalid argument

当父进程设置了 no_new_privs 的权限后,子进程无法关闭。

manual 中的描述信息

man prctl 中得到如下信息:

PR_SET_NO_NEW_PRIVS (since Linux 3.5)
              Set the calling thread's no_new_privs attribute to the value in arg2.  With no_new_privs set to 1, execve(2) prom‐
              ises not to grant privileges to do anything that could not have been done without the execve(2) call (for example,
              rendering the set-user-ID and set-group-ID mode bits,  and  file  capabilities  non-functional).   Once  set,  the
              no_new_privs attribute cannot be unset.  The setting of this attribute is inherited by children created by fork(2)
              and clone(2), and preserved across execve(2).

              Since Linux 4.10, the value of a thread's no_new_privs attribute can be viewed via the  NoNewPrivs  field  in  the
              /proc/[pid]/status file.

              For  more  information,  see  the  kernel  source file Documentation/userspace-api/no_new_privs.rst (or Documenta‐
              tion/prctl/no_new_privs.txt before Linux 4.13).  See also seccomp(2).

上述信息表明当设置了 no_new_privs 标志后,execve 会保证不会赋予运行进程新的权限, suid、sgid 权限会失效并且文件特殊权限也会失效,这意味着 execve 运行的新程序不会拥有比原进程更高的权限。

no_new_privs 标志一旦设置就不能撤销,使用 fork 与 clone 创建的子进程会继承父进程的此标志,且在 execve 的时候也会保留。

这里需要理解的点在于 execve 那些设置了 suid、sgid bit、Linux capabilities 的可执行文件时,未设置 no_new_privs 属性时,进程将会拥有比原进程更高的权限,no_new_privs 可以理解为是对 execve 提权操作的限制,在一些场景中能够增强系统的安全性。

参考链接

https://segmentfault.com/a/1190000041968184?utm_source=sf-similar-article

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值