« Previous 1 2 3 4 Next »
Best practices for secure script programming
Small Wonders
Vulnerable Temporary Files
Assume a script needs to make the credentials available to other applications. Temporary files are often used for this purpose. If you create the file in the /tmp/
folder, it should disappear after the script is processed. A classic example of this is the following script, which creates a file with a password:
#!/bin/sh SECRETDATA="ITA says hello" echo > /tmp/mydata chmod og-rwx /tmp/mydata echo "$SECRETDATA" > /tmp/mydata
Problems arise if an attacker monitors the temp file with the very fast acting FileSystem Watcher API. In the case of the present script, the attacker could call fopen
and tag the file as open to harvest the information deposited there immediately or later.
As before with plaintext passwords, the safest way is not to write the credentials to a temporary file. However, if this is essential to perform a function, the following approach is recommended:
#!/bin/sh SECRETDATA="ITA says hello" umask 0177 FILENAME="$(mktemp/tmp/mytempfile.XXXXXX)" echo "$SECRETDATA" > "$FILENAME"
First, the umask
command assigns the access attributes passed in as parameters to newly created files. Second, the mktemp
command creates a random file name each time, which makes configuring the FileSystem Watcher API more difficult. Numerous sources are critical of the use of temporary files in shell scripts in general [5].
Securing Against Incorrect Parsing
The most frequent point of criticism levied against the shell execution environment is that it lacks methods for string processing. In combination with passing in variables directly to the shell, serious vulnerabilities arise. A classic example is a script that reads in a parameter from STDIN and executes it with an eval
command:
#!/bin/bash read BAR eval $BAR
The eval
command is normally intended to perform calculations and harmless commands. One legitimate use case would be to deliver an echo
command (with a pipe redirect if needed):
./itaworker.sh echo hello
A malicious user would not pass an echo
call with redirection at this point but a command such as rm
, as mentioned earlier. As a matter of principle, the shell does not recognize this, which is why the script destroys data.
Because some eval
-based tasks are not feasible without this command, shell-checking tools like shellcheck
do not complain about the use of the command. The script
#!/bin/bash read BAR eval "$BAR"
would trigger the error message Double quote to prevent globbing and word splitting
. However, because this change does not verify the values contained in BAR
, the vulnerability still exists. From a security perspective, the only correct way to handle the eval
command is not to use it at all. If this is not an option, be sure to check the commands entered for strings like rm
and for non-alphanumeric characters.
Queries are another way to provoke problems. In the next example, I'll look at a script that checks to see whether the user enters the string foo
. The payload, which here consists only of a call to echo
, should only be executed if foo
is entered:
#!/bin/bash read FOO if [ x$FOO = xfoo ] ; then echo $FOO fi
In a superficial check of the script behavior, the input (e.g., itahello
) would not lead to the execution of the payload. However, if you pass a string following the pattern foo = xfoo <any>
, the payload is processed:
./itaworker.sh foo = xfoo -o sdjs foo = xfoo -o sdjs ~$
Damage occurs if something more sensitive, such as an eval
call, needs to be secured instead of the echo
payload. In the worst case, the payload then contains a command that causes damage because of invalid values.
To work around this problem, you can use quoting, as in other shell application scenarios:
if [ "$FOO" = "foo" ] ; then echo $FOO fi
The "new" version of the script evaluates the content of the variable completely, which results in the rejection of invalid input:
./itaworker.sh foo foo ./itaworker.sh foo = xfoo
Quoting is even more important when working with older shells. A program built inline with the following pattern could be prompted to execute arbitrary code by entering a semicolon-separated string:
#!/bin/sh read VAL echo $VAL
The cause of this problem is that older shells do not parse before substituting the variable: The semicolon would split the command and cause the section that followed to be executed.
Ruling Out Parsing Errors
The rather archaic language syntax sometimes makes for strange behavior, as in the following example, which is supposed to call the file /bin/tool_VAR
:
ENV_VAR="wrongval" ENV="tool" echo /bin/$ENV_VAR
If you run this at the command line, you will see that it uses the value stored in the variable ENV_VAR
. The shell separates echo /bin/$ENV_VAR
at the underscore. To work around this problem, it is advisable to set up all access to variables in curly brackets as a matter of principle. A corrected version of the script would be:
ENV_VAR="wrongval" ENV="tool" echo /bin/${ENV}_VAR
Running the corrected script shows that the string output is now correct:
./itaworker.sh /bin/tool_VAR
Problem number two concerns the analysis of the shell user permissions. The $UID
and $USER
variables can be used to analyze the permissions of the currently active user:
if [ $UID = 100 -a $USER = "myusername" ] ; then cd $HOME fi
Unfortunately, the two variables are not guaranteed to contain the value corresponding to the active user at runtime. Although many shells now save the variable $UID
, this is usually not the case for $USER
: In older shells it is even possible to provide both variables with arbitrary values. To circumvent this problem, it is a good idea to determine the username and user ID by calling operating system commands. The values returned in this way can only be influenced by setting the path variable, which complicates the attack.
In particularly critical execution environments, it is always advisable to write out paths in full. For example, if you call echo
by its path /bin/echo
, the activated program is independent of the path variable.
Finally, I should point out that the Bash shell supports a special version of the shebang: #!/bin/bash -p
. This variant instructs the shell not to execute the .bashrc
and .profile
files as part of the startup. The SHELLOPTS
variable and the start scripts created in ENV
and BASH_ENV
are also cast aside in this operating mode. The purpose of this procedure is that manipulations of the environment carried out by the attacker cannot be activated by random script executions.
« Previous 1 2 3 4 Next »
Buy this article as PDF
(incl. VAT)