文件系统级别的 SETUID

SETUID at filesystem level

假设我们正在查看以下场景:

文件saymyname.c(包括省略)

int main(int argc, char** argv){
    system("whoami");   
}

构建并设置权限位:

cake@lie> gcc saymyname.c -o saymyname
cake@lie> sudo chown root:root saymyname
cake@lie> sudo chmod u+s saymyname
cake@lie> ./saymyname
cake

天底下的所有资源都告诉我,在用户列上设置 s 权限应该使程序以所有者的权限执行,而不是调用用户的权限。为什么 system("whoami"); return cake?

修改程序手动设置UID如下:

int main(int argc, char** argv){
    setuid(geteuid());
    system("whoami");   
}

产生预期结果

cake@lie> ./saymyname
root

一些资源声称 SUID 和 GUID 位经常被忽略。这就是观察到的行为发生的原因吗?如果是这样,有没有办法让它表现得好像是由没有 setuid(.) 的 root 执行的?

看起来像 bash,由 system() 执行,删除了权限。在我的测试中,替换指向破折号的符号 link /bin/sh(而不是 bash)使其按预期工作。

还有bash,

execl("/bin/bash", "bash", "-c", "whoami", NULL);

送蛋糕,而

execl("/usr/bin/whoami", "whoami", NULL);

给根。

在技术上是正确的 1,但值得一提的是 system(3) 手册页明确指出使用 system() 不建议在 setuid 程序中使用:

Do not use system() from a program with set-user-ID or set-group-ID privileges, because strange values for some environment variables might be used to subvert system integrity. Use the exec(3) family of functions instead, but not execlp(3) or execvp(3). system() will not, in fact, work properly from programs with set-user-ID or set-group-ID privileges on systems on which /bin/sh is bash version [>=]2, since bash 2 drops privileges on startup. (Debian uses a modified bash which does not do this when invoked as sh.)

这在您的示例中尤其重要,因为您在没有完整路径的情况下调用 whoami。想象一下以下场景(作为非特权用户):

> whoami cat << 'EOF'
#!/bin/not-bash :)
echo "I'm root! let's clean up some trash ..."
# rm -rf /
EOF
chmod +x whoami

PATH="${PWD}" ./saymyname

这意味着,不要更改系统 shell(或使用 Debian),代码应该只使用 exec(),像这样:

int main(int argc, char** argv){
    execl("/usr/bin/whoami", "whoami", NULL);   
}

1 newer versions of dash on Ubuntu will also drop privileges