评估“ls”的输出是否会使脚本容易受到代码注入的攻击?

评估“ls”的输出是否会使脚本容易受到代码注入的攻击?

是否存在一个足够病态的文件名或文件名集合,其约束条件是它必须具有给定的前缀和后缀,比如file_<your_stuff_here>.txt这会使在 bash 脚本中评估或反引号 ls 的输出变得危险?我们所说的“危险”意味着它允许任何拥有相关目录文件创建权限的人运行任何命令。

例如,让 bash 脚本只是

#!/usr/bin/env bash
hello=(`ls -t`)

看起来文件系统通过将文件名用引号括起来来清理特殊字符,例如空格。它会捕获所有这些东西吗?

答案1

文件名...评估危险

当然。

$ touch 'file $(date >&2).txt'
$ bash -c 'eval ls *'
Wed 03 Feb 2021 06:07:08 PM EET
ls: cannot access 'file': No such file or directory
ls: cannot access '.txt': No such file or directory

别乱eval塞东西

或者在 bash 脚本中反引号 ls 的输出?

我不太确定这意味着什么。

如果你的意思是你所举的例子,

hello=(`ls -t`)

或者使用更理智的语法,

files=( $(ls -t) )

然后你只需将 wordsplit 的输出获取ls到数组即可。

$ declare -p files
declare -a files=([0]="file" [1]="\$(date" [2]=">&2).txt")

即使在可能的命令注入之前,这里最大的问题是文件名中的空格破坏了它,我们得到了两个数组条目,而不是一个。请参阅有关页面解析 ls在格雷格的维基上。不,你不能通过添加引号来解决这个问题,分词不是那样的。

所以,不要ls在那里使用。只需让 shell 生成文件名列表即可:

files=(*)
declare -p files
declare -a files=([0]="file \$(date >&2).txt")

这里唯一的问题是 Bash 没有提供按日期对文件进行排序的好方法,所以ls -t 诱人的。好的选择是将日期放在文件名本身中所以默认排序让您按日期排序, 或者使用 Zsh,因为它可以按日期排序。或者做丑陋的黑客解决该问题(警告,我尚未测试该解决方案)。

eval如果你需要将文件名传递给任何只需要 shell 脚本的东西,也会出现同样的问题,比如

ssh somehost "do something with $file"      # wrong
ssh somehost "do something with '$file'"    # still wrong, the name
                                            # could contain single ticks

看起来文件系统通过将文件名用引号括起来来清理特殊字符,例如空格。它会捕获所有这些东西吗?

哦,亲爱的,哦不,不是这样。如果文件系统做了一些事情来阻止将特殊字符存储到 shell,那么 unix.SE 上的一半帖子就不需要了。

如果你想要承受太多知识带来的痛苦,请阅读大卫·惠勒 (David Wheeler) 的一篇文章:修复 Unix/Linux/POSIX 文件名:控制字符(例如换行符)、前导破折号和其他问题

还有他的另一张,Shell 中的文件名和路径名:如何正确执行。许多相关主题也已在 unix.SE 上进行了讨论。

此外,引号甚至没有帮助。

$ touch '"quoted name"' 'othername'   # two files
$ printf "%s\n" $(ls)                 # oops
othername
"quoted
name"
$ printf "%s\n" *                     # this works better
othername
"quoted name"

因为当发生分词时,引号只是一个常规字符。 (除非您设置IFS包含引号,这可能只会使情况变得更糟。)此外,即使名称用引号引起来,它也会中间仍然可以有引号,打破这一点。您还需要注意转义或正确引用它们。

是 GNU ls 进行引用,取决于版本和设置:

$ ls -l
total 0
-rw-rw-r-- 1 ilkkachu ilkkachu 0 Feb  3 18:30  othername
-rw-rw-r-- 1 ilkkachu ilkkachu 0 Feb  3 18:30 '"quoted name"'

这与 相同ls --quoting-style=shell。顺便说一句,对于所有换行符、美元符号和引号,它似乎都是正确的。但你相信它能做对吗?如果你这样做,如果您知道如何正确使用它,您也许可以使用它来获取排序列表。

答案2

hello=(`ls -t`)

这种形式看起来很安全。 Bash 将仅对命令替换的结果执行 split + glob,而不将其解释为语法:

a=(`echo '[0]=1'`)
typeset -p a

declare -a a=([0]="[0]=1")  # aha!

然而,(晦涩的)declare "var=val"local "var=val"形式则不然,因为它们的工作方式类似于eval "var=val"

cd "$(mktemp -d)"
touch '$(yes BOOBS >&2&)'
declare -a "a=($(ls))"

BOOBS
BOOBS
BOOBS
...

declare -a a="($(ls))"declare -a a="(`ls`)"等相同。

或者甚至与未引用的形式也是如此,前提是变量已经声明为数组:

cd "$(mktemp -d)"
a=(1 2 3)
touch '($(yes BOOBS >&2&))'
declare a=$(ls)  # I forgot that a was an array

BOOBS
BOOBS
BOOBS
...

相关内容