If I understand correctly, var
is a variable in your programming language.
And in your programming language, you're asking a shell to interpret a string that is the concatenation of "cd "
, the content of that variable and "; echo > create_a_file_here"
.
If then, yes, if the content of var
is not tightly controlled, it's a command injection vulnerability.
You could try and properly quote the content of the variable¹ in the syntax of the shell so it is guaranteed to be passed as a single argument to the cd
builtin.
Another approach would be to pass the content of that variable another way. An obvious way would be to pass that in an environment variable. For instance, in C:
char *var = "; rm -rf /";
setenv("DIR", var, 1);
system("CDPATH= cd -P -- \"$DIR\" && echo something > create_a_file_here");
This time, the code that you ask the shell to interpret is fixed, we still need to write it properly in the shell's syntax (here assumed to be a POSIX-compliant shell):
- shell variable expansion must be quoted to prevent split+glob
- you need
-P
for cd
to do a simple chdir()
- you need
--
to mark the end of options to avoid problems with var
starting with -
(or +
in some shells)
- We set
CDPATH
to the empty string in case it was is in the environment
- We only run the
echo
command if cd
was successful.
There is (at least) one remaining problems: if var
is -
, it doesn't chdir into the directory called -
but to the previous directory (as stored in $OLDPWD
) and OLDPWD=- CDPATH= cd -P -- "$DIR"
is not guaranteed to work around it. So you'd need something like:
system(
"case $DIR in\n"
" (-) CDPATH= cd -P ./-;;\n"
" (*) CDPATH= cd -P -- \"$DIR\";;\n"
"esac && ....");
¹ Note that just doing a system(concat("cd \"", var, "\"; echo..."));
is not the way to go, you'd be just moving the problem.
For instance, a var = "$(rm -rf /)"
would still be a problem.
The only reliable way to quote text for Bourne-like shells is to use single quotes and also take care of the single quotes that may occur in the string. For instance, turn a char *var = "ab'cd"
to char *escaped_var = "'ab'\\''cd'"
. That is, replace all '
to '\''
and wrap the whole thing inside '...'
.
That still assumes that that quoted string is not used within backticks, and you'd still need the --
, -P
, &&
, CDPATH=
...
var
as an argument. E.g. callingsh
with arguments-c
,'cd "$1"; echo > create_a_file_here'
,'sh'
,var
works and does not need any changes tovar
. The'sh'
argument is passed as$0
. – ipsec Jan 09 '18 at 19:38sh
, or creating your own programming language with a similar syntax but which expandsvar
instead of requiring you to writecd "$var"
? Or is thisbash
withshopt -s cdable_vars
? Oh, I think you mean that some other program forks a shell to run these commands. So just quotevar
, but make sure it doesn't include a quote character itself... – Peter Cordes Jan 10 '18 at 03:40s='"'; echo "$s"
prints"
. – wjandrea Jan 10 '18 at 07:00var=untrusted string
in the parent program, sovar
is an environment variable that's already set when invokingsh
. Then you just need to quote it every time you expand it, which is possible to do reliably. Ah, I see that idea is already part of Stéphane's answer >.< – Peter Cordes Jan 10 '18 at 09:03