sábado, mayo 06, 2017

Apache: Server-Side Session Hijacking con Virtual Hosts hackeados (1 de 2)

El artículo de hoy, que por su extensión estará dividido en dos partes, comienza con una pregunta que me surgió el otro día cuando me encontraba mirando cosas por si hay que escribir la siguiente parte de nuestro libro de Hacking Web Technologies y me topé por ahí una aplicación que, a pesar de todas las protecciones que trataba de implementar, permitía subir archivos de cualquier tipo a una carpeta publicada por el servidor web. No hace falta decir que eso no es bueno.

Figura 1:Server-Side Session Hijacking con Virtual Hosts Hackeados (1 de 2)

Un atacante podría aprovechar esta vulnerabilidad para mil cosas distintas y todas perversas: desde alojar o promocionar en host ajeno su propia web de venta ilegal de fármacos, hasta colocar una shell con la que ejecutar comandos de forma remota. Tal interfaz de comandos le permitiría atacar, no sólo a la aplicación vulnerable sino también al propio servidor - de lo que por esta vez nos olvidaremos -, sino también a otras aplicaciones que pudieran estar alojadas en él. Lo cual obliga a hacerse, entre otras, las siguientes preguntas:
- ¿Cómo proteger a una aplicación frente a otras que estén alojadas en el mismo servidor?

- ¿Qué medidas debe implementar la propia aplicación para protegerse de estos problema
s?
Si no se encuentran las respuestas correctas, la seguridad final de todas las aplicaciones podría quedar comprometida. Si sólo se responde a una de ellas, posiblemente queden resquicios por los que alguien se podría colar. Si los desarrolladores dejan de implementar una protección porque la creen innecesaria, dado que "los de sistemas" configuran la seguridad del servidor de forma ideal, más temprano que tarde encontrarán un caso en que su suposición era incorrecta. Incluso aunque la aplicación venga con instrucciones precisas sobre cómo hacerlo. Y si los de sistemas confían en que las aplicaciones que instalan en sus servidores no presentan ni suponen problemas de seguridad... es que llevan poco tiempo en el negocio.

Escenario

Para explorar un poco, me planteé un escenario en el que un servidor web Apache con PHP tiene configurados dos servidores virtuales en dominios distintos. El primer dominio, "webserver.example.com", tiene una aplicación web de la que no se conoce - aún - ninguna vulnerabilidad. El directorio del servidor en que está alojada es "/var/www./html/com". Para no entorpecer demasiado la lectura, se dejará para el final su listado completo. Baste por ahora con señalar que viene de fábrica con tres cuentas de acceso:

Figura 2: Cuentas en el servidor de Apache

Y cuyo script principal "index.php" tiene el siguiente código:

Figura 3: index.php que gestiona los accesos y las sesiones a la aplicación

Dependiendo de si utilizas una cuenta de administrador o una de usuario, la aplicación ofrece distintas funcionalidades, tal y como se puede ver a continuación:

Figura 4: Inicios de sesión con diferentes cuentas de usuario

Del otro dominio, "danger.example.net", se supone que tiene una vulnerabilidad que permite subir ficheros con cualquier extensión. O que, sencillamente, está bajo control de un atacante. El directorio en que está alojado es "/var/www/html/net". En él se habrá colocado una sencilla shell PHP cuyo código se muestra en la siguiente imagen:

Figura 5: Shell PHP en el sitio web controlado por el atacante 

Simple, pero efectiva.

Cuentas separadas

En la configuración por defecto de Apache - aun sin pasar un proceso de fortificación - , éste utilizará una misma cuenta, "www-data" en el caso de Ubuntu, para acceder a ambos servidores virtuales. Unas condiciones en que poco se podrá hacer desde el punto de vista defensivo: La shell será capaz de leer los scripts de la aplicación víctima y obtener sus parámetros de configuración y cuentas de acceso a bases de datos, etcétera. Una información que después se podría utilizar para ejecutar instrucciones, modificar las tablas, u otras acciones de ataque.

Figura 6: lectura de ficheros del servidor

Con esto, el atacante tendría ya ganada la partida. Pero, a meros efectos ilustrativos, es conveniente presentar en este punto un ataque que pone de manifiesto la forma en que PHP usa las cookies. Todo empieza cuando el atacante abre su shell y la utiliza para crear una sesión en "danger.example.net" y poblarla con unas variables similares a las que utiliza la aplicación víctima:

Figura 7: Creación de cookies en la aplicación a través de la shell

Tras pulsar el botón "OK", las instrucciones se ejecutarán y realizarán su cometido. Hecho esto, el atacante abre las herramientas para desarrolladores (las de la tecla F12) y utiliza la consola para obtener el valor de la cookie de sesión:

Figura 8: Accediendo a la cookie generada en el cliente

Entonces, visita "webserver.example.com" y se encuentra con la página de inicio de la aplicación víctima. Pero, en lugar de introducir sus credenciales, vuelve a utilizar las herramientas para desarrolladores. Esta vez para asignar el valor de la cookie de sesión y forzar el valor obtenido en el paso anterior:

Figura 9: Se modifica la cookie de la web víctima con el valor de la cookie en la web del atacante

Ahora, refresca la página con F5 y ¡ya es administrador! Lo cual indica nuestro PHP, cuando recibe una cookie de sesión, no se pregunta de dónde - de qué dominio - le llega.

Figura 10: La web reconoce la cookie como si fuera de este servicio

En las siguientes pruebas se utilizará un módulo de Apache denominado "ruid2" que ayuda a mitigar estas situaciones. Con "ruid2", cada petición HTTP es servida por un proceso que se ejecuta utilizando la cuenta del usuario propietario del fichero correspondiente. O sea, que si el propietario del fichero "index.php" es "usuario", cuando alguien lo solicite, Apache lo abrirá en un proceso con permisos de "usuario".

Y, antes de que alguien pregunte: con "root" se hace una excepción. Los ficheros de "root" se ejecutan con permisos de "www-data". Lo contrario podría tener efectos devastadores. En definitiva, será necesario crear una cuenta de usuario en el sistema para la aplicación víctima y otra para la aplicación maliciosa. Y después asignar propietarios, grupos y permisos de forma que esta última no pueda acceder a los ficheros y directorios de la primera.

Entorno de pruebas

Para experimentar un poco preparé una máquina virtual nueva. Instalé en ella Kubuntu y le dejé instalar las últimas actualizaciones. Después le añadí Apache, PHP y el módulo ruid2 con unas pocas órdenes lanzadas desde la shell:
sudo apt-get update
sudo apt-get install apache2
sudo apt-get install php
sudo apt-get install libapache2-mod-ruid2
Con ello basta para tenerlo todo instalado, configurado y habilitado. A continuación, creé las dos cuentas de usuario a utilizar por las aplicaciones,  con el objeto de tener un entorno fortificado de Apache sobre GNU/Linux.
adduser comadduser net
Para configurar los servidores virtuales, abrí el fichero "/etc/apache2/sites-enabled/000-default.conf" y lo dejé como sigue:

Figura 11: Configuración de virtual hosts

Y para habilitar el uso de ruid2, añadí unas líneas al fichero "/etc/apache2/apache2.conf".

Figura 12: Configuración mod_ruid2.c en Apache

Después tocó reiniciar Apache con un simple sudo service apache2 restart. Finalmente, asigné propietarios, grupos y permisos a la aplicación y la dejé como se ve en la imagen siguiente:

Figura 13: Configuración de permisos en ficheros y carpetas. En general: usuario "com", grupo "com"
y permisos totales sólo para el propietario del fichero.

Las execpciones son el propio directorio principal ("com") y su subidrectorio "js". Porque Apache ("www-data") necesita poder pasar por estos directorios (permiso de "ejecución") para poder llegar a determinados ficheros, como "1.js".

En cuanto al directorio del otro servidor virtual, tenemos algo similar pero utilizando una cuenta distinta:

Figura 14: permisos de ww-data

Eso por lo que respecta al servidor. En cuando a lo que tiene que ver con cliente de las pruebas, que se supone que es el equipo del atacante, habrá que crear unas entradas en el fichero "hosts":
192.168.94.10 webserver.example.com
192.168.94.10 danger.example.net
Esa fue la IP que yo utilicé para mi máquina virtual. Si tú utilizas otra(s)...

Cookies y permisos

Por defecto, PHP usa ficheros para almacenar los valores de las variables de sesión. La shell PHP permite comprobar si así ocurre en este caso particular:

Figura 15: Protección de directorios para las variables de sesión

Como cabe esperar, el directorio utilizado para ello tendrá unos permisos que impidan listar su contenido:

Figura 16: Listado de contenido protegido

Y esto es importante. Porque los ficheros con los datos de la sesión tienen un nombre que incluye el valor de la cookie de sesión (típicamente, PHPSESSID). De modo que, si fuera posible listar el directorio, bastaría una orden del tipo "ls /var/lib/php/sessions" para hacerse con todas las cookies de sesión del servidor. Cosa poco deseable. Salvo para el atacante, claro.

Por otro lado, si se inicia sesión en la aplicación víctima, la información de diagnóstico mostrará que, gracias al módulo "ruid2", está siendo ejecutada con su propia cuenta:

Figura 17: La aplicación se ejecuta con su propia cuenta

Ojo al grupo, que es "www-data". Que, si uno no tiene cuidado al asignar grupos y permisos a los ficheros, este detalle puede ser de lo más relevante. Y obsérvese el valor de la cookie. En el directorio de las variables de sesión aparecería el correspondiente fichero cuya configuración de seguridad lo haría inacccesible a todo usuario menos para "com" (y, por supuesto, para "root" y quienes hagan un "sudo"):

Figura 18:  Sesiones solo accesibles a dueño y root

También la shell PHP se ejecutará con los permisos correspondientes al fichero que la aloja:

Figura 19: Shell ejecutada con sus propios permisos

Como puede observarse, se trata del usuario "net", que no tiene permisos para leer la cookie creada por la aplicación víctima. De modo que, aunque forcemos la cookie y le pongamos la misma que tiene dicha aplicación, la shell PHP será incapaz de leer o escribir las variables de sesión.

Figura 20: No se pueden leer o escribir variables de sesión

Por otro lado, PHP rehusa leerlo o escribir en una cookie cuyo propietario no sea el mismo usuario con el que se está ejecutando el script. De modo que tampoco valdría generar las variables de sesión desde la shell PHP, asignar permisos sobre el fichero de sesión que hagan posible su lectura por "com" y forzar la cookie para "webserver.example.com". Además, el usuario "net" no tiene privilegios suficientes para cambiar el propietario o el grupo de sus ficheros de sesión e intentar asignárselos a "com".

Reflexión intermedia

Llegados a este punto, todo parece apuntar a que:
- Los permisos de ficheros y carpetas impiden que la shell PHP pueda leer el contenido de los scripts de la aplicación. 
- La shell PHP no puede leer ni escribir las variables de sesión de la aplicación. 
- La shell PHP no puede crear un conjunto de variables de sesión que después puedan ser forzados a la aplicación víctima.
Entonces... todo está bien ¿verdad?

La respuesta es: NO

Y no hacía falta demasiada agudeza para darse cuenta: Si todo estuviera bien, este post no estaría publicado en este blog, pero deberás leer la segunda parte para conocer el resto de la historia.

Autor: Enrique Rando, escritor de los libros "Hacking con Buscadores", Hacking Web Applications: SQL Injection y "Hacking Web Technologies" de 0xWord.

No hay comentarios:

Publicar un comentario