Protección del comando de shell con variable de cadena

9

Dentro de un lenguaje de programación, ejecuto un comando de shell simple

cd var; echo > create_a_file_here

con var es una variable que contiene una cadena de (con suerte) un directorio al lugar donde quiero crear el archivo "create_a_file_here". Ahora, si alguien ve esta línea de código, es posible explotarla asignando, por ejemplo:

var = "; rm -rf /"

Las cosas pueden ponerse bastante feas. Una forma de evitar el caso anterior sería quizás buscar en la cadena en var algunos caracteres especiales como ';' antes de ejecutar el comando de shell, pero dudo que esto cubra todos los posibles exploits.

¿Alguien sabe una buena manera de asegurarse de que "cd var" solo cambie un directorio y nada más?

ES___
fuente
44
Dependiendo de cómo puede invocar el shell, también puede pasarlo varcomo argumento. Por ejemplo, llamando shcon argumentos -c, 'cd "$1"; echo > create_a_file_here', 'sh', varobras y no necesita ningún cambio var. El 'sh'argumento se pasa como $0.
ipsec
1
¿Qué lenguaje de programación? ¿Estás utilizando POSIX sho estás creando tu propio lenguaje de programación con una sintaxis similar pero que se expande en varlugar de requerir que escribas cd "$var"? ¿O es esto bashcon shopt -s cdable_vars? Oh, creo que quiere decir que algún otro programa bifurca un shell para ejecutar estos comandos. Así que solo cita var, pero asegúrate de que no incluya un personaje de cita en sí ...
Peter Cordes
@PeterCordes Si está hablando de Bash, una variable con comillas dobles que contiene comillas dobles está bien. Por ejemplo, s='"'; echo "$s"impresiones ".
wjandrea
@WJAndrea: sí, pero no hay una cita de "carta de triunfo" que no se pueda derrotar al construir una asignación variable a partir de una entrada no confiable. Oh, solución: hazlo var=untrusted stringen el programa padre, por lo que vares una variable de entorno que ya está establecida al invocar sh. Entonces solo necesita citarlo cada vez que lo expande, lo cual es posible de manera confiable. Ah, veo que esa idea ya es parte de la respuesta de Stéphane>. <
Peter Cordes

Respuestas:

9

Si entiendo correctamente, vares una variable en su lenguaje de programación.

Y en su lenguaje de programación, le está pidiendo a un shell que interprete una cadena que es la concatenación de "cd ", el contenido de esa variable y "; echo > create_a_file_here".

Si es así, sí, si el contenido de varno está estrictamente controlado, es una vulnerabilidad de inyección de comando.

Puede intentar citar correctamente el contenido de la variable¹ en la sintaxis del shell para que se garantice que se pase como un argumento único al cdbuiltin incorporado.

Otro enfoque sería pasar el contenido de esa variable de otra manera. Una forma obvia sería pasar eso en una variable de entorno. Por ejemplo, en C:

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

Esta vez, el código que le pide al intérprete que interprete es fijo, aún necesitamos escribirlo correctamente en la sintaxis del intérprete (aquí se supone que es un intérprete compatible con POSIX):

  • la expansión de la variable de shell se debe citar para evitar dividir + glob
  • lo que necesita -Ppara cdhacer un simplechdir()
  • debe --marcar el final de las opciones para evitar problemas al varcomenzar -(o +en algunos shells)
  • Configuramos CDPATHla cadena vacía en caso de que esté en el entorno
  • Nosotros sólo corremos el echocomando si cdse ha realizado correctamente.

Hay (al menos) un problema restante: si vares así -, no entra en el directorio llamado -sino en el directorio anterior (como está almacenado $OLDPWD) y OLDPWD=- CDPATH= cd -P -- "$DIR"no se garantiza que funcione a su alrededor. Entonces necesitarías algo como:

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

¹ Tenga en cuenta que simplemente hacer un nosystem(concat("cd \"", var, "\"; echo...")); es el camino a seguir, solo estaría moviendo el problema.

Por ejemplo, a var = "$(rm -rf /)"todavía sería un problema.

La única forma confiable de citar texto para shells tipo Bourne es usar comillas simples y también cuidar las comillas simples que pueden aparecer en la cadena. Por ejemplo, gire a char *var = "ab'cd"a char *escaped_var = "'ab'\\''cd'". Es decir, sustituir la totalidad 'de '\''y envolver todo el interior '...'.

Que todavía asume que esa cadena entre comillas no se utiliza dentro de acentos abiertos, y todavía necesitaríamos el --, -P, &&, CDPATH=...

Stéphane Chazelas
fuente
12

Solución simple: no llame al shell desde su programa. En absoluto.

Su ejemplo aquí es trivial, cambiar el directorio y crear archivos debería ser fácil en cualquier lenguaje de programación. Pero incluso si necesita ejecutar un comando externo, generalmente no es necesario hacerlo a través del shell.

Entonces, por ejemplo, en Python, en lugar de ejecutar os.system("somecmd " + somearg), use subprocess.run(["somecmd", somearg]). En C, en lugar de system(), use fork()y exec()(o busque una biblioteca que lo haga).

Si necesita usar el shell, cite los argumentos de la línea de comando o páselos por el entorno, como en la respuesta de Stéphane . Además, si te preocupa el carácter especial, la solución correcta es no tratar de filtrar (lista negra) los caracteres potencialmente peligrosos, sino solo mantener los caracteres que se sabe que son seguros (lista blanca).

Solo permita los personajes cuyas funciones sí conoce, de esa manera hay menos riesgo de perder algo. El resultado final puede ser que solo decidas permitir [a-zA-Z0-9_], pero eso podría ser suficiente para hacer el trabajo. También es posible que desee verificar que su configuración regional y su conjunto de herramientas no incluyan letras acentuadas como äy öen eso. Probablemente no sean considerados especiales por ningún shell, pero de nuevo, es mejor asegurarse de que pasen o no.

ilkkachu
fuente
1
Y asegúrese de que lo que use para comparar [a-zA-Z0-9]no incluya cosas como àcuya codificación también podría ser malinterpretada por algunos shells (como bash) en algunos entornos locales.
Stéphane Chazelas
@ StéphaneChazelas (fuera de interés :) ¿están (y justo debajo de esos lugares afectados?)
Wilf
10

Dentro de un lenguaje de programación, debería haber mejores formas de hacer cosas que ejecutar comandos de shell. Por ejemplo, reemplazar cd varcon el equivalente de su lenguaje de programación chdir (var);debería garantizar que cualquier truco con el valor de varsolo dé como resultado un error de "Directorio no encontrado" en lugar de acciones involuntarias y posiblemente maliciosas.

Además, puede usar rutas absolutas en lugar de cambiar directorios. Simplemente concatene el nombre del directorio, la barra diagonal y el nombre de archivo que desea usar.

En C, podría hacer algo como:

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") );

¿Seguramente tu lenguaje de programación puede hacer algo similar?

telcoM
fuente
Gracias por su respuesta, pero desafortunadamente tengo que usar la solución alternativa con los comandos bash. Probé citando la variable, como sugirió muru, ¡y parece funcionar!
ES___