¿Dónde está la vulnerabilidad de Bash Shellshock en el código fuente?

25

He escuchado sobre el problema Bash Shellshock desde ayer y tengo curiosidad por ver en qué parte del código fuente se produce este problema. He descargado la fuente de Bash 4.2 de aquí .

¿Dónde debería buscar exactamente Shellshock en el código fuente de Bash 4.2?

Pude encontrar este parche para 4.2 (de esto page ), pero aún si alguien pudiera explicar claramente dónde ocurre Shellshock, sería útil.

    
pregunta Jake 27.09.2014 - 16:41
fuente

1 respuesta

34

CVE-2014-6271

CVE-2014-6271 fue la primera vulnerabilidad descubierta. Puede encontrar un parche aquí .

De Wikipedia :

  

Las definiciones de funciones se exportan codificándolas dentro de   lista de variables de entorno como variables cuyos valores comienzan con   paréntesis ("()") seguidos de una definición de función. La nueva instancia   de Bash, al comenzar, escanea su lista de variables de entorno en busca de valores   en este formato y los convierte de nuevo en funciones internas.

     

Bash realiza esta conversión creando un fragmento de código que   define la función y la ejecuta, pero no verifica que el   fragmento es simplemente una definición de función . Por lo tanto cualquiera que pueda   hacer que Bash se ejecute con un par de nombre / valor particular en su   medio ambiente, también puede ejecutar comandos arbitrarios agregando aquellos   comandos a una definición de función exportada.

En el código fuente, podemos ver la importación de las variables de función en variables.c :

/* Initialize the shell variables from the current environment.
   If PRIVMODE is nonzero, don't import functions from ENV or
   parse $SHELLOPTS. */
void
initialize_shell_variables (env, privmode)
     char **env;
     int privmode;
{
  [...]
  for (string_index = 0; string = env[string_index++]; )
    {

      [...]
      /* If exported function, define it now.  Don't import functions from
     the environment in privileged mode. */
      if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))
      {
        [...]
        parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
        [...]
      }
}

Podemos ver un bucle for sobre todas las variables de entorno dadas a la función, y luego un "if" sobre si estamos en modo privilegiado, pero la mayoría de las veces está deshabilitada. La parte "no verificar que el fragmento es simplemente una definición de función" está en la línea parse_and_execute . La descripción de la función de builtins/evalstring.c :

/* Parse and execute the commands in STRING.  Returns whatever
   execute_command () returns.  This frees STRING.  FLAGS is a
   flags word; look in common.h for the possible values.  Actions
   are:
    (flags & SEVAL_NONINT) -> interactive = 0;
    (flags & SEVAL_INTERACT) -> interactive = 1;
    (flags & SEVAL_NOHIST) -> call bash_history_disable ()
    (flags & SEVAL_NOFREE) -> don't free STRING when finished
    (flags & SEVAL_RESETLINE) -> reset line_number to 1
*/
int
parse_and_execute (string, from_file, flags)
     char *string;
     const char *from_file;
     int flags;
{

Por lo tanto, todo lo que se pasa a la función se ejecuta como si fuera un comando bash ordinario. Las marcas SEVAL_NONINT y SEVAL_NOHIST se explican por sí mismas ( explicación de interactividad , NOHIST no agrega la definición a su historial de bash) no impide pasar otras cosas que las definiciones de funciones. El parche introduce los indicadores SEVAL_FUNCDEF y SEVAL_ONECMD que se pueden pasar en el campo de indicadores a parse_and_execute :

+ #define SEVAL_FUNCDEF 0x080       /* only allow function definitions */
+ #define SEVAL_ONECMD  0x100       /* only allow a single command */

El parche también agrega funcionalidad a parse_and_execute para cumplir con esas nuevas banderas, y cambia la llamada a parse_and_execute para pasar esas banderas:

-     parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
+     /* Don't import function names that are invalid identifiers from the
+        environment. */
+     if (legal_identifier (name))
+       parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);

CVE-2014-7169

CVE-2014-7169 se basa en un problema de análisis de funciones que señalado por Tavis Ormandy. La solución de parse.y parece muy simple, pero es más complicada que CVE-2014-6271:

/* Called from shell.c when Control-C is typed at top level.  Or
   by the error rule at top level. */
void
reset_parser ()
  [...]
  FREE (word_desc_to_read);
  word_desc_to_read = (WORD_DESC *)NULL;
+ eol_ungetc_lookahead = 0;
+
  current_token = '\n'; /* XXX */
  last_read_token = '\n';
  token_to_read = '\n';

La variable eol_ungetc_lookahead se explica en su definición:

/* This implements one-character lookahead/lookbehind across physical input
   lines, to avoid something being lost because it's pushed back with
   shell_ungetc when we're at the start of a line. */
static int eol_ungetc_lookahead = 0;

Se lee dentro de la función shell_getc , y si se configura, en su lugar se lee su contenido (de un carácter).

El comando rm echo; env -i X='() { function a .>\' bash -c 'echo date'; cat echo primero crea un error de sintaxis con el carácter . (también puede usar otros caracteres aquí, como a o = ), y luego usa la limpieza insuficiente de la variable eol_ungetc_lookahead en la función reset_parser para inyectar el carácter > en la cadena de "fecha de eco" que también se le da a bash. Es equivalente a rm echo; bash -c '> echo date'; cat echo .

Recursos adicionales en la lista de correo oss-sec .

    
respondido por el user10008 27.09.2014 - 17:33
fuente

Lea otras preguntas en las etiquetas