如何将写保护脚本的 $0 (第零个参数)伪造为我没有写访问权限的路径?

如何将写保护脚本的 $0 (第零个参数)伪造为我没有写访问权限的路径?

据说在脚本中依赖 $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

这将设置$0foobar,然后简单地源到/tmp/a1.sh.

相关内容