Dove si trova la vulnerabilità di Bash Shellshock nel codice sorgente?

25

Ho sentito parlare del problema Bash Shellshock da ieri e sono curioso di vedere dove codice sorgente questo problema si verifica. Ho scaricato il sorgente per Bash 4.2 da qui .

Dove dovrei cercare Shellshock nel codice sorgente di Bash 4.2?

Sono stato in grado di trovare questa patch informazioni per 4.2 (da questo pagina ), ma se qualcuno potrebbe spiegare chiaramente dove si verifica Shellshock, sarebbe utile.

    
posta Jake 27.09.2014 - 16:41
fonte

1 risposta

33

CVE-2014-6271

CVE-2014-6271 è stata la prima vulnerabilità scoperta. Una patch può essere trovata qui .

Da Wikipedia :

Function definitions are exported by encoding them within the environment variable list as variables whose values begin with parentheses ("()") followed by a function definition. The new instance of Bash, upon starting, scans its environment variable list for values in this format and converts them back into internal functions.

Bash performs this conversion by creating a fragment of code that defines the function and executing it, but it does not verify that the fragment is merely a function definition. Therefore anyone who can cause Bash to execute with a particular name/value pair in its environment, can also execute arbitrary commands by appending those commands to an exported function definition.

Nel codice sorgente, possiamo vedere l'importazione delle variabili di funzione in 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);
        [...]
      }
}

Possiamo vedere un ciclo for su tutte le variabili di ambiente assegnate alla funzione, e poi un se per sapere se siamo in modalità privilegiata, ma che è disabilitato il più delle volte. La parte "non verificare che il frammento sia solo una definizione di funzione" si trova nella riga parse_and_execute . La descrizione della funzione da 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;
{

Quindi tutto ciò che viene passato alla funzione viene eseguito come se fosse un normale comando bash. I flag SEVAL_NONINT e SEVAL_NOHIST sono autoesplicativi ( spiegazione dell'interattività , NOHIST non aggiunge la definizione alla cronologia di bash) non impediscono il passaggio di altre cose oltre alle definizioni di funzione. La patch introduce i flag SEVAL_FUNCDEF e SEVAL_ONECMD che possono essere passati nel campo flags a parse_and_execute :

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

La patch aggiunge anche funzionalità a parse_and_execute per rispettare questi nuovi flag e modifica la chiamata a parse_and_execute per passare quei flag:

-     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-2.014-7.169

CVE-2014-7169 si basa su un problema di analisi delle funzioni che è stato evidenziato da Tavis Ormandy. La correzione di parse.y sembra molto semplice, ma è più complicata di 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 variabile eol_ungetc_lookahead è spiegata alla sua definizione:

/* 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;

È letto all'interno della funzione shell_getc , e se è impostato, il suo contenuto (un carattere) viene invece letto.

Il comando rm echo; env -i X='() { function a .>\' bash -c 'echo date'; cat echo crea prima un errore di sintassi con il carattere . (qui puoi usare anche altri caratteri, come a o = ), e poi usa la pulizia insufficiente della variabile eol_ungetc_lookahead nella funzione reset_parser per iniettare il carattere > nella stringa 'echo date' che è anche data a bash. È equivalente a rm echo; bash -c '> echo date'; cat echo .

Altre risorse sulla mailing list di oss-sec .

    
risposta data 27.09.2014 - 17:33
fonte

Leggi altre domande sui tag