From findutils' manual:
For example constructs such as these two commands
# risky find -exec sh -c "something {}" \; find -execdir sh -c "something {}" \;
are very dangerous. The reason for this is that the ‘{}’ is expanded
to a filename which might contain a semicolon or other characters
special to the shell. If for example someone creates the file
/tmp/foo; rm -rf $HOME
then the two commands above could delete
someone’s home directory.So for this reason do not run any command which will pass untrusted
data (such as the names of fi les) to commands which interpret
arguments as commands to be further interpreted (for example ‘sh’).In the case of the shell, there is a clever workaround for this
problem:# safer find -exec sh -c 'something "$@"' sh {} \; find -execdir sh -c 'something "$@"' sh {} \;
This approach is not guaranteed to avoid every problem, but it is much
safer than substituting data of an attacker’s choice into the text of
a shell command.
- Is the cause of the problem in
find -exec sh -c "something {}" \;
that the replacement for{}
is
unquoted and therefore not treated as a single string? -
In the solution
find -exec sh -c 'something "$@"' sh {} \;
,-
first
{}
is replaced, but since{}
is unquoted, doesn't"$@"
also have the same problem as the original command? For example,
"$@"
will be expanded to"/tmp/foo;"
,"rm"
,"-rf"
, and
"$HOME"
? -
why is
{}
not escaped or quoted?
-
- Could you give other examples (still with
sh -c
, or without it if
applicable; with or withoutfind
which may be not necessary) where the same kind of problem and solution apply, and
which are minimal examples so that we can focus on the problem and
solution with little distraction as possible? See Ways to provide arguments to a command executed by `bash -c`
Thanks.
Best Answer
This isn’t really related to quoting, but rather to argument processing.
Consider the risky example:
This is parsed by the shell, and split into six words:
find
,-exec
,sh
,-c
,something {}
(no quotes any more),;
. There’s nothing to expand. The shell runsfind
with those six words as arguments.When
find
finds something to process, sayfoo; rm -rf $HOME
, it replaces{}
withfoo; rm -rf $HOME
, and runssh
with the argumentssh
,-c
, andsomething foo; rm -rf $HOME
.sh
now sees-c
, and as a result parsessomething foo; rm -rf $HOME
(the first non-option argument) and executes the result.Now consider the safer variant:
The shell runs
find
with the argumentsfind
,-exec
,sh
,-c
,something "$@"
,sh
,{}
,;
.Now when
find
findsfoo; rm -rf $HOME
, it replaces{}
again, and runssh
with the argumentssh
,-c
,something "$@"
,sh
,foo; rm -rf $HOME
.sh
sees-c
, and parsessomething "$@"
as the command to run, andsh
andfoo; rm -rf $HOME
as the positional parameters (starting from$0
), expands"$@"
tofoo; rm -rf $HOME
as a single value, and runssomething
with the single argumentfoo; rm -rf $HOME
.You can see this by using
printf
. Create a new directory, enter it, and runRunning the first variant as follows
produces
whereas the second variant, run as
produces