1

It's said relying on $0 in a script is dangerous (often citing to fake it: exec -a ...). Yes, in dirs where I have write-access I can symlink to a script to fake its $0. But how to make $0 point to a path where I have no write-access (such as /bin/pwd below)?

Below, provided my script uses a shebang, I cannot achieve it. Can it be done when target script uses a 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)

That is, if a script (& its parent-dirs) are write-protected, AND uses a shebang, then its use of $0 is safe (from faking another write-protected path)?

Here using (on centos 7) bash's exec statement as easy fake of $0. C program using execve() won't be different? Is the failure to fake it downstream of the execve() (in kernel or shebang-target), not that bash's exec is too weak?

EDIT: execve() in C also does NOT fake the $0 in shebang case, same output as above:

#include <stdio.h>
int main(void) {
  char* argv[] = { "/bin/pwd", NULL };
  execve("/tmp/a1.sh", argv, NULL);
  perror(NULL);
}

I'm aware if users gain root, then fake $0 is least of your issues. But MY write-protected scripts, with shebang, cannot be $0 faked (to another write-protected path), by non-root users: the danger of $0 ('just use exec -a ...') seems false.

2
  • A simple C program and the execve system call will let you set argv[0] (where $0 comes from) to anything you like. man execve or linux.die.net/man/2/execve
    – waltinator
    Mar 20, 2022 at 4:05
  • Use sudo cp to copy your executable to a directory, like /usr/local/bin/ that you (as $USER) don't have write access to. Remember to sudo rm your executable at the end of testing.
    – waltinator
    Mar 20, 2022 at 4:10

1 Answer 1

1

Well, the execve(2) man page says:

Interpreter scripts

An interpreter script is a text file that has execute permission enabled and whose first line is of the form:

#!interpreter [optional-arg]

The interpreter must be a valid pathname for an executable file.

If the pathname argument of execve() specifies an interpreter
script, then interpreter will be invoked with the following
arguments:

interpreter [optional-arg] pathname arg...

So, when you run exec -a /bin/pwd /tmp/a1.sh, the system runs /bin/sh /tmp/a1.sh, and when started like that, the shell sets $0 from the name of the script file.

The value the shell gets in argv[0] doesn't come to it, e.g. exec -a foobar /bin/sh /tmp/a1.sh would still show /tmp/a1.sh in $0, and from the contents of /proc/.../cmdline, it looks like the value set when running the script gets lost anyway.

But it doesn't mean the value the script sees in $0 could not be faked: a user can make a symlink to the script and call it through the link, or do something like:

/bin/sh -c ". /tmp/a1.sh" foobar

that would set $0 to foobar, then simply source into /tmp/a1.sh.

1
  • As I said symlink doesn't give power to fake it to another write-protected path (where you have write-access [with or without root] you can make a script to do anything you want). But the source... form: that does it exactly, works to fake it to /bin/pwd without root or write-access to /bin! (And "just use exec -a" or power of execve() doesn't enter into it).
    – bk-se
    Mar 21, 2022 at 15:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .