如果一个进程从一个 setuid 二进制 forks 开始,并且子进程放弃了特权,那么这个子进程仍然可以信任吗?

If a process started from a setuid binary forks, and the child drops privileges, can the child still be trusted?

假设以下程序是由非 root 用户从 setuid-root 二进制文件执行的:

int main()
{
    if (fork()) {
        /* Parent process */
        int wstatus; wait(&wstatus);
        if (WEXITSTATUS(wstatus) == 0) {
            /* Child process exited with return code indicating success */
            do_something_potentially_dangerous();
            return 0;
        } else {
            /* Child process exited with return code indicating failure */
            puts("Access denied");
            return 1;
        }
    } else {
        /* Child process */
        setuid(getuid());
        /*
        ...
        */
        return do_critical_security_check();  /* let's say this returns 0 if it's safe */
    }
}

setuid 程序通常运行时带有额外的安全措施,以防止启动它的用户篡改其(特权)执行。我只能假设在分叉子进程时会保留此状态。

但是当子进程放弃 root 权限时,它是否也放弃了任何这种保护?除非子进程的代码中存在漏洞,否则非特权用户是否能够干扰 do_critical_security_check(),或者在不应该的情况下强制子进程为 return 0? (假设 kernel.yama.ptrace_scope 设置为 0。)

ptrace 手册页指定它将(我强调的):

  1. Deny access if neither of the following is true:

    • The real, effective, and saved-set user IDs of the target match the caller's user ID, and the real, effective, and saved-set group IDs of the target match the caller's group ID.

    • The caller has the CAP_SYS_PTRACE capability in the user namespace of the target.

因此,如果跟踪器无法使用已保存的 UID,则跟踪进程无法附加和劫持 child 来访问它。

如果 child 不可撤销地删除其 EUID(使用 setresuid()),则跟踪进程可以附加,但没有保存的 UID 可供滥用。