Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bad array subscript error when trying to increment an associative array element

I can create an associative array and assign an integer to a key that contains a single quote in it:

$ declare -A dict
$ var="john's"
$ dict[$var]=1
$ echo ${dict[$var]}
1
$ declare -p dict
declare -A dict=(["john's"]="1" )

But when I try to increment its value:

$ (( dict[$var]++ ))
bash: ((: dict[john's]++ : bad array subscript (error token is "dict[john's]++ ")
$ (( dict["$var"]++ ))
bash: ((: dict[john's]++ : bad array subscript (error token is "dict[john's]++ ")
$ (( dict["${var}"]++ ))
bash: ((: dict[john's]++ : bad array subscript (error token is "dict[john's]++ ")

I always get the same error. What am I doing wrong?

like image 822
Bobby Wan-Kenobi Avatar asked May 09 '20 18:05

Bobby Wan-Kenobi


3 Answers

The single quote in key name is causing the parser to treat it as a un-terminated quote character. One way to fix this would be to escape the ' character in the key

key="john's"
printf -v escKey "%q" "$key"

now because of the %q specifier, printf() would have applied required escapes to all shell meta characters i.e. making it "shell-quoted" and re-usable. If you print the contents of escKey you'll notice the ' escaped

printf '%s\n' "$escKey"
john\'s

Now you can use this keyname in your associative array. Remember you can always manually add the escapes which could be messy. Since %q is a native way provided by the shell, it is much safe to use it.

(( dict["$escKey"]++ ))

Also in bash versions >= 4.4 parameter expansion has @Q which is a shortcut for %q specifier of printf() using which you can do

(( dict["${key@Q}"]++ ))
like image 178
Inian Avatar answered Oct 24 '22 09:10

Inian


You need to make sure, by escaping $, that $var isn't expanded until Bash starts parsing dict[$var]++ as an arithmetic expression.

$ (( dict[\$var]++ ))
$ declare -p dict
declare -A dict=(["john's"]="2" )

On newer versions of Bash, enabling the assoc_expand_once shell option works too, but this is unsafe when keys are read from an untrusted source. An example of how even when this option is enabled the shell can still be tricked into executing arbitrary commands with a tainted $var is given in Stephane's answer.

like image 6
oguz ismail Avatar answered Oct 24 '22 10:10

oguz ismail


Yes, not only

(( dict[$var]++ ))

Won't work with some values of $var, but it's also an arbitrary command execution vulnerability.

bash-5.1$ var='1$(uname>&2)'
bash-5.1$ typeset -A dict
bash-5.1$ (( dict[$var]++ ))
Linux
Linux

Quoting the value of $var with printf %q or ${var@Q} is not an option because with some values of $var, those operators use the $'...' form of quoting which is not understood in an arithmetic expression:

bash-5.1$ typeset -A dict
bash-5.1$ var=$(seq 2)
bash-5.1$ (( dict[${var@Q}]++ ))
bash-5.1$ echo "${!dict[@]}"
$1\n2
bash-5.1$ var=$'\n\'$(uname >&2)],x['
bash-5.1$ (( dict["${var@Q}"]++ ))
Linux
bash: ((: dict[$'\n\'$(uname >&2)],x[']++ : bad array subscript (error token is "x[']++ ")

(still an ACE).

Using the new (since 5.0) assoc_expand_once doesn't help as it still doesn't address the problem with the @/* values or the ] character (and so doesn't fix the ACE vulnerabilities):

bash-5.1$ shopt -s assoc_expand_once
bash-5.1$ var='x],b[1$(uname>&2)'
bash-5.1$ typeset -A dict
bash-5.1$ (( dict[$var]++ ))
Linux

Another problem specific to bash (not ksh93 nor zsh) is that bash associative arrays don't support the empty key, so you just can't use arbitrary strings as associative array keys. A work around is to prefix/suffix all your keys with a fixed string.

Here, you can do:

dict[.$var]=$(( ${dict[.$var]} + 1 ))

Or (assuming the assoc_expand_once option is not enabled):

let 'dict[.$var]++'

(make sure you use single quotes).

Here using . as the prefix.

See How to use associative arrays safely inside arithmetic expressions? for all the gory details and more caveats about that.

More generally, shell arithmetic expressions are a minefield. See also: Security Implications of using unsanitized data in Shell Arithmetic evaluation.

like image 1
Stephane Chazelas Avatar answered Oct 24 '22 10:10

Stephane Chazelas