






















下面这段脚本看起来无害:
echo -n "要查哪个文件?" read name ls -l $name
但如果用户输入的"文件名"是:
. ; /bin/rm -Rf /
那实际执行的就成了 ls -l . ; /bin/rm -Rf / :先列当前目录,然后递归删除根分区。
来做一个实际演示。注意,要演示分号注入,脚本必须用 eval 来解析参数。没有 eval 的话,Shell 不会在变量扩展后重新识别分号为命令分隔符:
cat > /tmp/vuln_eval.sh << 'EOF' #!/bin/sh echo "查文件: $1" eval ls -l $1 EOF chmod +x /tmp/vuln_eval.sh
/tmp/vuln_eval.sh "nonexistent; echo '分号注入成功!'"
查文件: nonexistent; echo '分号注入成功!' ls: 无法访问 'nonexistent': 没有那个文件或目录 分号注入成功!
注意最后一行: echo 命令确实执行了("分号注入成功" 被打印出来)。这就是命令注入:输入中的分号被解析为命令分隔符。
光用 eval 还不够,还必须加上引号。如果把 eval ls -l $1 改为 eval ls -l "$1" ,分号就会变成普通字符,不再起到命令分隔的作用。
除了分号,反引号是另一种注入方式,而且不需要 eval 就能生效。Shell 在变量扩展阶段就会执行反引号里的命令:
/tmp/vuln_eval.sh "`echo '反引号注入成功'`"
查文件: 反引号注入成功 ls: 无法访问 '反引号注入成功': 没有那个文件或目录
反引号里的 echo 命令确实被执行了,它的输出被当成了"文件名"参数传给 ls 。
防御方法: 引用变量是最简单的防线:
/bin/ls -l "$name"
但引用只能防止单词分割,不能防止命令注入。如果你的脚本需要处理来自未知用户的输入,更可靠的做法是对输入做消毒:只允许字母、数字和一小组安全标点,碰到其他字符就直接报错退出。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。