可以安全使用`find -exec sh -c`吗?


29

我试图使用findecho 0为一些文件,但显然这只是作品有sh -c

find /proc/sys/net/ipv6 -name accept_ra -exec sh -c 'echo 0 > {}' \;

但是使用sh -cwith find -exec使我感到非常不安,因为我怀疑引用问题。我摆弄了一下,显然我的怀疑是有道理的:

  • 我的测试设置:

    martin@dogmeat ~ % cd findtest 
    martin@dogmeat ~/findtest % echo one > file\ with\ spaces
    martin@dogmeat ~/findtest % echo two > file\ with\ \'single\ quotes\'
    martin@dogmeat ~/findtest % echo three > file\ with\ \"double\ quotes\"
    martin@dogmeat ~/findtest % ll
    insgesamt 12K
    -rw-rw-r-- 1 martin martin 6 Sep 17 12:01 file with "double quotes"
    -rw-rw-r-- 1 martin martin 4 Sep 17 12:01 file with 'single quotes'
    -rw-rw-r-- 1 martin martin 4 Sep 17 12:01 file with spaces
    
  • 使用no似乎可以find -exec毫无问题sh -c地工作-此处无需引用:

    martin@dogmeat ~ % find findtest -type f -exec cat {} \;
    one
    two
    three
    
  • 但是,当我使用时,sh -c {}似乎需要某种形式的引用:

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c 'cat {}' \;
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: spaces: No such file or directory
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: single quotes: No such file or directory
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: double quotes: No such file or directory
    
  • 只要没有文件名包含双引号,双引号就起作用:

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c 'cat "{}"' \;
    one
    two
    cat: findtest/file with double: No such file or directory
    cat: quotes: No such file or directory
    
  • 只要没有文件名包含单引号,单引号就起作用:

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c "cat '{}'" \;
    one
    cat: findtest/file with single: No such file or directory
    cat: quotes: No such file or directory
    three
    

我还没有找到在所有情况下都适用的解决方案。有什么事我俯瞰,或正在使用sh -cfind -exec固有的不安全?

Answers:


40

永远不要嵌入{}shell代码!这将创建一个命令注入漏洞。注意,对于cat "{}",它不仅对"人物,\`$也是一个问题(例如考虑一个名为./$(reboot)/accept_ra)。

(顺便说一句,某些find实现不允许您这样做,并且当POSIX{}的参数不是靠自己时,POSIX会留下未指定的行为find

在这里,您希望将文件名作为单独的参数传递给sh(不在code参数中),并将sh内联脚本(code参数)传递给使用位置参数来引用它们:

find . -name accept_ra -exec sh -c 'echo 0 > "$1"' sh {} \;

或者,为避免sh每个文件运行一个:

find . -name accept_ra -exec sh -c 'for file do
  echo 0 > "$file"; done' sh {} +

这同样适用于xargs -I{}zshzargs -I{}。不要写:

<list.txt xargs -I {} sh -c'cmd> {}'

find上面相同,这将是一个命令注入漏洞,但是:

<list.txt xargs sh -c 'for file do cmd > "$file"; done' sh

这样做还有一个好处,就是避免sh每个文件运行一个文件,并且在list.txt不包含任何文件的情况下出错。

使用zshzargs,您可能希望使用一个函数而不是调用sh -c

do-it() cmd > $1
zargs ./*.txt -- do-it

请注意,在以上所有示例中,上面的第二个示例sh都放入了内联脚本的中$0。您应该使用相关的有什么(如shfind-sh),不喜欢的东西_---或空字符串,如值$0用于壳的错误信息:

$ find . -name accept_ra -exec sh -c 'echo 0 > "$1"' inline-sh {} \;
inline-sh: ./accept_ra: Permission denied

GNU的parallel工作方式有所不同。有了它,你就不会想用sh -c作为parallel不运行shell已经和尝试,以取代{}在shell的语法正确引述的说法

<list.txt PARALLEL_SHELL=sh parallel 'cmd > {}'

这就是第二个sh似乎是某种占位符,它的作品太多,如果换成_例如-如果你想打电话给bash的内部非常有用:find /tmp -name 'fil*' -exec bash -c 'printf "%q\n" "$1"' _ {} \;。但是有人知道在哪里记录吗?
弗洛里安·菲达

1
@FlorianFida的第一个参数壳变成$0(通常是shell的名称,你需要跳过它在这种情况下,因此不会吃你的正常位置参数一项的文档。-c提到这一点。
伊唐赖斯纳


1
@phk,不在argv[0]这里,仅$0是脚本的。Sven的页面在此不准确,它r不会使shell只能进入受限模式,zsh也不会基于更改模式$0(exec -a rksh ksh -c 'cd /')将运行受限ksh,但不会ksh -c 'cd /' rksh)。
斯特凡Chazelas

1
斯特凡(Stéphane),如果您不介意的话,您的答案是否解释为“为什么不将{}嵌入到Shell代码中”?我虽然找到了所有答案,但发誓我找不到,虽然我发誓我看到了一些您写的关于该主题的内容,但无法回忆起它是答案,聊天中的评论还是在诸如compunix的其他网站上...如果/有时间,您会介意扩展“永不嵌入{}”部分,还是您认为我们应该有专门的Q,例如find与shell代码一起使用-exec sh -c和嵌入的安全隐患{}
don_crissti
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.