用字符串变量保护Shell命令


9

在一种编程语言中,我执行一个简单的shell命令

cd var; echo > create_a_file_here

var是一个变量,其中包含一个字符串(希望)到我要在其中创建文件“ create_a_file_here”的地方的目录。现在,如果有人看到这一行代码,则可以通过分配例如来利用它:

var = "; rm -rf /"

事情可能变得很丑。避免上述情况的一种方法可能是在var中的字符串中搜索一些特殊字符,例如';'。在执行shell命令之前,但是我怀疑这是否涵盖了所有可能的漏洞利用。

有谁知道一种确保“ cd var”仅更改目录而没有其他更改的好方法?


4
根据调用外壳的方式,还可以将其var作为参数传递。例如,调用sh带参数-c'cd "$1"; echo > create_a_file_here''sh'var作品并不需要任何修改var。该'sh'参数作为传递$0
ipsec

1
什么编程语言?您是在使用POSIX sh,还是使用类似的语法创建自己的编程语言,但是这种语言会扩展var而不需要您编写cd "$var"?或者,这是bashshopt -s cdable_vars?哦,我认为您的意思是其他一些程序派生一个shell来运行这些命令。因此,只需引用var,但请确保它本身不包含引号字符...
Peter Cordes

@PeterCordes如果您正在谈论Bash,则可以使用包含双引号的双引号变量。例如s='"'; echo "$s"印刷品"
wjandrea

@WJAndrea:是的,但是当从不受信任的输入构造变量分配时,没有“王牌”引号不能被击败。噢,解决方案:var=untrusted string在父程序中执行,var调用时已经设置的环境变量也是如此sh。然后,您只需要在每次扩展时都引用它,就可以可靠地做到这一点。嗯,我知道这个想法已经成为Stéphane的回答的一部分>。<
Peter Cordes

Answers:


9

如果我理解正确,var则是您的编程语言中的变量。

而在您的编程语言中,您正在要求外壳程序解释一个字符串,该字符串是该"cd "变量和的内容的串联"; echo > create_a_file_here"

如果是的话,是的,如果var没有严格控制的内容,那就是命令注入漏洞。

您可以尝试在Shell的语法中正确引用变量¹的内容,以确保将其作为单个参数传递给cd内置函数。

另一种方法是通过另一种方式传递该变量的内容。一个明显的方法是在环境变量中传递它。例如,在C中:

char *var =  "; rm -rf /";
setenv("DIR", var, 1);
system("CDPATH= cd -P -- \"$DIR\" && echo something > create_a_file_here");

这次,您要求外壳程序解释的代码是固定的,我们仍然需要使用外壳程序的语法(此处假定是符合POSIX的外壳程序)正确编写代码:

  • shell变量扩展必须加引号,以防止split + glob
  • 你需要-Pcd做一个简单的chdir()
  • 您需要--标记选项的结尾,以避免使用var开头-(或+在某些shell中)出现问题
  • 我们设置CDPATH为空字符串,以防它在环境中
  • 我们仅在成功的情况下运行echo命令cd

仍然存在(至少)一个问题:如果var-,则它不会chdir进入所调用的目录,-而是进入先前的目录(存储在中$OLDPWD),OLDPWD=- CDPATH= cd -P -- "$DIR"并且不能保证能解决该问题。因此,您需要以下内容:

system(
  "case $DIR in\n"
  " (-) CDPATH= cd -P ./-;;\n"
  " (*) CDPATH= cd -P -- \"$DIR\";;\n"
  "esac && ....");

¹请注意,只是做了system(concat("cd \"", var, "\"; echo..."));不是要走的路,你只是移动的问题。

例如,a var = "$(rm -rf /)"仍然是一个问题。

为类似Bourne的shell引用文本的唯一可靠方法是使用单引号,并且还要注意字符串中可能出现的单引号。例如,将char *var = "ab'cd"转到char *escaped_var = "'ab'\\''cd'"。也就是说,将所有内容替换''\''并将整个内容包装在内'...'

这仍然假定该引用字符串不是反引号内使用,你仍然需要的---P&&CDPATH=...


12

简单的解决方案:不要从程序中调用Shell。完全没有

这里的示例很简单,无论使用哪种编程语言,更改目录和创建文件都应该很容易。但是,即使您确实需要运行外部命令,通常也不需要通过外壳执行该命令。

因此,如在Python,而不是运行os.system("somecmd " + somearg),使用subprocess.run(["somecmd", somearg])。在C中system(),使用fork()exec()(或查找执行该操作的库)代替C。

如果需要使用shell,请引用命令行参数,或将其传递给环境,如Stéphane的answer所示。另外,如果您担心特殊字符,则正确的解决方案是不要尝试过滤掉(黑名单)潜在危险字符,而只保留已知的安全字符(白名单)。

只允许您知道其功能的字符,这样就可以减少遗漏某些东西的风险。最终结果可能是您只决定允许[a-zA-Z0-9_],但这仅足以完成工作。您可能还需要检查您的语言环境和工具集是否不包含诸如ä和这样的带重音的字母ö。它们可能不会被任何shell认为是特殊的,但是同样,最好确保它们是否通过。


1
并确保您用来匹配的内容[a-zA-Z0-9]不包括诸如某些语言àbash在某些语言环境中,某些shell也可能会误解其编码)之类的内容。
斯特凡Chazelas

@StéphaneChazelas(出于兴趣:)他们是(并且在那些受影响的地区下吗?)
Wilf

10

在一种编程语言中,应该有比执行shell命令更好的方法。例如,用cd var与您的编程语言相当的替代替换chdir (var);应确保var仅具有值的任何欺骗行为都将导致“找不到目录”错误,而不是意外的和可能的恶意操作。

另外,您可以使用绝对路径而不是更改目录。只需连接目录名称,斜杠和您要使用的文件名。

在C语言中,我可能会执行以下操作:

char filepath[PATH_MAX];  /* alternative constant: MAXPATHLEN */

/* Join directory name in var and the filename, guarding against exceeding PATH_MAX */
snprintf (filepath, PATH_MAX, "%s/%s", var, "create_a_file_here");

/* create an empty file/truncate an existing one */
fclose (fopen (filepath, "w") );

您的编程语言肯定可以做类似的事情吗?


感谢您的回答,但不幸的是,我必须对bash命令使用替代方法。我测试了引用该变量的时间-正如专家建议的那样-似乎有效!
ES___
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.