如何让命令 id 在 Ubuntu 中打印不同的真实有效的 UID

如何让命令 id 在 Ubuntu 中打印不同的真实有效的 UID

命令的信息页面id表明,如果与真实用户ID不同,它将输出有效用户ID。我一直在尝试以某种方式实现这一目标,在 Ubuntu 中以我的普通(非特权)用户身份使用 Bash shell 运行命令。例如,我尝试了@Asain Kujovic 答案中的示例这里但它不起作用,我没有得到euid任何输出。这是例子:

osbo@osboxes:~/t$ sudo gcc -o test.bin -xc - <<EOF
     #include <stdlib.h>
     #include <unistd.h>
     int main() { seteuid(0); system("id"); }
EOF
osbo@osboxes:~/t$ sudo chmod +s test.bin && ./test.bin && sudo rm test.bin
uid=1000(osbo) gid=1000(osboxes) groups=1000(osboxes),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),113(bluetooth),119(scanner)

编辑
我是 Linux 的初学者,所以欢迎初学者友好的答案,如果这对于我提出的问题来说是可能的。我知道什么是 shell 程序(如 Bash 或 sh)、什么是环境变量、什么是 C 程序以及什么是系统调用(例如getuid和 )geteuid。我还知道什么是真实的、有效的和保存的用户/组 ID,与用户生成的进程相关。然而,我对父子进程场景或strace命令知之甚少。

答案1

system("code")实际上是sh 带着参数运行的sh, -c, --,code1 在子进程中。作为一种安全措施,一些 shell 的作用相当于:

if (geteuid() != (uid = getuid())
  setuid(uid);

也就是说,如果有效 uid 与真实 uid 不同,它们会将有效 uid 恢复为真实 uid。

在编写 setuid 可执行文件时(如果可能的话,首先应该避免这样做),您需要确保以提升的权限运行的代码部分尽可能短且受到严格控制,以避免引入权限提升漏洞。

从 setuid 可执行文件运行 shell 是您最不想做的事情,因为编写安全的 shell 脚本几乎是不可能的。因此,这些 shell 通过在启动时尽早删除这些特权来帮助实现这一点,这有助于编写糟糕的 setuid 命令,这些命令本身无法做到这一点。

这里来自 zsh,在 sh 基于 dash 的 Ubuntu 系统上:

$ sudo install -m 4750 -o root -g "$GID" =env .
$ ls -ld env
-rwsr-x--- 1 root stephane 43352 Dec  7 16:12 env*
$ sudo strace -u "$USERNAME" -ze '/uid|execve' ./env sh -c ''
execve("./env", ["./env", "sh", "-c", ""], 0x7ffde6ddf108 /* 17 vars */) = 0
execve("/usr/bin/sh", ["sh", "-c", ""], 0x7fff075efcd0 /* 17 vars */) = 0
getuid()                                = 10031
geteuid()                               = 0
geteuid()                               = 0
setuid(10031)                           = 0
geteuid()                               = 10031
+++ exited with 0 +++

一些执行此操作的 shell 通常可以-p选择禁用它,但这在使用system().然后他们不会放弃他们的特权,但仍然更加小心,不要太信任他们的环境。

$ sudo strace -u "$USERNAME" -ze '/uid|execve' ./env sh -pc ''
execve("./env", ["./env", "sh", "-pc", ""], 0x7ffd4616dfb8 /* 17 vars */) = 0
execve("/usr/bin/sh", ["sh", "-pc", ""], 0x7ffef3883230 /* 17 vars */) = 0
getuid()                                = 10031
geteuid()                               = 0
+++ exited with 0 +++

您在这里可以做的是setuid(0)在调用之前执行此操作system(),这样真实和有效的 uid 都将为 0,但这意味着您的 shell 不会放弃其特权并信任其环境。所以这是一个非常糟糕的主意。您至少希望在sudosetuid 可执行文件调用shas之前执行诸如之类的命令所做的所有清理工作root

例如,这里使用zshwhich 不会执行删除操作,并且支持更改内置 uids 来执行以下操作setuid()

$ sudo strace -u "$USERNAME" -ze '/uid|execve' ./env zsh -c 'UID=0; exec "$@"' zsh sh -c 'id -u'
execve("./env", ["./env", "zsh", "-c", "UID=0; exec \"$@\"", "zsh", "sh", "-c", "id -u"], 0x7ffc6de6e5e8 /* 17 vars */) = 0
execve("/usr/bin/zsh", ["zsh", "-c", "UID=0; exec \"$@\"", "zsh", "sh", "-c", "id -u"], 0x7ffda351af70 /* 17 vars */) = 0
getuid()                                = 10031
geteuid()                               = 0
getuid()                                = 10031
setuid(0)                               = 0
execve("/usr/bin/sh", ["sh", "-c", "id -u"], 0x562a89149d20 /* 21 vars */) = 0
getuid()                                = 0
geteuid()                               = 0
geteuid()                               = 0
0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=47576, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

如果您确实需要在子进程中的 setuid 可执行文件中运行单独的可执行文件,您可以执行相应system()操作(在子进程中 fork、exec,阻止某些信号并在父进程中等待),但至少跳过sh.您希望提供可执行文件的完整路径,而不是依赖于查找$PATH,并且您可能需要进行一些环境清理。


1 事实上,通常情况下,它是用sh, -c,code,这意味着对于 的现代实现sh,如果code-以或开头+。现在可能会有更多的实现发生变化POSIX 强制执行自标准第 8 版起。

相关内容