15

I have seen advice in several places to use the following shebang line

#!/usr/bin/env bash

instead of

#!/usr/bin/bash

My knee-jerk reaction is, "what if somebody substitutes this executable for their own in say ~/.local/bin ?" That directory is often set up in the user's path before the system-wide paths. I see this raised as a security issue often as a side note rather than anything to take seriously, but I wanted to test the theory.

To try this out I did something like this:

echo -e "#!/usr/bin/python\nprint 'Hacked!'" > $HOME/.local/bin/bash
chmod 755 $HOME/.local/bin/bash
PATH=$HOME/.local/bin env bash

This yields

/usr/bin/env: ‘bash’: No such file or directory

To check whether it was picking up anything at all I also did

echo -e "#!/usr/bin/python\nprint 'Hacked!'" > $HOME/.local/bin/perl
chmod 755 $HOME/.local/bin/perl
PATH=$HOME/.local/bin env perl

which prints, as I expected,

Hacked!

Can someone explain to me why the substitute bash is not found, but the substitute perl is? Is this some sort of "security" measure that (from my point of view) misses the point?

EDIT: Because I have been prompted: I am not asking how /usr/bin/env bash is different from using /bin/bash. I am asking the question as stated above.

EDIT2: It must have been something I was doing wrong. Tried again today (using explicit path to env instead of implicit), and no such "not found" behaviour.

8
  • 1
    I'm wondering which env program your command is picking up, since /usr/bin is not in PATH anymore. I suspect bash's hashing mechanism is at play here. Bash remembers the full pathnames of commands. See help hash for more info. Commented Apr 10, 2017 at 12:30
  • amended the examples. I did mean $HOME/.local/bin and not $HOME/bin Commented Apr 10, 2017 at 13:22
  • 2
    @taifwa: Your examples with bash and perl work identically for me, except that I get env: command not found so I have to use PATH=$HOME/bin /usr/bin/env bash. Commented Apr 10, 2017 at 16:21
  • @Christopher no it is not. Question edited to highlight the actual ask. Commented Apr 10, 2017 at 23:33
  • 2
    I'm unable to reproduce your bash test. I need to give the full path to env because of the explicit PATH setting, and when I do, the command prints Hacked!. Commented Apr 11, 2017 at 4:48

3 Answers 3

17

"what if somebody substitutes this executable for their own in say ~/.local/bin ?

Then the script doesn't work for them.

But that doesn't matter, since they could conceivably break the script for themselves in other ways, or run another program directly without messing with PATH or env.

Unless your users have other users' directories in their PATH, or can edit the PATH of other users, there's really no possibility of one user messing another one.


However, if it wasn't a shell script, but something that grants additional privilege, such as a setuid wrapper for some program, then things would be different. In that case, it would be necessary to use an absolute path to run the program, place it in a directory the unprivileged users cannot modify, and clean up the environment when starting the program.

6
  • on the one hand I intended to mean "what if someone maliciously substitutes the executable", but I see your point. And if someone maliciously got in there are so many other issues that this point becomes moot.... Commented Apr 10, 2017 at 13:25
  • 5
    @taifwa, yep, if you can put something on my path, you can just put a modified ls there. I'll probably run it quite soon. Commented Apr 10, 2017 at 19:52
  • 1
    "However, if you have a setuid wrapper for a script or another program" - If setuid is involved, then generally speaking a shebang is not involved, at least on most modern Unices. Setuid does nothing on shebanged scripts. Commented Apr 10, 2017 at 21:14
  • 1
    @Kevin, yeah, I meant that a shell script isn't problematic in this since it can't have any more privilege than the running user. Commented Apr 10, 2017 at 21:24
  • Oh and I remember why I asked this in the first place. If a script with setuid or setgid uses this env technique, I can get root access that way. So the caveat really is: don't let scripts with an env shebang have set*id Commented Apr 11, 2017 at 13:28
4

As for the difference in behavior between the shell and perl, perl unlike the shell inspects the first line to determine what to run:

$ cat tcl
#!/usr/bin/env tclsh
puts "TCL running as [pid]"
$ perl tcl
TCL running as 39689
$ cat sbcl
#!/opt/local/bin/sbcl --script
(format t "~a running as ~a~%" 'lisp (sb-unix:unix-getpid))
$ perl sbcl
LISP running as 39766
$ 

Uses of this feature include writing tests in some other language besides perl so one can have TAP-compatible scripts in other languages and a prove or whatnot will run them correctly.

2
  • That's an interesting tidbit about Perl, but if the setup in the question is working as one might naively expect, the system perl should never be run at all, so its interpretation of shebang lines shouldn't be relevant here. The test is set up so that the only file named perl that env finds is the one in $HOME/.local/bin. Running that file should run (only) python, shouldn't it? Commented Apr 11, 2017 at 4:56
  • @RobKennedy if env manages to somehow run perl (it does on Mac OS X, but not Linux for me...), then the exec-other-program feature of perl will come into play. This is not so if env manages to run instead the shell (unless that shell has a similar feature...). Commented Apr 11, 2017 at 14:11
1

There's no security risk here. /usr/bin/env is supposed to select the appropriate binary according to the user's environment. So if the user has installed their own bash in ~/.local/bin, then /usr/bin/env should indeed attempt to use it (they may, for example, have compiled a version with extra features that aren't available in the system-wide version, and would prefer to use it in place of the system-wide version). The same goes for any other binary/interpreter. There's no security risk, because the user won't be able to run anything that they wouldn't have been able to run anyway.

As for why the substitute is failing in your case, I doubt it has anything to do with /usr/bin/env. Try running PATH=~/.local/bin bash, PATH=~/.local/bin bash <path-to-script> (as it would be called when you run the script) and PATH=~/.local/bin /usr/bin/env bash and see which of those fail. That should give a clue as to what the "file not found" error is referring to.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.