Size: 6687
Comment: more on declare
|
Size: 6689
Comment: needs more === around Query
|
Deletions are marked like this. | Additions are marked like this. |
Line 145: | Line 145: |
== Query == | === Query === |
Eval command and security issues
Examples of bad use of eval
"eval" is a common misspelling of "evil". The section dealing with spaces in file names used to include the following quote "helpful tool (which is probably not as safe as the \0 technique)", end quote.
Syntax : nasty_find_all [path] [command] <maxdepth>
#This code is evil and must never be used export IFS=" " [ -z "$3" ] && set -- "$1" "$2" 1 FILES=`find "$1" -maxdepth "$3" -type f -printf "\"%p\" "` #warning, evilness eval FILES=($FILES) for ((I=0; I < ${#FILES[@]}; I++)) do eval "$2 \"${FILES[I]}\"" done unset IFS
This script is supposed to recursively search for files with newlines and/or spaces in them, arguing that find -print0 | xargs -0 was unsuitable for some purposes such as multiple commands. It was followed by an instructional description on all the lines involved, which we'll skip.
To its defense, it works:
$ ls -lR .: total 8 drwxr-xr-x 2 vidar users 4096 Nov 12 21:51 dir with spaces -rwxr-xr-x 1 vidar users 248 Nov 12 21:50 nasty_find_all ./dir with spaces: total 0 -rw-r--r-- 1 vidar users 0 Nov 12 21:51 file?with newlines $ ./nasty_find_all . echo 3 ./nasty_find_all ./dir with spaces/file with newlines $
But consider this:
$ touch "\"); ls -l $'\x2F'; #"
You just created a file called "); ls -l $'\x2F'; #
Now FILES will contain ""); ls -l $'\x2F'; #. When we do eval FILES=($FILES), it becomes
FILES=(""); ls -l $'\x2F'; #"
Which becomes the two statements FILES=(""); and ls -l / . Congratulations, you just allowed execution of arbitrary commands.
$ touch "\"); ls -l $'\x2F'; #" $ ./nasty_find_all . echo 3 total 1052 -rw-r--r-- 1 root root 1018530 Apr 6 2005 System.map drwxr-xr-x 2 root root 4096 Oct 26 22:05 bin drwxr-xr-x 3 root root 4096 Oct 26 22:05 boot drwxr-xr-x 17 root root 29500 Nov 12 20:52 dev drwxr-xr-x 68 root root 4096 Nov 12 20:54 etc drwxr-xr-x 9 root root 4096 Oct 5 11:37 home drwxr-xr-x 10 root root 4096 Oct 26 22:05 lib drwxr-xr-x 2 root root 4096 Nov 4 00:14 lost+found drwxr-xr-x 6 root root 4096 Nov 4 18:22 mnt drwxr-xr-x 11 root root 4096 Oct 26 22:05 opt dr-xr-xr-x 82 root root 0 Nov 4 00:41 proc drwx------ 26 root root 4096 Oct 26 22:05 root drwxr-xr-x 2 root root 4096 Nov 4 00:34 sbin drwxr-xr-x 9 root root 0 Nov 4 00:41 sys drwxrwxrwt 8 root root 4096 Nov 12 21:55 tmp drwxr-xr-x 15 root root 4096 Oct 26 22:05 usr drwxr-xr-x 13 root root 4096 Oct 26 22:05 var ./nasty_find_all ./dir with spaces/file with newlines ./ $
It doesn't take much imagination to replace ls -l with rm -rf or worse.
One might think these circumstances are obscure, but one should not be tricked by this. All it takes is one malicious user, or perhaps more likely, a benign user who left the terminal unlocked when going to the bathroom, wrote a funny php uploading script that doesn't sanity check file names or who made the same mistake as oneself in allowing arbitrary code execution (now instead of being limited to the www-user, an attacker can use nasty_find_all to traverse chroot jails and/or gain additional privileges), uses an IRC or IM client that's too liberal in the filenames it accepts for file transfers or conversation logs, etc.
Examples of good use of eval
Command eval has other uses especially when creating variables out of blue. Here is an example how to parse command line options that do not take parameters automatically:
# # Create option variables dynamically. Try call: # # sh -x example.sh --verbose --test --debug for i in "$@" do case "$i" in --test|--verbose|--debug) shift # Remove option from command line name=${i#--} # Delete option prefix eval "$name='$name'" # make *new* variable ;; esac done echo "verbose: $verbose" echo "test: $test" echo "debug: $debug"
So, why is this version acceptable? It's acceptable because we have restricted the eval command so that it will only be executed when the input is one of a finite set of known values. Therefore, it can't ever be abused by the user to cause arbitrary command execution -- any input with funny stuff in it wouldn't match one of the three predetermined possible inputs. This variant would not be acceptable:
# Dangerous code. Do not use this. for i in "$@" do case "$i" in --test*|--verbose*|--debug*) shift # Remove option from command line name=${i#--} # Delete option prefix eval "$name='$name'" # make *new* variable ;; esac done
All that's changed is that we attempted to make the previous "good" example (which doesn't do very much) useful in some way, by letting it take things like --test=foo. But look at what this enables:
$ ./foo --test='; ls -l /etc/passwd;x=' -rw-r--r-- 1 root root 943 2007-03-28 12:03 /etc/passwd
Once again: by permitting the eval command to be used on unfiltered user input, we've permitted arbitrary command execution.
Query
Could this not be done better with declare? eg:
for i in "$@" do case "$i" in --test|--verbose|--debug) shift # Remove option from command line name=${i#--} # Delete option prefix declare $name=Yes # set default value ;; --test=*|--verbose=*|--debug=*) shift name=${i#--} value=${name#*=} # value is whatever's after first word and = name=${name%%=*} # restrict name to first word only (even if there's another = in the value) declare $name="$value" # make *new* variable ;; esac done
Note that --name for a default, and --name=value are the required formats.
declare does seem to have some sort of parser magic in it, much like [[ does. Here's a test I performed with bash 3.1.17:
griffon:~$ declare foo=x;date;x=Yes Sun Nov 4 09:36:08 EST 2007 griffon:~$ name='foo=x;date;x' griffon:~$ declare $name=Yes griffon:~$ echo $foo x;date;x=Yes
It appears that, at least in bash, declare is much safer than eval.