答案1
问题在于,如果 的内容$x
尚未清理并且包含可能受到攻击者控制的数据,那么 shell 代码最终可能会在权限升级上下文中使用(例如,由 setuid 调用的脚本)应用程序、sudoers 脚本或用于直接或间接处理离网数据(CGI、DHCP 挂钩...)。
如果:
x='(PATH=2)'
然后:
x=$((1-$x)))
PATH
设置为2
(很可能受到攻击者控制的相对路径)有副作用。您可以替换PATH
为LD_LIBRARY_PATH
或IFS
... bash、zsh 或 ksh 中也会发生同样的情况x=$((1-x))
(不是 dash 或 yash,它们只接受变量中的数字常量)。
注意:
x=$((1-$x))
$x
在某些实现(根据 POSIX 可选)(减量)运算符的 shell 中,对于 的负值将无法正常工作--
(与 一样x=-1
,这意味着要求 shell 计算1--1
算术表达式)。"$((1-x))"
没有问题,因为x
作为算术评估的一部分(而不是之前)进行了扩展。
在bash
,zsh
和ksh
(非dash
或yash
)中,如果x
是:
x='a[0$(uname>&2)]'
$((1-$x))
然后或的扩展导致执行$((1-x))
该命令(对于,需要是一个数组变量,但可以使用例如它)。uname
zsh
a
psvar
总之,不应在 shell 的算术表达式中使用未初始化或未清理的外部数据。
$((...))
请注意,算术评估可以通过(又名$[...]
inbash
或)来完成,但也取决于、/ 、、、、、、、内置函数、数组索引和构造zsh
中的 shell等)。let
[
test
declare/typeset/export...
return
break
continue
exit
printf
print
((..))
[[...]]
ksh
因为它适用于//中的数组索引,zsh
所以bash
它也适用于所有以变量名作为参数的内置函数([
/ test
with -v
, read
, unset
, export
// typeset
, / readonly
with , ... )。print
printf
-v
getopts
事实上,使用[[...]]
或不使用[
/内置函数将数字测试运算符的操作数视为算术表达式,这是在or中通常更倾向于使用后者的test
原因之一。bash
zsh
比较:
$ a='x[1$(uname>&2)]' bash -c '[ "$a" -eq "$b" ]'
bash: line 0: [: x[1$(uname>&2)]: integer expression expected
(安全)与:
$ a='x[1$(uname>&2)]' bash -c '[[ "$a" -eq "$b" ]]'
Linux
(uname
已被处决)。
(在ksh
, 和[
都有[[ ... ]]
问题)
要检查变量是否包含文字十进制整数,可以使用 POSIXly:
case $var in
("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac
请注意,在某些区域设置中[0-9]
, 和[[:digit:]]
的匹配范围超过 0123456789,因此应避免进行任何形式的输入验证/清理。
另请记住,在某些情况下,带有前导零的数字被视为八进制(010
有时是 10,有时是 8),并且请注意,上面的检查将允许可能大于您的系统(或您将使用的任何应用程序)支持的最大整数的数字。使用该整数;例如,bash 将 18446744073709551616 视为 0,因为那是 2 64)。因此,您可能需要在上面的 case 语句中添加额外的检查,例如:
(0?* | -0?*)
echo >&2 'Only decimal numbers without leading 0 accepted'; exit 1;;
(-??????????* | [!-]?????????*)
echo >&2 'Only numbers from -999999999 to 999999999 supported'; exit 1;;
例子:
$ export 'x=psvar[0$(uname>&2)]'
$ ksh93 -c 'echo "$((x))"'
Linux
ksh93: psvar: parameter not set
$ ksh93 -c '[ x -lt 2 ]'
Linux
ksh93: [: psvar: parameter not set
$ bash -c 'echo "$((x))"'
Linux
0
$ bash -c '[[ $x -lt 2 ]]'
Linux
$ bash -c 'typeset -i a; export a="$x"'
Linux
$ bash -c 'typeset -a a=([x]=1)'
Linux
$ bash -c '[ -v "$x" ]'
Linux
$ bash -c 'read "$x"' < /dev/null
Linux
$ env psvar= bash -c 'unset "$x"'
Linux
$ mksh -c '[[ $x -lt 2 ]]'
Linux
$ zsh -c 'echo "$((x))"'
Linux
0
$ zsh -c 'printf %d $x'
Linux
0
$ zsh -c 'integer x'
Linux
$ zsh -c 'exit $x'
Linux
更多阅读:
- http://www.zsh.org/mla/workers/2014/msg01041.html(奥利弗·基德尔(Oliver Kiddle)向
x[0$(...)]
我们提出了这个问题)。 - http://thread.gmane.org/gmane.comp.standards.posix.austin.general/9971(
lynx news://news.gmane.io/gmane.comp.standards.posix.austin.general/9971
现在 gmane 的 Web 界面已停止使用)。 - https://lists.gnu.org/archive/html/bug-bash/2014-12/msg00028.html另一个可能导致代码注入的错误设计
bash
。 - 忘记在 bash/POSIX shell 中引用变量的安全隐患其中,将变量不加引号可能会互相加剧。