What the absolute fuck: https://yossarian.net/til/post/some-surprising-code-execution-sources-in-bash
In short: [[ "$foo" -eq whatever ]]
in bash can run arbitrary code.
That looks like something that can realistically trigger in a lot of scripts.
(also test -v
, but I barely ever see that one used)
Edit: This also happens in zsh 5.9 (but the referenced variable needs to exist) and mksh
Same issue in zsh.
On my debian system, one script that uses [[ $foo -eq bar ]]
is wg-quick.
The uses I have found so far don't appear to be exploitable because they use local variables or a global they set themselves, or special variables like $#
This is basically a problem of double-evaluation in an "arithmetic context". It might be inherited from ksh.
The thing is this: They finagled arrays into posix script, which does not have them.
They use the awkward ${foo[1]}
indexing syntax, but then add a shortcut to make arithmetic less odious, so you can do $((foo[1] + 1))
(look ma, no $
!).
And then they apparently decided that that would also run substitutions.
And then, they decided that those substitutions would run after expansion. So if the variable "x" contains foo[$(echo 1)]
and is used in $((x + 1))
, it is first expanded to $((foo[$(echo 1)] + 1))
,
and then it is expanded a second time, which runs the echo 1
code.
And then they added that to [[ ]]
, the improved test
/ [ ]
.
@bean my reaction is a confusing combination of "huh, didn't know that one" and "nothing surprises me about shells finding executable meaning in anything, they're like Qanon conspiracy theorists piecing together hidden messages and codes from the craziest things, and then interpreting them as instructions to blindly follow"
It's not just double evaluation.
It's fully recursive!
( for i in {1..101}; do echo foo$i=foo$((i + 1)); done; echo foo101="'a[\$(echo hahaha >&2)]'"; echo '[[ "$foo1" -eq 5 ]]' ) | bash
This writes a script with 101 variables, each pointing to the next, except for $foo101 which contains the payload.
Wait this doesn't even need the "$foo1"
. Just foo1
is enough.
As soon as you do -eq
or any other numeric operator, it will expand the operands as far as it can until it finally reaches a number.
So, yeah: If you're using any ksh-flavored shell, don't use [[
with any of the numeric operators (-eq
, -lt
, -le
, -gt
, -ge
) or -v
.
Ideally never, use [
for those.
(bash taps out at 1024 levels of recursion, zsh at 257, the coward)