谁为只执行的文件运行解释器?

谁为只执行的文件运行解释器?

如果当前用户对文件仅具有执行(--x)权限,则解释器(由#!/path/to/interpreter文件开头指定)在哪个用户下运行?

它不可能是当前用户,因为他没有读取该文件的权限。它不可能是 root,因为解释器中包含的任意代码都将获得 root 访问权限。

那么,解释器进程以哪个用户身份运行呢?

编辑:我认为我的问题假设该文件已经被充分读取以了解它指定的解释器,而实际上它不会走那么远。当前 shell(通常是 b/a/sh)解释执行目标文件的命令将尝试读取它,但会失败。

答案1

如果用户对可执行脚本没有读取权限,则尝试运行它将失败,除非她有CAP_DAC_OVERRIDE能力(例如她是root):

$ cat > yup; chmod 100 yup
#! /bin/sh
echo yup
^D
$ ./yup
/bin/sh: 0: Can't open ./yup

解释器(无论失败还是成功)将始终以当前用户身份运行,忽略脚本的任何 setuid 位或 setcap 扩展属性。

可执行脚本与二进制文件的不同之处在于,解释器应该能够打开和读取它们才能运行它们。但是,请注意,它们只是作为参数传递给解释器,解释器可能根本不会尝试读取它们,而是执行完全不同的操作:

$ cat > interp; chmod 755 interp
#! /bin/sh
printf 'you said %s\n' "$1"
^D
$ cat > script; chmod 100 script
#! ./interp
nothing to see here
^D
$ ./script
you said ./script

当然,翻译员本身可能是 setuid 或cap_dac_override=ep-setcap 二进制文件(或将脚本的路径作为参数传递给此类二进制文件),在这种情况下,它将以提升的权限运行,并且可以忽略任何文件权限。

Linux 上通过 binfmt_misc 无法读取的 setuid 脚本

在 Linux 上,您可以使用以下模块绕过可执行脚本的所有限制(并破坏您的系统;-))binfmt_misc

以 root 身份:

# echo ':interp-test:M::#! ./interp::./interp:C' \
    > /proc/sys/fs/binfmt_misc/register

# cat > /tmp/script <<'EOT'; chmod 4001 /tmp/script # just exec + setuid
#! ./interp
id -u
EOT

作为普通用户:

$ echo 'int main(void){ dup2(getauxval(AT_EXECFD), 0); execl("/bin/sh", "sh", "-p", (void*)0); }' |
    cc -include sys/auxv.h -include unistd.h -x c - -o ./interp
$ /tmp/script
0

雅皮士!

更多信息请参见Documentation/admin-guide/binfmt-misc.rst在内核源代码中。

-p选项可能会导致某些 shell 出现错误(可以简单地删除该选项),但新版本的dash和需要该选项,bash以防止它们放弃特权即使没有要求。

答案2

执行脚本分两个阶段。首先,内核读取文件的开头并发现它以 开头#!,因此它读取舍邦行并确定要调用哪个解释器。然后,内核将原始命令行转换为解释器的路径、shebang 行上的选项(如果有)、文件的路径和原始选项。然后它执行此命令行,就像这一直是命令行一样,但不进行任何进一步的 shebang 处理。

到目前为止,内核已检查调用者是否具有对该脚本文件的执行权限。读取权限尚未发挥作用。内核在执行文件的过程中读取文件的开头,因此这是由执行权限控制的,而不是由读取权限控制。

在第二阶段,解释器被执行。由于它在命令行上看到脚本文件名,因此它可能会尝试打开它。此时需要读取权限。如果解释器没有读取该文件的权限,则其open调用将失败,然后解释器可能会打印一条错误消息并放弃。我说“大概”是因为这是每个明智的解释者所做的,但没有技术义务要求事情以这种方式发生。如果您在 shebang 行上使用的程序不是解释器,则在第一阶段不会有任何区别,但该程序将在第二阶段执行其操作。例如,以 开头的“脚本”#!/bin/echo将仅打印脚本文件名和任何其他命令行参数,然后退出,并且该脚本只需为此可执行。

1许多内核,包括 Linux,只允许一种选择。

答案3

一般来说,即使您拥有该文件,没有“r”权限也无法执行脚本

$ ls -l tst
---x--x--x 1 sweh sweh 24 May  4 21:22 tst*

$ ./tst
/bin/bash: ./tst: Permission denied

$ sudo cat tst
#!/bin/bash

echo hello

根据您编辑的问题。

程序的这一#!部分被内核解释为exec()系统调用的一部分。因此,要达到这一点,脚本不需要可读。

在我的示例中,实际上发生的是内核将 my 转换./tst/bin/bash ./tst调用。

这种转换解释了为什么脚本需要r访问才能被处理,但内核只需要x确定要使用的解释器。

相关内容