“运行任何将不可信数据传递给将参数解释为命令的命令的命令”

Tim*_*Tim 18 shell

来自 findutils 的手册:

例如,像这两个命令这样的结构

# risky
find -exec sh -c "something {}" \;
find -execdir sh -c "something {}" \;
Run Code Online (Sandbox Code Playgroud)

非常危险。这样做的原因是“{}”被扩展为一个文件名,其中可能包含分号或 shell 特有的其他字符。例如,如果有人创建了该文件, /tmp/foo; rm -rf $HOME那么上面的两个命令可能会删除某人的主目录。

因此,出于这个原因,不要运行任何会将不受信任的数据(例如文件名)传递给将参数解释为要进一步解释的命令的命令(例如“sh”)的命令。

在 shell 的情况下,这个问题有一个巧妙的解决方法:

# safer
find -exec sh -c 'something "$@"' sh {} \;
find -execdir sh -c 'something "$@"' sh {} \;
Run Code Online (Sandbox Code Playgroud)

这种方法不能保证避免所有问题,但它比将攻击者选择的数据替换到 shell 命令的文本中要安全得多。

  1. 问题的原因是否find -exec sh -c "something {}" \;在于替换为{}未加引号并因此不被视为单个字符串?
  2. 在解决方案中find -exec sh -c 'something "$@"' sh {} \;

    • first{}被替换了,但是因为没有{}被引用,是不是"$@"也和原来的命令有同样的问题?例如, "$@"将扩大至"/tmp/foo;""rm""-rf",和 "$HOME"

    • 为什么{}没有转义或引用?

  3. 您能否举出其他示例(sh -c如果适用,仍然有或没有它;有或没有find可能没有必要),其中适用相同类型的问题和解决方案,哪些是最小的例子,以便我们可以专注于问题和解决方案尽可能少分心?请参阅为由`bash -c` 执行的命令提供参数的方法

谢谢。

Ste*_*itt 29

这实际上与引用无关,而是与参数处理有关。

考虑一个危险的例子:

find -exec sh -c "something {}" \;
Run Code Online (Sandbox Code Playgroud)
  • 这由外壳程序解析,并分成六个词:find, -exec, sh, -c, something {}(不再有引号), ;. 没有什么可以扩展的。shellfind以这六个词作为参数运行。

  • find认定的东西的过程,也就是说foo; rm -rf $HOME,它会替换{}foo; rm -rf $HOME,并运行sh的论据sh-csomething foo; rm -rf $HOME

  • sh现在看到-c,并作为结果解析something foo; rm -rf $HOME第一个非选项参数)并执行结果。

现在考虑更安全的变体:

find -exec sh -c 'something "$@"' sh {} \;
Run Code Online (Sandbox Code Playgroud)
  • shell 运行时find带有参数find, -exec, sh, -c, something "$@", sh, {}, ;

  • 现在,当findfinds 时foo; rm -rf $HOME,它{}再次替换,并sh使用参数sh, -c, something "$@", sh, 运行foo; rm -rf $HOME

  • sh看到-c,并解析something "$@"作为命令来运行,并shfoo; rm -rf $HOME作为位置参数(从开始$0),扩大"$@"foo; rm -rf $HOME 作为一个单一的值,并运行 something与单个参数foo; rm -rf $HOME

您可以使用printf. 创建一个新目录,输入它,然后运行

touch "hello; echo pwned"
Run Code Online (Sandbox Code Playgroud)

运行第一个变体如下

find -exec sh -c "printf \"Argument: %s\n\" {}" \;
Run Code Online (Sandbox Code Playgroud)

产生

Argument: .
Argument: ./hello
pwned
Run Code Online (Sandbox Code Playgroud)

而第二个变体,运行为

find -exec sh -c 'printf "Argument: %s\n" "$@"' sh {} \;
Run Code Online (Sandbox Code Playgroud)

产生

Argument: .
Argument: ./hello; echo pwned
Run Code Online (Sandbox Code Playgroud)

  • 我的意思是,这对于一般的贝壳来说是不正确的。参见 [POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html#tag_20_47_16)。`findutils` 手册中的那个特别声明至少可以追溯到 1996 年......当然,引用 `{}` 没有坏处,无论是 `'{}'` 还是 `"{}"`,但它不是没必要。 (3认同)