Explicación del comando para verificar el shellshock

32

Aquí está el comando que he usado para verificar mi bash shell para el error Shellshock:

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

¿Alguien puede explicar el comando en detalles?

heemayl
fuente
44
Ver también: unix.stackexchange.com/q/157329/70524 - La respuesta de Fixee podría ser útil.
muru

Respuestas:

45

Esta respuesta es un derivado de un artículo original en la Revista Fedora de Matthew Miller, licenciado bajo la licencia Creative Commons Attribution-Share Alike 4.0 .

Dejame explicar:

env x='() { :;}; echo OOPS' bash -c :

Esto imprimirá "OOPS" en un sistema vulnerable, pero saldrá silenciosamente si se ha parcheado bash.

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

Esto imprimirá "OOPS" en un sistema vulnerable, pero imprimirá “this is a test”si bash ha sido parcheado.

Y probablemente haya escuchado que tiene algo que ver con las variables de entorno. Pero, ¿por qué se ejecuta el código en las variables de entorno? Bueno, no se supone que lo sea, pero, debido a una característica que me siento tentada a llamar demasiado inteligente por su propio bien, hay espacio para un defecto. Bash es lo que se ve como un indicador de terminal, pero también es un lenguaje de script y tiene la capacidad de definir funciones. Lo haces así:

$ Ubuntu()  { echo "Ubuntu is awesome."; }

y luego tienes un nuevo comando. Tenga en cuenta que el echoaquí todavía no se ejecuta; simplemente se guarda como lo que sucederá cuando ejecutemos nuestro nuevo comando. ¡Esto será importante en un minuto!

$ Ubuntu
 Ubuntu is awesome.

¡Útil! Pero, digamos, por alguna razón, necesitamos ejecutar una nueva instancia de bash, como un subproceso, y queremos ejecutar mi nuevo comando increíble bajo eso. La declaración bash -c somecommandhace exactamente esto: ejecuta el comando dado en un nuevo shell:

$ bash -c Ubuntu
  bash: Ubuntu: command not found

Oh. Triste. El niño no heredó la definición de la función. Pero sí es inherente al entorno: una colección de pares clave-valor que se han exportado desde el shell. (Este es un concepto completamente diferente; si no está familiarizado con esto, confíe en mí por ahora). Y resulta que bash también puede exportar funciones. Asi que:

$ export -f Ubuntu
$ bash -c Ubuntu
  Ubuntu is awesome.

Lo cual está muy bien, excepto que el mecanismo por el cual esto se logra es algo dudoso . Básicamente, dado que no hay magia de Linux / Unix para realizar funciones en variables de entorno, la función de exportación en realidad solo crea una variable de entorno regular que contiene la definición de la función. Luego, cuando el segundo shell lee el entorno "entrante" y encuentra una variable con contenido que parece una función, la evalúa.

En teoría, esto es perfectamente seguro porque, recuerde, definir una función en realidad no la ejecuta . Excepto, y es por eso que estamos aquí, hubo un error en el código donde la evaluación no se detuvo cuando se llegó al final de la definición de la función. Simplemente sigue yendo.

Eso nunca sucedería cuando la función almacenada en una variable de entorno se haga legítimamente con export -f. Pero, ¿por qué ser legítimo? Un atacante puede inventar cualquier variable de entorno antigua, y si parece una función, ¡los nuevos shells bash pensarán que sí!

Entonces, en nuestro primer ejemplo:

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

El envcomando ejecuta un comando con un conjunto de variables dado. En este caso, estamos configurando xalgo que parece una función. La función es solo una :, que en realidad es un comando simple que se define como no hacer nada. Pero luego, después de lo semi-colonque señala el final de la definición de la función, hay un echocomando. Se supone que eso no debe estar allí, pero no hay nada que nos impida hacerlo.

Entonces, el comando dado para ejecutarse con este nuevo entorno es un nuevo shell bash, nuevamente con un comando " echo this is a test" o "no hacer nada :", después del cual saldrá, completamente inofensivo.

Pero - ¡Uy! Cuando ese nuevo shell se inicia y lee el entorno, llega a la xvariable y, como parece una función, la evalúa. La definición de la función se carga de forma inofensiva, y luego también se activa nuestra carga maliciosa. Por lo tanto, si ejecuta lo anterior en un sistema vulnerable, se le devolverá la “OOPS”impresión. O, un atacante podría hacer mucho peor que simplemente imprimir cosas.

αғsнιη
fuente
1
Muchas gracias por una excelente explicación de por qué esto funciona.
Doug R.
2
Tenga en cuenta que envno es necesario. Se puede obtener el mismo resultado (pasa / no pasa, dependiendo de si se ha actualizado Bash) utilizando el comando sin ella: x='() { :;}; echo OOPS' bash -c "echo this is a test". Esto se debe a que preceder un comando con una asignación de variable pasa esa variable y su valor al bash -c "..."entorno del comando ( en este caso).
Pausado hasta nuevo aviso.
1
... pero puede ser necesario en algunos de los parches más recientes. Las cosas están cambiando.
Pausado hasta nuevo aviso.
44
@DennisWilliamson Si envel shell desde el que se ejecuta la prueba determina si es necesario o no , no el shell que se está probando. (Estos pueden ser los mismos. Incluso entonces, estamos probando cómo bash procesa su propio entorno). Los shells de estilo Bourne aceptan la NAME=value commandsintaxis; Los depósitos de estilo C (p. Ej. csh, tcsh) No. Entonces, la prueba es un poco más portátil env(a costa de crear a veces confusión sobre cómo funciona).
Eliah Kagan
2

En la versión no parcheadabash , almacena definiciones de funciones exportadas como variables de entorno.

Almacenar una función xcomo,

$ x() { bar; }
$ export -f x

Y verifique su definición como,

$ env | grep -A1 x
x=() {  bar
}

Entonces uno podría explotar esto definiendo sus propias variables de entorno e interpretándolas como definiciones de funciones. Por ejemplo env x='() { :;}'sería tratado como

x() { :;
}

¿Qué hace el comando para verificar shellshock,

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

De man env,

  1. env - ejecutar un programa en un entorno modificado.

  2. :no haga nada más que salidas con estado de salida 0. ver mas

  3. Cuando se inicia una nueva instancia de bash sin parches bash -c "echo this is a test", la variable de entorno creada se trata como una función y se carga. En consecuencia, uno obtiene la salida

    vulnerable
    esto es una prueba

Nota: El eco fuera de la definición de la función se ejecutó inesperadamente durante el inicio de bash. La definición de la función es solo un paso para que la evaluación y la explotación sucedan, la definición de la función en sí y la variable de entorno utilizada son arbitrarias. El shell observa las variables de entorno, ve x, que parece que cumple con las restricciones que conoce sobre cómo se ve una definición de función, y evalúa la línea, ejecutando involuntariamente también el eco (que podría ser cualquier comando, malicioso o no) . Ver también esto

souravc
fuente
Todavía encontré que cualquier función bash definida, si se exporta, se evalúa en el shell secundario en la versión parcheada de bash. Vea esto: chayan @ chayan: ~ / testr $ test () {echo "cualquier cosa"; }; exportación -f prueba; bash -c test Ouput: cualquier cosa Por lo tanto, su respuesta no está dirigida correctamente. La explicación de kasiyA del error como expandir la variable más allá de su definición es correcta, creo.
heemayl
@heemayl este comportamiento es natural. Pero si lo intentas env test='() { echo "anything"; }' bash -c "echo otherthing"verás en la salida otherthing. Eso se corrige en el parche. siéntase libre si aún no estoy claro.
souravc
Por favor déjame claro una vez más. En su último comentario básicamente definimos la función y luego le decimos a bash que ejecute echo. En este ejemplo, no hemos llamado a la función en bash. ¿No tendría esto la misma salida en bash parcheado y no parcheado? Tengo la noción de que el error fue básicamente porque bash estaba ejecutando comandos colocados después de la definición de la función, mientras que la función nunca se llamó a ninguna parte más tarde, por ejemplo, si hacemos esto env test = '() {echo "cualquier cosa"; }; echo "foo" 'bash -c "echo otherthing". Por favor aclararme en este contexto.
heemayl
@heemayl He editado mi respuesta, espero que ahora esté claro. Tienes razón en el ejemplo en mi último comentario que no hemos llamado la función. Pero la diferencia es que en un unpatched bashpuede llamar a la función como está definida, pero en un parche bashla definición en sí no está allí.
souravc
@heemayl: No, eso es incorrecto. Un Bash parcheado aún pasará la definición de la función al entorno del niño. La diferencia que hace el parche es que el código que sigue a la definición de función ( echo vulnerable) no se ejecuta. Tenga en cuenta que en los últimos parches, la función pasada debe tener un prefijo específico ( env 'BASH_FUNC_x()'='() { :;}; echo vulnerable' bash -c "echo this is a test"). Se pueden usar algunos parches más recientes en %%lugar del primero ().
Pausado hasta nuevo aviso.