为什么这个代码注入不起作用?

为什么这个代码注入不起作用?
$ 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 处理,因此您将得到一个带引号的分号。

在所有易受攻击的情况下,没有一个文件名可以用来注入命令,这取决于命令的构建方式。

相关内容