据说在脚本中依赖 $0 是危险的(经常引用来伪造它:exec -a ...)。是的,在我有写访问权限的目录中,我可以符号链接到脚本来伪造其 $0。但是如何使 $0 指向我没有写访问权限的路径(例如下面的 /bin/pwd )?
下面,假设我的脚本使用了 shebang,我就无法实现它。当目标脚本使用shebang时可以完成吗?
$ (exec -a /bin/pwd /tmp/a0.sh)
no-shebang: argv0=/bin/pwd /proc/.../cmdline=/bin/bash^@--noediting^@-i^@
$ (exec -a /bin/pwd /tmp/a1.sh)
yes-shebang: argv0=/tmp/a1.sh /proc/.../cmdline=/bin/sh^@/tmp/a1.sh^@
$ head /tmp/a0.sh /tmp/a1.sh
==> /tmp/a0.sh <==
echo no-shebang: argv0="$0" /proc/.../cmdline=$(cat -v /proc/$$/cmdline)
==> /tmp/a1.sh <==
#!/bin/sh
echo yes-shebang: argv0="$0" /proc/.../cmdline=$(cat -v /proc/$$/cmdline)
也就是说,如果一个脚本(及其父目录)是写保护的,并且使用了 shebang,那么它对 $0 的使用是安全的(不会伪造另一个写保护路径)?
这里使用(在 centos 7 上)bash 的 exec 语句作为 $0 的简单伪造。使用 execve() 的 C 程序不会有所不同吗?是未能在 execve() 下游(在内核或 shebang-target 中)伪造它,而不是 bash 的 exec 太弱了?
编辑:C 中的 execve() 也不会伪造 shebang 情况下的 $0,输出与上面相同:
#include <stdio.h>
int main(void) {
char* argv[] = { "/bin/pwd", NULL };
execve("/tmp/a1.sh", argv, NULL);
perror(NULL);
}
我知道如果用户获得 root 权限,那么假 $0 就不是你的问题了。但是我的写保护脚本(使用 shebang)不能被非 root 用户伪造 $0(到另一个写保护路径): $0 的危险(“只需使用 exec -a ...”)似乎是错误的。
答案1
出色地,手册execve(2)
页说:
解释器脚本
解释器脚本是一个启用了执行权限的文本文件,其第一行的形式为:
#!interpreter [optional-arg]
解释器必须是可执行文件的有效路径名。
如果 execve() 的路径名参数指定解释器
脚本,则将使用以下
参数调用解释器:interpreter [optional-arg] pathname arg...
因此,当您运行 时exec -a /bin/pwd /tmp/a1.sh
,系统会运行/bin/sh /tmp/a1.sh
,并且当这样启动时,shell$0
根据脚本文件的名称进行设置。
shell 获取的值argv[0]
不会出现,例如exec -a foobar /bin/sh /tmp/a1.sh
仍会显示/tmp/a1.sh
在 中$0
,并且从 的内容/proc/.../cmdline
来看,运行脚本时设置的值无论如何都会丢失。
但这并不意味着脚本看到的值$0
不能被伪造:用户可以创建到脚本的符号链接并通过链接调用它,或者执行以下操作:
/bin/sh -c ". /tmp/a1.sh" foobar
这将设置$0
为foobar
,然后简单地源到/tmp/a1.sh
.