La vulnerabilidad ShellShock ha traído un nuevo caos a Internet, miles de servidores se ven comprometidos por esta vulnerabilidad, la cual dispone de diversos CVE. Si echamos un ojo por Internet podemos observar como la vulnerabilidad ha sido explotada en diversos entornos, para explotar servidores web y meter shels, a través de VMWare Fusion en los OS X de Apple, o para distribuir malware como Kaiten.
En el congreso de seguridad informática Navaja Negra, celebrado en Albacete, yo quería que la gente que asistía al workshop de Metasploit pudiera entender y ver como se corresponden los módulos que ellos pueden configurar con el código que podemos desarrollar. Para esto quise tomar como base la vulnerabilidad, con el primer CVE, de ShellShock.
Mi idea fue desarrollar un módulo en vivo para explotar dicha vulnerabilidad, ya que era realmente sencillo realizarlo, y los asistentes podrían fácilmente guiarse, tal y como se pudo ver en el artículo de RetroMalware para controlar NetBus desde Metasploit. Es cierto que la gente de Rapid7 ya tiene sus módulos sobre esta vulnerabilidad realizada, pero mi idea era hacerlo un poco más sencillo para que cualquiera de los asistentes con nociones cero de Ruby pudieran seguirlo.
¿Qué necesitamos para llevar a cabo el módulo? En la imagen se puede ver que al menos la función de inicialización y la función exploit son necesarias. El objetivo de estos módulos son las de conseguir una sesión para controlar el equipo o realizar alguna acción sobre él, tras aprovechar una vulnerabilidad. Opcionalmente, podemos definir la función check, con la que podemos chequear que una vulnerabilidad existe en la máquina remota, siempre y cuando el módulo no sea client-side, ya que en este escenario no tiene sentido realizar un chequeo.
La función: initialize(info={})
Esta función permite inicializar valores al módulo y actualizar información que es heredada por el propio framework. Podemos entender que la información de ayuda e informativa que debemos proporcionar en los módulos de Metasploit debemos configurarla en esta función. Por ejemplo, cuando nosotros ejecutamos el comando info la información proporcionada por la consola se corresponde con el atributo description que previamente hemos definido, o la información sobre el autor, las referencias a los CVE, etcétera.
A continuación se presenta el código, cuya descripción corresponde con la del módulo de Rapid7. Simplemente es importante ver que en esta parte del código son datos a rellenar, y que estos datos son informativos. Hay que recordar que la función de inicialización puede tener más instrucciones relevantes, como veremos después.
Hasta aquí, no hemos tocado nada. Existe un método denominado register_options con el que podemos modificar los atributos configurables que tendrá mi módulo. Recordemos que por ser un módulo de tipo exploit remoto se heredan atributos propios del módulo, como por ejemplo RHOST, pero en muchas ocasiones nosotros querremos añadir atributos configurables para que un usuario pueda realizar otro tipo de acciones con esos parámetros.
Nosotros queremos varias cosas en nuestro módulo:
En la imagen podemos ver que cada atributo aparte del nombre tiene una serie de información extra introducida en un listado. El primer campo true o false indica si el atributo será requerido para ejecutar el módulo o no. Cuando se ejecuta un show options vemos una columna denominada required, dónde los atributos tienen valor yes o no. El segundo campo del listado es la descripción del atributo, mientras que el tercero es el valor por defecto que tiene ese parámetro.
La función: request(command)
Antes de empezar a destripar las funciones check y exploit vamos a necesitar una función request para agilizar y no repetir código en el envío de peticiones. Esta función será utilizada para explotar la vulnerabilidad de ShellShock en su versión para Apache mod_cgi.
La función tiene una implementación básica, utiliza el método send_request_cgi para enviar la petición HTTP. Se le pasa un parámetro a la función que es el comando que se quiere ejecutar en remoto, si la vulnerabilidad está presente en el servidor remoto. A continuación se muestra el código sencillo de la función.
El atributo TARGETURI, METHOD y TIMEOUT, explicados anteriormente, son utilizados para la generación del paquete.
La función: check()
La función check permitirá comprobar si el servidor remoto es vulnerable sin necesidad de dañar o aprovecharse del sistema remoto. Es cierto que check lo que está realizando es una ejecución de comandos remota, pero lo que ejecutaremos será un simple echo hola, que intentaremos ver reflejado en el body de la respuesta.
Como puede verse en la función se llama a request con el comando echo hola. Si la respuesta incluye hola en el cuerpo es vulnerable. Tenemos que tener cuidado, porque si, lógicamente, la respuesta incluyera el texto “hola” porque la web tuviera dicha palabra nos aparecería como vulnerable. Lo ideal sería generar un hash o un texto que fuera “imposible” encontrar en la respuesta.
La función: exploit()
Esta función la tenemos pensada para dos cosas en esta prueba de concepto. La primera es que nos permita ejecutar comandos, por así decirlo línea a línea o petición a petición con el servidor. El segundo modo de funcionamiento se tiene pensado para que automáticamente genere las peticiones necesarias realizando lo siguiente:
En el código se puede ver como se genera el payload mediante la instrucción payload.encoded_exe. Este payload se codifica en base64 almacenándolo en la variable enc. Es importante realizar el cambio de los “\n” en el base64 para que la shellcode no se rompa.
Después podemos observar las 4 peticiones que se realizan con lo comentado anteriormente. Una vez se termina la cuarta petición la shellcode se genera y se obtiene el control remoto de la máquina, si el payload seleccionado es para tomar el control, por ejemplo un meterpreter.
Configuración y ejecución
Ahora vamos a probar el módulo programado, cuyo código se puede encontrar en mi github. La configuración para probar el código en modo FULL a true, será el siguiente:
Si elegimos la opción FULL = false, realmente podemos seleccionar en COMMAND que binario lanzar, y con RPATH cuál es la ruta remota dónde se encuentra. En el taller de Navaja Negra lo estuvimos viendo, y con las prisas las cosas no quedaron del todo claras, por eso decidí hacer este post.
Autor: Pablo González Pérez (@pablogonzalezpe)
Escritor del libro "Metasploit para Pentesters" 3ª Ed.
Figura 1: Crea tu módulo de Metasploit para Shellshock |
En el congreso de seguridad informática Navaja Negra, celebrado en Albacete, yo quería que la gente que asistía al workshop de Metasploit pudiera entender y ver como se corresponden los módulos que ellos pueden configurar con el código que podemos desarrollar. Para esto quise tomar como base la vulnerabilidad, con el primer CVE, de ShellShock.
Mi idea fue desarrollar un módulo en vivo para explotar dicha vulnerabilidad, ya que era realmente sencillo realizarlo, y los asistentes podrían fácilmente guiarse, tal y como se pudo ver en el artículo de RetroMalware para controlar NetBus desde Metasploit. Es cierto que la gente de Rapid7 ya tiene sus módulos sobre esta vulnerabilidad realizada, pero mi idea era hacerlo un poco más sencillo para que cualquiera de los asistentes con nociones cero de Ruby pudieran seguirlo.
Figura 2: Cosas básicas para hacer un módulo de tipo exploit remoto |
¿Qué necesitamos para llevar a cabo el módulo? En la imagen se puede ver que al menos la función de inicialización y la función exploit son necesarias. El objetivo de estos módulos son las de conseguir una sesión para controlar el equipo o realizar alguna acción sobre él, tras aprovechar una vulnerabilidad. Opcionalmente, podemos definir la función check, con la que podemos chequear que una vulnerabilidad existe en la máquina remota, siempre y cuando el módulo no sea client-side, ya que en este escenario no tiene sentido realizar un chequeo.
La función: initialize(info={})
Esta función permite inicializar valores al módulo y actualizar información que es heredada por el propio framework. Podemos entender que la información de ayuda e informativa que debemos proporcionar en los módulos de Metasploit debemos configurarla en esta función. Por ejemplo, cuando nosotros ejecutamos el comando info la información proporcionada por la consola se corresponde con el atributo description que previamente hemos definido, o la información sobre el autor, las referencias a los CVE, etcétera.
A continuación se presenta el código, cuya descripción corresponde con la del módulo de Rapid7. Simplemente es importante ver que en esta parte del código son datos a rellenar, y que estos datos son informativos. Hay que recordar que la función de inicialización puede tener más instrucciones relevantes, como veremos después.
Figura 3: Función de inicialización |
Hasta aquí, no hemos tocado nada. Existe un método denominado register_options con el que podemos modificar los atributos configurables que tendrá mi módulo. Recordemos que por ser un módulo de tipo exploit remoto se heredan atributos propios del módulo, como por ejemplo RHOST, pero en muchas ocasiones nosotros querremos añadir atributos configurables para que un usuario pueda realizar otro tipo de acciones con esos parámetros.
Nosotros queremos varias cosas en nuestro módulo:
- El usuario pueda indicar cuál es la URI. Al atributo lo llamaremos TARGETURI.
- Que el usuario pueda seleccionar el método HTTP a utilizar (GET | POST). El atributo se llama METHOD.
- Que el usuario pueda indicar al exploit el path remoto que debe utilizar mediante el atributo RPATH.
- El usuario puede indicar el comando que quiere lanzar mediante el atributo COMMAND.
- Mediante la configuración del atributo TIMEOUT se indica el número de segundos para obtener respuesta de una petición HTTP.
- El atributo FULL es algo especial. Lo que queremos hacer es que si el parámetro FULL vale false, el módulo se comporte como una consola remota en la cual sólo se ejecutará la orden que se introduzca en COMMAND. Pero si el atributo FULL vale true, el módulo estará programado para lanzar una secuencia de acciones sobre el servidor remoto con el que se conseguirá subir una shellcode y obtendremos el control remoto de la máquina.
- El atributo NAMESHELLBIN será utilizado en caso de que FULL sea true, y proporciona el nombre que utilizaremos para crear el binario en la máquina remota.
Figura 4: Opciones nuevas en el módulo |
En la imagen podemos ver que cada atributo aparte del nombre tiene una serie de información extra introducida en un listado. El primer campo true o false indica si el atributo será requerido para ejecutar el módulo o no. Cuando se ejecuta un show options vemos una columna denominada required, dónde los atributos tienen valor yes o no. El segundo campo del listado es la descripción del atributo, mientras que el tercero es el valor por defecto que tiene ese parámetro.
Figura 5: Atributos del módulo visto con show options |
La función: request(command)
Antes de empezar a destripar las funciones check y exploit vamos a necesitar una función request para agilizar y no repetir código en el envío de peticiones. Esta función será utilizada para explotar la vulnerabilidad de ShellShock en su versión para Apache mod_cgi.
La función tiene una implementación básica, utiliza el método send_request_cgi para enviar la petición HTTP. Se le pasa un parámetro a la función que es el comando que se quiere ejecutar en remoto, si la vulnerabilidad está presente en el servidor remoto. A continuación se muestra el código sencillo de la función.
Figura 6: Código de request |
El atributo TARGETURI, METHOD y TIMEOUT, explicados anteriormente, son utilizados para la generación del paquete.
La función: check()
La función check permitirá comprobar si el servidor remoto es vulnerable sin necesidad de dañar o aprovecharse del sistema remoto. Es cierto que check lo que está realizando es una ejecución de comandos remota, pero lo que ejecutaremos será un simple echo hola, que intentaremos ver reflejado en el body de la respuesta.
Figura 7: Código de check |
Como puede verse en la función se llama a request con el comando echo hola. Si la respuesta incluye hola en el cuerpo es vulnerable. Tenemos que tener cuidado, porque si, lógicamente, la respuesta incluyera el texto “hola” porque la web tuviera dicha palabra nos aparecería como vulnerable. Lo ideal sería generar un hash o un texto que fuera “imposible” encontrar en la respuesta.
La función: exploit()
Esta función la tenemos pensada para dos cosas en esta prueba de concepto. La primera es que nos permita ejecutar comandos, por así decirlo línea a línea o petición a petición con el servidor. El segundo modo de funcionamiento se tiene pensado para que automáticamente genere las peticiones necesarias realizando lo siguiente:
1. Generar una shellcode, que definirá el usuario en el atributo PAYLOAD antes de lanzar el módulo, es decir, antes de lanzar el método exploit.
2. Esta shellcode se transforma a base64 con la intención de poder “pegarla” con un echo en un archivo del servidor remoto. La instrucción a ejecutar en remoto sería algo tal que así echo shellcode_en_base_64 > /var/tmp/fichero_almacena_shellcode_base64.
3. Una vez se dispone de la shellcode en un fichero en base64 se realiza su transformación a binario y se le cambia los permisos para que el nuevo binario pueda ejecutar.
4. Por último, se realiza una petición para ejecutar ese binario, el cual lanzará la shellcode. En función del tipo de shellcode se realizará unas acciones u otras. Automáticamente el módulo de Metasploit nos lanzará por debajo el handler con el que podremos gestionar de forma trasparente las conexiones con las shellcode.
Figura 8: Generación, subida y ejecución de Shellcode, Toma de control |
En el código se puede ver como se genera el payload mediante la instrucción payload.encoded_exe. Este payload se codifica en base64 almacenándolo en la variable enc. Es importante realizar el cambio de los “\n” en el base64 para que la shellcode no se rompa.
Después podemos observar las 4 peticiones que se realizan con lo comentado anteriormente. Una vez se termina la cuarta petición la shellcode se genera y se obtiene el control remoto de la máquina, si el payload seleccionado es para tomar el control, por ejemplo un meterpreter.
Configuración y ejecución
Ahora vamos a probar el módulo programado, cuyo código se puede encontrar en mi github. La configuración para probar el código en modo FULL a true, será el siguiente:
- FULL = true.Tras lanzar el módulo con la configuración podemos obtener el control remoto de la máquina, tal y como se puede ver en la imagen.
- NAMESHELLBIN = poc.
- RHOST = dirección IP servidor remoto, en este caso 192.168.56.102.
- TARGETURI = URI remota, en este caso /cgi-bin/vuln.cgi.
- PAYLOAD = linux/x86/meterpreter/reverse_tcp.
- LHOST = dirección IP máquina del atacante.
Figura 9: Configuración y obtención del control remoto a través de ShellShock |
Si elegimos la opción FULL = false, realmente podemos seleccionar en COMMAND que binario lanzar, y con RPATH cuál es la ruta remota dónde se encuentra. En el taller de Navaja Negra lo estuvimos viendo, y con las prisas las cosas no quedaron del todo claras, por eso decidí hacer este post.
Autor: Pablo González Pérez (@pablogonzalezpe)
Escritor del libro "Metasploit para Pentesters" 3ª Ed.
Muy buen tuto. Muchas gracias. La unica pega es que no se cual es tu github, podrias ponerlo?.
ResponderEliminarMuchas gracias.
ResponderEliminarBueno de verdad.
ResponderEliminar