0

One of my bash scripts contains this test:

if [ ! -v "$3" ]; then
   exit
else
   ...
fi

Is there a way to inject data in $3 argument that will execute something nasty ? (my script is run with suid privilege and contains sensitive variables...)

Thanks

Bob5421
  • 7,757
  • 14
  • 81
  • 175
  • 1
    There's probably a better approach than expecting the caller to know the name of an in-script variable. – chepner Jul 13 '22 at 19:27
  • How can the caller see the content of in an in-script variable ? – Bob5421 Jul 13 '22 at 19:28
  • What is the `-v` operator supposed to mean? I can't find it in the documentation. – Barmar Jul 13 '22 at 19:29
  • The -v is for checking variable existence – Bob5421 Jul 13 '22 at 19:29
  • Can you provide a reference to it? I don't see it in the Bash manual. – Barmar Jul 13 '22 at 19:31
  • 2
    @Barmar: This might help: `help test | grep -- -v` – Cyrus Jul 13 '22 at 19:31
  • 1
    Anyway, I think the answer to your question is no. Since you quote the variable, nothing in it is executed. – Barmar Jul 13 '22 at 19:31
  • Since `-v` is a `bash` extension, I would use it with `[[` instead of `[`, even if `bash`'s built-in `[` supports it. – chepner Jul 13 '22 at 20:21
  • Your test asks if, say, SOME_VARIABLE_NAME is defined. Why would the caller of the script be expected to know the name of a variable to pass as argument 3, rather than having your script look at the value of `$3` and *it* deciding which variable to use in that case? `case $3 of FOO) var=$SOME_VAR;; BAR) var=$SOME_OTHER_VAR;; esac; # use $var in some way` – chepner Jul 13 '22 at 20:28
  • This is a root-me.org challenge ... https://www.root-me.org/fr/Challenges/App-Script/Bash-quoted-expression-injection – CodeOverFlow Sep 11 '22 at 10:53

2 Answers2

5

Yes, there is an injection vulnerability here. The problem is that -v test evaluates its argument as a variable, and in bash v4.3 and later that can include an array element (e.g. arrayVar[5]), and since array indexes (for non-associative arrays) are numbers, the index part gets evaluated as an arithmetic context, which can include command substitutions.

So if $3 is something like this:

x[$(touch /tmp/pwned)]

...or, if you're worried about sensitive variables:

x[$(echo "$SensitiveVar" >/tmp/pwned)]

...it'll wind up executing the part inside $( ), with privilege and access to internal shell variables.

Note that since this occurs because of how the -v test is evaluated, quoting $3 doesn't help, and neither does using [[ ]] instead of [ ].

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
  • I can't make this work (bash 4.2) – Fravadona Jul 13 '22 at 21:10
  • 1
    @Fravadona Interesting. The `-v` test was added in bash v4.2, but apparently it didn't handle array element references until v4.3 (see the [release announcement](https://lists.gnu.org/archive/html/bug-bash/2014-02/msg00081.html), point "ll."). Therefore, this injection vulnerability only exists under bash v4.3 and later. – Gordon Davisson Jul 13 '22 at 21:23
  • Thanks for your great answer but there is something i do not understand: If i run "./script.sh ... ... "x[$(echo "$SensitiveVar" >/tmp/pwned)]" : The echo command will be run before script.sh... – Bob5421 Jul 14 '22 at 07:35
  • @Bob5421 Your interactive shell expanding (& executing) the `$( )` because it's only in double-quotes. Use single-quotes to keep the interactive shell from expanding it prematurely. See ["Difference between single and double quotes in Bash"](https://stackoverflow.com/questions/6697753/difference-between-single-and-double-quotes-in-bash). – Gordon Davisson Jul 14 '22 at 09:14
1

No. Since the variable is quoted, it's simply expanded and the result is treated as a single word in the expansion. No further processing is done with the result. Quoting a variable is the sure way to prevent code injection in shell scripts, unless you pass the expansion to something else that executes it. E.g.

ssh hostname "$3"

doesn't execute $3 locally, but the remote server will execute it.

Note that [ -v "$3" ] doesn't test whether $3 is set, it expands $3, which should contain the putative variable name, and tests whether that variable exists. If you want to know whether $3 exists, you would have to use [ -v 3 ] or [ $# -ge 3 ]

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Syntactically you're correct, and while it does answer OPs question, and I agree with your statement(s), I am dubious that this sort of testing is the correct rout. This seems to be an [X Y Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – Zak Jul 13 '22 at 19:50
  • See Gordon's answer. There is an injection vulnerability that can't be addressed just by quoting the variable. – tjm3772 Jul 13 '22 at 20:48