Find: Hidden Dangers Of Embedding {} In Shell Code

The Perils of Unquoted Braces

Brace expansion in shell scripts refers to the process whereby curly braces=”{}” containing commas-separated strings are expanded into the individual strings. For example, echo {file1,file2} will expand to echo file1 file2. This can cause significant issues when used with unsafe variables in scripts.

How unquoted braces enable unintended file globbing

File globbing refers to expanding file name patterns like *.txt into matching file names. If a shell variable contains braces but is not quoted properly, it enables unwanted globbing and code execution.

For example, if filename=”$string{}” is used, $string’s value will glob and expand the {} brace pattern before assignment. If $string contains user-input like * or *, all files in the current directory could be unexpectedly deleted or moved based on the script’s logic.

Real-world examples of file deletion bugs

A real-world example of a brace expansion bug deleting files unintentionally happened in Apple’s Mac OS X 10.10 launch in 2014.

The following script was intended to create a temporary file from a user-provided filename variable, perform processing, and later delete the temp file:

filename="$1"
tempdir="/tmp/${filename}.tmp"
touch "$tempdir"
# ... process $tempdir file ...  
rm "$tempdir"

However, if $1 contains */*, brace expansion causes the * wildcard pattern to glob match and substitute to all file names before creating $tempdir. Thus tempdir gets multiple file names appended with .tmp, many of which are important system files, all eventually getting deleted.

Why brace expansion happens before other expansions

Bash performs brace expansion early, before any other substitutions like variable expansion, globbing, or command substitution. This enables concise notation like mv file{.txt,.bak} expanding to rename operations.

But such early expansion has dangerous implications for unsanitary code. If a variable embeds brace patterns, those patterns will expand first and cause globs or code execution before the variable even gets assigned or evaluated.

Mitigating Issues with Brace Expansion

Thankfully there are straightforward ways to mitigate potential issues due to unsafe brace expansion.

Quoting braces to disable expansion

Putting double quotes around variables disables brace expansion during assignment:

filename="$1"             # Potentially unsafe
filename="'$1'"          # Secure - quotes disable brace expansion

This guarantees brace patterns assigned intact without accidentally expanding. Later code can choose to explicitly expand the braces when ready by trimming quotes.

Setting nomatch to suppress errors

Bash’s shopt builtin controls shell optional behavior. shopt -s nullglob nomatch changes two relevant behaviors:

shopt -s nullglob nomatch
  • nullglob – Expand non-matching glob patters to nothing, preventing unintended filename generation
  • nomatch – Suppress “No match” errors from failed glob patterns, preventing unexpected program termination

These options make glob expansions safer and less disruptive.

Using an array to store file names

File names can be safely collected into an array instead of globbed together:

filenames=()
for file in *; do
  filenames+=("$file") 
done

This avoids wildcard expansion issues by iteratively appending names.

Safe Brace Usage Guidelines

Following best practices helps avoid issues with incorrect brace handling.

Double quote variables containing braces

Always wrap variable references in double quotes unless explicitly intending glob pattern expansion:

  
var="some{}"
echo "$var" # Good - braces quoted by default

glob="*.txt"
echo $glob # Good - disabling quotes to enable globbing 

This best practice eliminates unintended code execution risks.

Escape braces when intended as literals

If braces should output rather than expand, precede them with backslashes:

literal="\{}"
echo "$literal"

This renders braces harmless by escaping the special meaning.

Review scripts to identify unquoted variable expansions

Audit scripts to find instances of unquoted variables like $var or ${var}. These indicate either intention to expand braces or globs, or require adding double quotes for safety.

Static analysis tools can also help reliably identify unsafe expansion that may need developer review.

Example Code Snippets

Buggy script leading to unintended file deletion

This realistic but vulnerable example allows arbitrary file deletion through incorrect brace handling:

echo "Enter file pattern to remove"
read pattern  

files="/tmp/${pattern}"
echo "Removing $files" 
rm -rf $files

If pattern has */* then rm deletes entire filesystem due to root directory globbing.

Improved script with quoted brace expansion

The following better practices make it robust against code injection from brace mishandling:

  
echo "Enter file pattern to remove"
read -r pattern  

shopt -s nullglob # Null glob non-matches
files="/tmp/${pattern}"   
echo "Removing '$files'" # Quoted expansion  
rm -rf "$files"

This sanitizes unsafe expansion by:

  • Disabling error on failed globs
  • Quoting expanded variables
  • Escaping special meaning of braces

Greatly improving script security.

Leave a Reply

Your email address will not be published. Required fields are marked *