It makes me super uncomfortable that globbing in Bash can turn into code execution. The fact that the name of a file can change the behavior of ls is scary. This also works for other commands that you tend to glob with, such as rm.
@Lee_Holmes Oh weird. I had to look at that a few times to figure out what was happening.
I'm sure there's something here, but I don't have the patience to find it :)
@Lee_Holmes It's not just bash, this affects any shell that does globbing, and there's no real way to prevent it. Programs that use getopt can use --
to signify end of parameters, but you're the one that has to remember to use it.
@Lee_Holmes explore the unknown 🙂 but your example is expected 🙃
@Lee_Holmes Usually RCE with tar in some cronjob for backups like in this CTF task https://www.reddit.com/r/hackthebox/comments/13yasnj/bash_globbing_for_privilege_escalation/
@jernej__s Yeah, I'm aware of that. For sure everybody else in the world is as well right? :) And everybody who is aware of it uses it all the time right? :)
@Lee_Holmes @Lee_Holmes This is part of why to use --
to denote end of options and quoted variable substitutions such as "$@"
religiously in POSIX shell scripts.
Notably, the bash
builtin getopts
and the C Standard Library function getopt
both implement --
, which means any Bash script using said built-in or C[++] binary using said function handle a --
option, even if not documented in their manual. (The Python module argparse
implements this too. Check corresponding modules for other languages; some provide it, others provide a means to implement it.)
(Side note: Don't use the getopt
executable; use the getopts
builtin instead. There are unavoidable argument mishandling bugs caused in any shell script using the getopt
executable, because (per the BSD getopt
manual) "arguments containing white space or embedded shell metacharacters generally will not survive intact" when unparsed arguments are passed as arguments to a separate argument parsing executable. However, do use the C Standard Library function getopt
, but mind your char*
buffers, because C isn't Rust.)
@Lee_Holmes If you're writing a script, or accessing and untrusted file system, use ls -- *
(and likewise for rm
, mv
, cp
, and pretty well anything else where you're going to use a glob).
In an interactive shell, if you're not sure, just use ls
(without the *
), and see if there are any names starting with hyphens. There's never a reason to type ls *
anyway, but it's a good idea to use just plain ls
before using, say, rm *
(and then, if you see any files with names starting with hyphens, that's what rm -- *
is for).
@Lee_Holmes
I mirror this concern. That's why I give myself a small safety net and alias those commands to e. g. ls -- $@
. If I really want to omit the --
, I can always call /bin/ls
directly.
@Lee_Holmes its CTF stuff: tar file checkpoint execution TartarSauce I think was a box on HackTheBox I did ('tar' geddit corny jokes), as yoou know so * expands to a space-separated list of every filename in the directory so tools that treat anything that begins with - or -- as an option.
With tar the expanded list contains --checkpoint=1, tar thinks you typed that flag yourself --checkpoint-action=exec=<some suid>
also rsync and others I am sure someone made a list of globbables
@nf3xn Oh, I thought you meant that getting code execution through 'find' was old hat. That scenario seems sketch too.
@Lee_Holmes the learning experience most boxes I think were trying to teach is always use full paths in your backups scripts and don't allow easy injection paths - you are supposed to be able to read the script or just guess oh I think there was a Chronos box too that ran a cronjob every five minutes... I mean its not very real world but interesting enough, certainly if you do enough CTFs you look for it
@deFractal Yeah, this is why it's scary. "Don't use globbing without deeply thinking through its security implications" is tough at scale.
@Lee_Holmes Yep. The Bourne shell is old (1979), and POSIX shells such as Bash require compatibility with Bourne shell features, in order to not break decades of shell scripts. The security implications follow from what globbing is actually doing (substituting strings), not what it's commonly (and mistakenly) thought of as doing (listing files).
In Bourne shell, an unquoted *
does not mean "all the files in this directory"; it means (approximately, modulo the nullglob
, failglob
, nocaseglob
, and dotglob
, and globskipdots
shell options) "before passing arguments to the command, substitute for this character a list of words formed from the names of items matching the non-glob part of the path (or a literal asterisk if there are no matches of a partial path, or the names of children of the current directory whose names don't start with periods if the asterisk is on its own, or a literal asterisk if there are no directory children whose names don't start with periods for a lone asterisk)."
It's closer to C macros (or "find and replace all" in a text editor) than to Rust macros: it works at the string level, not the semantic level.
To use globbling safely in a POSIX shell script, unconditionally use --
after options (even if you're using no options but --
) to any supporting command (and use patterns like readarray -t list < <(…) ; some-command "${list[@]}"
or find … -print0 | xargs -0 …
for the rare commands not supporting --
).
Meanwhile, in an interactive shell, just be sure to ls
first before doing anything to *
.
@Lee_Holmes that is kinda scary and fascinating at the same time
@Lee_Holmes @coldclimate You could at least credit the woman who discovered this and has just posted about it! Passing off is frowned upon!
@cyberspice @coldclimate What other post are you talking about?
If you want to see how deep the rabbit hole goes:
[2010] - Filenames and Pathnames in Shell: How to do it Correctly - https://dwheeler.com/essays/filenames-in-shell.html
[2014] - Unix Wildcards Gone Wild - https://www.exploit-db.com/papers/33930
[2014] - Revisiting an Old Friend, Shell Globbing - https://insinuator.net/2014/12/revisiting-an-old-friend-shell-globbing/
@Lee_Holmes Who does ls *
?
The problem is clearly between keyboard and chair, not in a 50 year old design.
Also, stop holding your phone like that!
;)
@adamshostack Here you go, IRREFUTABLE evidence that this is a critical issue that impacts people every day :)
@Lee_Holmes It can also be a feature. I have a file named -i in my home directory. Very helpful!
Learn to use -- btw.
@Lee_Holmes I feel left out of your regexps.... it's... touching. 😜
@Lee_Holmes Ive been upset with zsh because it won't do scp with a wildcard in the remote request, but this is definitely worse.
@LinuxAndYarn SCP is worse. It does full shell command substitution.
@Lee_Holmes I remember typing “h*” return might run halt if you were in the right directory.
@Lee_Holmes I don't think of this as "code execution". Bash (and many other shells) do globbing and the like to expand the command they execute. If a filename looks like an argument to the program you are running, then yes, it will act like that argument was specified directly on the command line. Honestly, I find it hard to be scared by this outcome, even though it could be of some possibly misuse.
@brainwagon @Lee_Holmes “some possible misuse” = “one of the more common root causes of serious hacking incidents”
@CarbonCarrot @Lee_Holmes Nope, find doesn’t support it (otherwise it would be too easy). You need to ./*
@Lee_Holmes wait but it does not run thru the command parser right? A file named `; very-evil` will pass that as an argument to ls right?? Right????
@ity No thankfully. Globbing only provides the expanded strings to the parameter parser of the tool itself, although SCP does have the issue you're talking about - https://infosec.exchange/deck/@Lee_Holmes/114474887141631750
@swapgs @Lee_Holmes thx — just read the man page. Nothing new, apparently
@CarbonCarrot @Lee_Holmes and find is not the only offender, there are so many non-POSIX-compliant argument parser out there :)