Bash Globbing Attacks
Bash globbing attacks use crafted filenames to change what a wildcard command touches. A safe script quotes every expansion and never lets a filename act as an option or as several words.
Unquoted expansion splits one name into pieces
Two text files are created, one with a space in its name. The quoted array counts two files, but the unquoted loop counts three pieces.
printf 'x\n' > "a b.txt"
printf 'x\n' > "c.txt"
files=(*.txt)
echo "count: ${#files[@]}"
for f in ${files[@]}; do echo "piece: $f"; doneA dash filename is read as an option
A file named -n holds one word. Plain cat treats -n as its own option and reads empty input instead of the file. The -- form forces cat to treat -n as a filename.
printf 'hello\n' > ./-n
echo "without dashdash:"
cat -n
echo "with dashdash:"
cat -- -nAn unmatched pattern stays literal
No text file exists in this empty directory. Nullglob counts zero matches, while the default setting leaves the pattern as the literal word.
shopt -s nullglob
files=(*.txt)
echo "count: ${#files[@]}"
shopt -u nullglob
for f in *.txt; do echo "literal: $f"; doneExample
printf 'x\n' > "a b.txt"
printf 'x\n' > ./-n.txt
printf 'x\n' > keep.txt
safe=0
for f in *.txt; do
safe=$((safe+1))
printf 'checked: %s\n' "$f"
done
echo "safe count: $safe"
broken=0
for f in *.txt; do
for part in $f; do
broken=$((broken+1))
done
done
echo "broken count: $broken"Key points
- Unquoted expansion: an unquoted variable splits on spaces and expands wildcards again.
- Quoted expansion: quotes keep one filename as one word, spaces included.
- Leading dash: a filename starting with a dash can be read as an option by the next command.
- Dash defence: put -- before filenames, or prefix them with ./, so no name is read as an option.
- Empty match: with no match, a pattern stays literal by default instead of expanding to nothing.
- Nullglob: shopt -s nullglob makes an unmatched pattern expand to nothing.
- Count check: count matches before acting, so an empty or huge match cannot surprise the script.
In this example, three files are created, including one with a space and one starting with a dash. The first loop quotes each name, so it checks three files exactly once each. The second loop leaves each name unquoted, so the spaced name splits into two pieces and the count rises to four.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
A script loops over *.txt files with an unquoted variable. One file is named "a b.txt". What goes wrong?