gar*_*Red 29 shell security arithmetic
在对最近问题的评论中,Stéphane Chazelas 提到双括号算术存在安全隐患,例如:
x=$((1-$x))
Run Code Online (Sandbox Code Playgroud)
在大多数贝壳上。
我的谷歌技能似乎生疏了,我找不到任何东西。双括号算术的安全含义是什么?
Sté*_*las 32
问题在于 的内容$x
尚未清理并且包含可能在攻击者控制之下的数据的情况下,如果 shell 代码可能最终被用于权限提升上下文(例如由 setuid 调用的脚本)应用程序、sudoers 脚本或用于直接或间接处理离线数据(CGI、DHCP 钩子...))。
如果:
x='(PATH=2)'
Run Code Online (Sandbox Code Playgroud)
然后:
x=$((1-$x)))
Run Code Online (Sandbox Code Playgroud)
具有设置PATH
为2
(很可能受攻击者控制的相对路径)的副作用。您可以替换PATH
为LD_LIBRARY_PATH
或IFS
...x=$((1-x))
在 bash、zsh 或 ksh 中也会发生同样的情况(不是 dash 或 yash,它们只接受变量中的数字常量)。
注意:
x=$((1-$x))
Run Code Online (Sandbox Code Playgroud)
$x
在某些实现(根据 POSIX 可选)--
(递减)运算符的shell 中,对于负值将无法正常工作(与 一样x=-1
,这意味着要求 shell 评估1--1
算术表达式)。"$((1-x))"
没有问题,因为x
作为算术评估的一部分(不是之前)进行了扩展。
在bash
, zsh
and ksh
(not dash
or yash
), ifx
是:
x='a[0$(uname>&2)]'
Run Code Online (Sandbox Code Playgroud)
然后扩展$((1-$x))
or$((1-x))
导致该uname
命令被执行(for zsh
,a
需要是一个数组变量,但可以使用psvar
例如)。
总之,不应在 shell 的算术表达式中使用未初始化或未清理的外部数据。
请注意,算术计算可以通过$((...))
(又名$[...]
inbash
或zsh
)完成,但也取决于let
、[
/ test
、declare/typeset/export...
、return
、break
、continue
、exit
、printf
、print
内置函数、数组索引((..))
和[[...]]
构造中的外壳,仅举几例)。
因为它适用于数组索引中ksh
/ zsh
/ bash
,它也适用于需要变量名作为参数所有内建([
/test
用-v
,read
,unset
,export
/ typeset
/ readonly
,print
/printf
用-v
,getopts
...)。
数字测试运算符的操作数被视为带有[[...]]
和不带有[
/test
内置的算术表达式这一事实是为什么 inbash
或zsh
,通常最好使用后者的原因之一。
相比:
$ a='x[1$(uname>&2)]' bash -c '[ "$a" -eq "$b" ]'
bash: line 0: [: x[1$(uname>&2)]: integer expression expected</pre>
Run Code Online (Sandbox Code Playgroud)
(安全)与:
$ a='x[1$(uname>&2)]' bash -c '[[ "$a" -eq "$b" ]]'
Linux
Run Code Online (Sandbox Code Playgroud)
(uname
被处决)。
(中ksh
,双方[
并[[ ... ]]
有问题)
要检查变量是否包含文字十进制整数,您可以使用 POSIXly:
case $var in
("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac
Run Code Online (Sandbox Code Playgroud)
要注意的是[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;;
Run Code Online (Sandbox Code Playgroud)
例子:
$ 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
Run Code Online (Sandbox Code Playgroud)
更多阅读:
x[0$(...)]
我们注意的地方)。bash
。