命令的信息页面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
, --
,code
1 在子进程中。作为一种安全措施,一些 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 不会放弃其特权并信任其环境。所以这是一个非常糟糕的主意。您至少希望在sudo
setuid 可执行文件调用sh
as之前执行诸如之类的命令所做的所有清理工作root
。
例如,这里使用zsh
which 不会执行删除操作,并且支持更改内置 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 版起。