21

Using eval is often discouraged because it allows execution of arbitrary code. However, if we use eval echo, then it looks like the rest of the string will become arguments of echo so it should be safe. Am I correct on this?

Braiam
  • 35,991
Cyker
  • 4,274
  • 7
  • 36
  • 46
  • 1
    not always safe you could wrap a fork bomb or nasty rm -fR * – μολὼν.λαβέ Dec 25 '16 at 21:39
  • Maybe this is just a thought experiment, but if you were actually thinking of doing this to pass it multiple arguments like -n, you can just do it with an unquoted variable like echo $arguments or if $arguments is an array echo "${arguments[@]}". Using eval echo is pointless even if it were safe. – JoL Dec 25 '16 at 23:03

4 Answers4

41

Counterexample:

DANGEROUS=">foo"
eval echo $DANGEROUS

The arbitrary arguments to echo could have done something more nefarious than creating a file called "foo".

Celada
  • 44,132
27

@Celada has provided an excellent answer. To demonstrate eval is really evil, here's something more nefarious than creating a file called "foo":

DANGEROUS='$(rm foo)'
eval echo "$DANGEROUS"

And of course there can be something more nefarious than something more nefarious than creating a file called "foo".

Cyker
  • 4,274
  • 7
  • 36
  • 46
  • 9
    +1 for demonstrating that quoting the variable like "$THIS" instead of just having it like $THIS doesn't even help! – Celada Dec 24 '16 at 22:34
  • Sending an aditional pair of quotes seems to help. Something like eval echo '"'"$DANGEROUS"'"'. Try it on https://goo.gl/L2pPQP – Ismael Miguel Dec 26 '16 at 15:25
  • Actually, your example isn't any more nefarious than >foo, because "creating a file called 'foo'" isn't necessarily all that >foo does. The only real difference your example has is that it doesn't leave an empty file behind. The contents are still gone. – Sparkette Apr 19 '18 at 15:35
12

No, it is not always safe. An eval could execute any command.

A safe command, like this (the date is not executed as it is inside single quotes):

$ echo '$(date)'
$(date)

Becomes dangerous if used with eval:

$ eval echo '$(date)'
Sat Dec 24 22:55:55 UTC 2016

Of course, date could be any command.

One way to improve this is to additionally quote the arguments to eval:

$ eval echo '\$(date)'
$(date)

But it is usually difficult to correctly quote twice an expression.

And it becomes impossible to control the correct quoting if the expression could be set by an external attacker, like:

$ var='$(date);echo Hello!'
$ eval echo "$var"
Sat Dec 24 23:01:48 UTC 2016
Hello!
1

While it is true that eval always needs to be approached with caution, the eval echo construction is not always pointless and can be used safely. I recently needed it to get multiple brace expansions evaluated in the order I needed them done.

bash does multiple brace expansions from left to right, so

xargs -I_ cat _/{11..15}/{8..5}.jpg

expands to

xargs -I_ cat _/11/8.jpg _/11/7.jpg _/11/6.jpg _/11/5.jpg _/12/8.jpg _/12/7.jpg _/12/6.jpg _/12/5.jpg _/13/8.jpg _/13/7.jpg _/13/6.jpg _/13/5.jpg _/14/8.jpg _/14/7.jpg _/14/6.jpg _/14/5.jpg _/15/8.jpg _/15/7.jpg _/15/6.jpg _/15/5.jpg

but I needed the second brace expansion done first, yielding

xargs -I_ cat _/11/8.jpg _/12/8.jpg _/13/8.jpg _/14/8.jpg _/15/8.jpg _/11/7.jpg _/12/7.jpg _/13/7.jpg _/14/7.jpg _/15/7.jpg _/11/6.jpg _/12/6.jpg _/13/6.jpg _/14/6.jpg _/15/6.jpg _/11/5.jpg _/12/5.jpg _/13/5.jpg _/14/5.jpg _/15/5.jpg

The best I could come up with to do that was

xargs -I_ cat $(eval echo _/'{11..15}'/{8..5}.jpg)

This works because the single quotes protect the first set of braces from expansion during the parsing of the eval command line, leaving them to be expanded by the subshell invoked by eval.

There may be some cunning scheme involving nested brace expansions that allows this to happen in one step, but if there is I'm too old and stupid to see it. There are also shells other than bash that allow for tidier ways of achieving this kind of thing. But in any case, this use of eval is safe because its arguments are all fixed strings that contain no parameter expansions.

  • You don't need echo and the command substitution here (which also has a dependency on $IFS). You could do eval xargs -I_ cat _/'{11..15}'/{8..5}.jpg – Stéphane Chazelas Jan 05 '18 at 10:51
  • That works too, but it makes the subshell process spawned by eval stick around until the xargs process is finished; the eval echo version makes that subshell go away before xargs is even started. This is probably only important to others who are as anal as me about what shows up in htop tree views and set -x logs though :-) – flabdablet Jan 05 '18 at 16:40