En ocasiones anteriores hemos visto en qué consiste una vulnerabilidad y cómo un atacante puede aprovecharla en un equipo para cometer ciberdelitos. A pesar de que hemos revisado configuraciones de seguridad para plataformas como Wordpress y Joomla, el trabajo no termina aquí.
Los sitios web muchas veces también poseen vulnerabilidades, algunas muy conocidas que datan de hace tiempo y otras tal vez menos populares; en este caso hablaremos de Local File Inclusion (LFI) o, en español, Inclusión local de archivos.
Esta técnica consiste en incluir ficheros locales, es decir, archivos que se encuentran en el mismo servidor de la web con este tipo de fallo -a diferencia de Remote File Inclusión o inclusión de archivos remotos (RFI) que incluye archivos alojados en otros servidores. Esto se produce como consecuencia de un fallo en la programación de la página, filtrando inadecuadamente lo que se incluye al usar funciones en PHP para incluir archivos.
¿Qué peligro representa esto si solo se incluyen ficheros que estén en el mismo servidor? En un escenario como este, un atacante podría modificar los parámetros de lo que se incluye, por ejemplo, podría indicarle al sitio web que se incluyan otros archivos que también están en el servidor, comprometiendo la seguridad del mismo por completo. Un ejemplo muy claro es el archivo de contraseñas como /etc/passwd en sistemas Linux.
LFI en la práctica
A continuación usaremos un laboratorio de pruebas llamado bts_lab, el cual viene preparado con las configuraciones vulnerables necesarias para llevar a cabo las pruebas necesarias, sin infringir la ley. En la siguiente captura de pantalla se muestra cómo un atacante puede leer el archivo mencionado anteriormente en un sistema Linux, en el cual obtiene información sensible del servidor:
En la captura puede verse cómo desde la dirección URL se inserta el comando para llegar a leer el archivo en cuestión (/etc/passwd). Teniendo en cuenta que (../) es utilizado para subir un directorio, lo que hace el comando es subir directorios hasta llegar a la raíz del sistema operativo, para luego ingresar en etc y traer el archivo passwd.
Para poder corroborar si un sitio es vulnerable, se puede colocar un valor ilógico a la variable. En nuestro ejemplo tenemos: http://localhost/index.php?page= donde se coloca un valor, en este caso http://localhost/index.php?page=78se3, algo aleatorio que no se encuentre registrado. Si arroja un error como Warning: main()... o Warning: include()... o similar entonces es probable que sea vulnerable a RFI o LFI. En el ejemplo que veremos a continuación, se ofuscó toda la información referente al sitio por seguridad:
Para tener en cuenta y estar atentos a la hora de programar un sitio, así es como se ven los respectivos códigos en PHP:
- Código vulnerable:
<?php
include $_GET['pagina'];
?>
- Codigo NO vulnerable:
<?php
include('pagina.php');
?>
Se puede notar que, para que una web sea vulnerable a LFI se necesita poder modificar los parámetros de lo que se va a incluir; el código no vulnerable anteriormente mostrado, include('pagina.php'), no es vulnerable a LFI porque no hay nada que pueda modificar, ya que nada más se incluye pagina.php.
Ahora bien, si el código es vulnerable como el anteriormente mostrado (include $_GET['pagina'];) entonces sí permite modificar los parámetros de lo que se incluye: GET pasa los datos por URL, por lo que en la barra de direcciones URL se vería algo así: http://localhost/index.php?pagina=78se3.php, donde se incluye el archivo 78se3.ph. La manipulación de este archivo puede modificar lo que se incluye mostrar, por ejemplo, el fichero de contraseñas de linux /etc/hosts.
¿Cómo puede prevenirse este tipo de ataques?
Para evitar que esta técnica tenga mayor impacto, es importante pensar en montar el servidor con el mínimo privilegio posible, limitando la posibilidad de acceso a archivos del servidor dentro de su propia carpeta. Con esto se logra evitar el acceso a archivos del sistema, aunque no a ficheros propios de la aplicación.
Si se desea hacer una protección mediante programación en lugar de tratar de controlar mediante permisos en el servidor, hay que tener en cuenta que las rutas a ficheros se pueden escribir de dos maneras:
- Directa: escribimos la ruta donde se encuentra el fichero directamente. Se debería eliminar los caracteres “\” o “/” de los datos enviados por los usuarios.
- Relativa: subir hacia directorios superiores mediante el uso de “..\” o “../”. Lo podríamos evitar excluyendo, además de lo anterior, los puntos.
Prevenir este tipo de vulnerabilidades como la de inclusión de archivos remotos requiere una planificación muy meticulosa en las fases de diseño y arquitectura. Generalmente, una aplicación bien escrita no usaría ninguna información proporcionada por el usuario en nombres de ficheros para ningún recurso del servidor (como imágenes, documentos de transformación .XML y .XSL o scripts), también tener configuradas reglas en el firewall para evitar conexiones salientes a Internet o internamente a cualquier otro servidor.
Sin embargo, muchas aplicaciones heredadas continuarán teniendo la necesidad de aceptar datos proporcionados por los usuarios.
Compartimos algunas consideraciones publicadas por OWASP importantes a tener en cuenta:
- Usar un mapa de referencias indirectas a objetos: Donde se ha usado una vez un nombre de fichero parcialmente, una buena alternativa es usar cifrado de una vía en la referencia parcial.
- Validación estricta de la entrada del usuario
- Comprobar cualquier archivo o nombre de archivo ingresado por el usuario: Siempre es recomendable tomar los controles necesarios a la hora de ingresar algún tipo de dato por el usuario, si bien la mayoría de usuarios usará el servicio legítimamente, un atacante podría tratar de vulnerar el sistema a través de las falta de controles adecuados.
- Enjaular usuarios con el comando chroot: Incorporar algún otro mecanismo de sandbox como la virtualización para aislar las aplicaciones entre sí por ejemplo, esto permitirá reducir escalamientos de privilegios.
- PHP: deshabilitar allow_url_fopen y allow_url_include: Dentro del archivo .ini se recomienda compilar PHP localmente sin incluir esta funcionalidad. Muy pocas aplicaciones necesitan esta funcionalidad y para estos casos estas opciones deberían habilitarse desde la base de la aplicación.
- PHP: deshabilitar register_globals y usar E_STRICT para encontrar variables no inicializadas
- PHP: ser extremadamente cuidadoso si pasa datos a system(), eval(), passthru() o (el operador de backtick).
- PHP: asegúrese de que todas las funciones de ficheros y flujos de datos (stream_*) son controladas rigurosamente: La aplicación debe revisar siempre que los datos de usuario, no son proporcionados a ninguna función que tenga como argumento un nombre de fichero, incluyendo:
include() include_once() require() require_once() fopen() imagecreatefromXXX() file() file_get_contents() copy() delete() unlink() upload_tmp_dir() $_FILES move_uploaded_file()
Tomando en cuenta este tipo de configuraciones se incrementará la seguridad en el servidor, de lo contrario la violación del mismo puede comprometer no solo información de usuarios, sino la totalidad del servidor, llevando al quiebre en la reputación y pérdida de confianza en el servicio.
Es por eso que se vuelve imprescindible establecer un plan de desarrollo óptimo, corregir este tipo de fallas en fases de prueba permitirá ahorrar tiempo y dinero, sin poner en juego la reputación de la empresa o entidad representada.