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.