¿El comportamiento detrás de la vulnerabilidad Shellshock en Bash está documentado o es intencional?

Se reveló una vulnerabilidad reciente, CVE-2014-6271 , sobre cómo interpreta Bash las variables de entorno. El exploit se basa en el análisis de Bash de algunas declaraciones de variables de entorno como definiciones de funciones, pero luego sigue ejecutando el código siguiendo la definición:

$ x='() { echo i do nothing; }; echo vulnerable' bash -c ':' vulnerable 

Pero no lo entiendo No hay nada que haya podido encontrar en el manual de Bash sobre la interpretación de variables de entorno como funciones (excepto para funciones heredadas , que es diferente). De hecho, una definición adecuada de función nombrada simplemente se trata como un valor:

 $ x='y() { :; }' bash -c 'echo $x' y() { :; } 

Pero uno corrupto no imprime nada:

 $ x='() { :; }' bash -c 'echo $x' $ # Nothing but newline 

La función corrupta no tiene nombre, por lo que no puedo simplemente llamarla. ¿Es esta vulnerabilidad un error de implementación pura, o hay una función prevista aquí, que simplemente no puedo ver?

Actualizar

Por el comentario de Barmar, hipoteticé que el nombre de la función era el nombre del parámetro:

 $ n='() { echo wat; }' bash -c 'n' wat 

Lo cual podría jurar que lo intenté antes, pero supongo que no lo intenté lo suficiente. Es repetible ahora. Aquí hay un poco más de prueba:

 $ env n='() { echo wat; }; echo vuln' bash -c 'n' vuln wat $ env n='() { echo wat; }; echo $1' bash -c 'n 2' 3 -- 4 wat 

… así que aparentemente los argumentos no están establecidos en el momento en que se ejecuta el exploit.

De todos modos, la respuesta básica a mi pregunta es, sí, así es como Bash implementa funciones heredadas .

Esto parece un error de implementación.

Aparentemente, la forma en que las funciones exportadas funcionan en bash es que usan variables de entorno especialmente formateadas. Si exporta una función:

 f() { ... } 

define una variable de entorno como:

 f='() { ... }' 

Lo que probablemente está sucediendo es que cuando el nuevo shell ve una variable de entorno cuyo valor comienza por () , antepone el nombre de la variable y ejecuta la cadena resultante. El error es que esto incluye ejecutar cualquier cosa después de la definición de la función también.

La solución descrita aparentemente es analizar el resultado para ver si es una definición de función válida. De lo contrario, imprime la advertencia sobre el bash de definición de función no válida.

Este artículo confirma mi explicación de la causa del error. También entra un poco más de detalle sobre cómo la solución lo resuelve: no solo analizan los valores más cuidadosamente, sino que las variables que se utilizan para pasar las funciones exportadas siguen una convención de nomenclatura especial. Esta convención de nomenclatura es diferente de la utilizada para las variables de entorno creadas para scripts CGI, por lo que un cliente HTTP nunca debería ser capaz de entrar en esta puerta.

El seguimiento:

 x='() { echo I do nothing; }; echo vulnerable' bash -c 'typeset -f' 

huellas dactilares

 vulnerable x () { echo I do nothing } declare -fx x 

Parece, que Bash, después de haber analizado el x=... , lo descubrió como una función, lo exportó, vio el declare -fx x y permitió la ejecución del comando después de la statement.

echo vulnerable

 x='() { x; }; echo vulnerable' bash -c 'typeset -f' 

huellas dactilares:

 vulnerable x () { echo I do nothing } 

y ejecutando la x

 x='() { x; }; echo Vulnerable' bash -c 'x' 

huellas dactilares

 Vulnerable Segmentation fault: 11 

segfaults – infinitas llamadas recursivas

No anula la función ya definida

 $ x() { echo Something; } $ declare -fx x $ x='() { x; }; echo Vulnerable' bash -c 'typeset -f' 

huellas dactilares:

 x () { echo Something } declare -fx x 

por ejemplo, la x sigue siendo la función previamente definida (correctamente).

Para el 4.3.25(1)-release Bash 4.3.25(1)-release la vulnerabilidad está cerrada, por lo que

 x='() { echo I do nothing; }; echo Vulnerable' bash -c ':' 

huellas dactilares

 bash: warning: x: ignoring function definition attempt bash: error importing function definition for `x' 

pero – lo que es extraño (al menos para mí)

 x='() { x; };' bash -c 'typeset -f' 

IMPRESIONES AÚN

 x () { x } declare -fx x 

y el

 x='() { x; };' bash -c 'x' 

la segmentación falla también, por lo que TODAVÍA acepta la extraña definición de función …

Creo que vale la pena mirar el código Bash en sí mismo. El parche da una idea del problema. En particular,

 *** ../bash-4.3-patched/variables.c 2014-05-15 08:26:50.000000000 -0400 --- variables.c 2014-09-14 14:23:35.000000000 -0400 *************** *** 359,369 **** strcpy (temp_string + char_index + 1, string); ! if (posixly_correct == 0 || legal_identifier (name)) ! parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST); ! ! /* Ancient backwards compatibility. Old versions of bash exported ! functions like name()=() {...} */ ! if (name[char_index - 1] == ')' && name[char_index - 2] == '(') ! name[char_index - 2] = '\0'; if (temp_var = find_function (name)) --- 364,372 ---- strcpy (temp_string + char_index + 1, string); ! /* Don't import function names that are invalid identifiers from the ! environment, though we still allow them to be defined as shell ! variables. */ ! if (legal_identifier (name)) ! parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD); if (temp_var = find_function (name)) 

Cuando Bash exporta una función, se muestra como una variable de entorno, por ejemplo:

 $ foo() { echo 'hello world'; } $ export -f foo $ cat /proc/self/environ | tr '\0' '\n' | grep -A1 foo foo=() { echo 'hello world' } 

Cuando un nuevo proceso de Bash encuentra una función definida de esta manera en su entorno, evalúa el código en la variable usando parse_and_execute() . Para el código normal, no malicioso, ejecutarlo simplemente define la función en Bash y continúa. Sin embargo, dado que se pasa a una función de ejecución genérica, Bash analizará y ejecutará correctamente el código adicional definido en esa variable después de la definición de la función.

Puede ver que en el nuevo código, se ha agregado un indicador llamado SEVAL_ONECMD que le dice a Bash que solo evalúe el primer comando (es decir, la definición de la función) y SEVAL_FUNCDEF para permitir solo las definiciones de la función.

Con respecto a su pregunta sobre la documentación, observe aquí en la documentación de la línea de comando para el comando env , que un estudio de la syntax muestra que env está funcionando como está documentado.

  • Hay, opcionalmente, 4 opciones posibles
  • Un guión opcional como sinónimo de -i (para compatibilidad con versiones anteriores, supongo)
  • Cero o más NAME = VALUE pares. Estas son las asignaciones de variables que podrían incluir definiciones de funciones.
  • Tenga en cuenta que no se requiere punto y coma (;) entre las asignaciones o después de ellas.
  • El último argumento (s) puede ser un comando único seguido de su (s) argumento (s). Se ejecutará con los permisos otorgados al inicio de sesión utilizado. La seguridad se controla mediante la restricción de permisos en el usuario de inicio de sesión y la configuración de permisos en ejecutables accesibles para el usuario de modo que los usuarios que no sean el propietario del ejecutable solo puedan leer y ejecutar el progtwig, no modificarlo.
 [ spot@LX03:~ ] env --help Usage: env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...] Set each NAME to VALUE in the environment and run COMMAND. -i, --ignore-environment start with an empty environment -u, --unset=NAME remove variable from the environment --help display this help and exit --version output version information and exit A mere - implies -i. If no COMMAND, print the resulting environment. Report env bugs to bug-coreutils@gnu.org GNU coreutils home page:  General help using GNU software:  Report env translation bugs to