$ touch '"; echo world "'
$ find . -exec sh -c 'ls -l "$@"' sh {} \;
total 0
-rw-rw-r-- 1 t t 0 Jun 8 23:13 '"; echo world "'
-rw-rw-r-- 1 t t 0 Jun 8 23:13 './"; echo world "'
我想知道为什么文件名中的开头和结尾双引号不与 中的开头和结尾双引号配对"$@"
,以便echo world
文件名中可以运行?
是否是因为在 shell 命令的词法分析过程中必须识别引号sh
才能引用?这里文件名中的引号仅在参数扩展后才出现在shell命令中,已经通过词法分析并且文件名中的引号来不及被识别?类似于https://unix.stackexchange.com/a/448643/674?
不同的是,添加eval
也不会使注入工作,因为虽然eval
会使shell 可以识别文件名中的"
和,但文件名中的 也会被 shell 删除:;
"
$ find . -exec sh -c 'eval ls -l "$@"' sh {} \;
total 0
-rw-rw-r-- 1 t t 0 Jun 8 23:13 '"; echo world "'
ls: cannot access './; echo world ': No such file or directory
调试以查看eval
实际执行的内容:
$ find . -exec sh -c 'echo ls -l "$@"' sh {} \;
ls -l .
ls -l ./"; echo world "
$ ls -l ./"; echo world "
ls: cannot access './; echo world ': No such file or directory
谢谢。
附带说明一下,为什么命令的输出中有同一文件的两项find
?自从 。是到当前目录的硬链接,为什么find不输出直接本身而是其下的文件?
答案1
为什么文件名中的开头和结尾双引号不与中的开头和结尾双引号配对
"$@"
因为变量扩展不是直接的文本替换。变量内的引号不会被处理为引号,此时它们只是普通字符。
这类似于任何包含引号的变量。
$ foo='"foo bar"'
中的引号foo
不会结束命令行中的引号,没有分词:
$ printf "%s\n" "$foo"
"foo bar"
还有这里,那里是分词(因为扩展没有被引用),并且引号没有foo
做任何阻止它的事情:
$ printf "%s\n" $foo
"foo
bar"
后一个类似于为什么将命令存储在变量中的简单方法不起作用。
至于eval
,我不明白你为什么要添加它,但由于它涉及另一轮扩展,你可以在其中放置一个命令替换:
$ touch '$(uname -m)'
$ find . -exec sh -c 'eval ls "$@"' sh {} \;
$(uname -m)
ls: cannot access './x86_64': No such file or directory
或分号:
$ touch ';uname -m'
$ find . -exec sh -c 'eval ls "$@"' sh {} \;
;uname -m
;uname -m
x86_64
(不过,我不确定为什么会运行ls
两次。)
对于名为 的文件"; echo world "
,引号会扩展为 的一部分"$@"
,其中eval
会看到它们,并且由于它再次运行整个 shell 处理,因此您将得到一个带引号的分号。
在所有易受攻击的情况下,没有一个文件名可以用来注入命令,这取决于命令的构建方式。