How to avoid code injection in Jenkins shell calls?

Viewed 114

Consider the following code, which invokes a program (echo) with some arguments:

String malicious_input = '""; rm -rf / #yikes'
sh "echo Hello ${malicious_input}!"

The resulting shell script is then

echo Hello ""; rm -rf / #yikes!

Simple, classic code injection. Nothing unheard. What I have been struggling to find is a way to properly handle this case. First approaches to fix this are:

  • Just add single quotes around the string in the shell call, like sh "echo Hello '${malicious_input}'!". Yes, but no, I only need to switch to malicious_input = "'; rm -rf / #yikes" to circumvent that.
  • Just add double quotes then! Still no, not only are these just as simple to circumvent but those are even prone to path globbing/expansion.
  • Then add the quotes around the input string before invoking Groovy string interpolation. Same thing, the shell commandline is unchanged.
  • Then, add single quotes but prefix every single quote inside the string with a backslash to prevent its interpretation as meta character by the shell. Yes, that kind-of works, if I also escape every existing backslash with a second one. Still, the details of how to prevent this expansion depend a bit on the shell (POSIX-ish, Windows bat, not sure about powershell). Also, this takes three lines of code for every argument. Plus, without an explicit shebang line, I can't even be sure which shell is taken.

So, my question is this: Where is the built-in function in Groovy that does this for me in a portable, shell-agnostic way? I find it hard to believe that this doesn't exist, yet I can't find it. Also, quite puzzling for me that I'm the first one to come across this issue...

1 Answers

What you are describing is called Argument Injection referenced as CWE-88 which is a subclass of Command Injection referenced as CWE-77:

Some potential mitigations described in these CWEs are:

  • If at all possible, use library calls rather than external processes to recreate the desired functionality.
  • (Parameterization) Where possible, avoid building a single string that contains the command and its arguments.
  • (Input Validation) Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does.

The answer to your question:

Where is the built-in function in Groovy that does this for me in a portable, shell-agnostic way?

is : there is no such built-in Groovy function.

What you should do is:

  1. Avoid, when possible, using shell scripts with user input data and use instead Groovy functions.
  2. Use input validation by allowing only alphanumerical characters.
  3. Use quoting as discussed in How to prevent command injection through command options?. This is the less safe option, because the sh Jenkins command use the the system default shell (which can be anything) and there is no 100% guarantee that there is no tricks to bypass this quoting.
Related