Local File Inclusion (LFI) o, en español, Inclusión local de archivos, es una técnica que consiste en la inclusión de ficheros locales, es decir, archivos que se encuentran en el mismo servidor de la web (a diferencia de Remote File Inclusión, que incluye archivos alojados en un servidor externo).
Imagen 1: ¿Cómo funciona la inclusión local de archivos (LFI)? |
La vulnerabilidad se produce como consecuencia de un fallo en la programación de la página, que filtra de manera inadecuada lo que se incluye al usar funciones en PHP para la inclusión de archivos. El problema reside en que si se presenta un escenario como este, un "atacante" podría modificar los parámetros de lo que se incluye e indicarle al sitio web que incluya otros archivos que también están en el mismo servidor, comprometiendo por completo la seguridad del mismo. Un claro ejemplo podría ser el archivo de contraseñas /etc/passwd en sistemas Linux.
Para entender mejor como funciona esta vulnerabilidad, usaremos un laboratorio de pruebas llamado bts_lab. Es un entorno el cual viene ya preparado con las configuraciones con los fallos que necesitamos para llevar a cabo las pruebas, sin cometer ningún delito. En la siguiente imagen se puede ver cómo un atacante podría leer el archivo que hemos mencionado anteriormente en un sistema Linux, el cual obtiene información sensible del servidor:
Imagen 2: vemos como se podría leer el archivo passwd |
Podemos observar cómo en la dirección URL se inserta el comando para leer el archivo en cuestión (/etc/passwd). Teniendo en cuenta que (../) se utiliza 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 mostrar 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 cualquier error como Warning: main()… o Warning: include()… o algo parecido entonces probablemente sea vulnerable a RFI o LFI.
Así es como se verían los respectivos códigos en PHP:
Código vulnerable:
<?php include $_GET[‘pagina’]; ?>
Código no vulnerable:
<?php include(‘pagina.php’); ?>
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.
En cambio, si el código es como el anteriormente mostrado (include $_GET[‘pagina’];) entonces sí nos permite modificar los parámetros de lo que se incluye: GET pasa los datos por URL, por lo que en la barra de direcciones veríamos algo como esto: 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 para mostrar, por ejemplo, el fichero de contraseñas de linux /etc/hosts.
Para evitar que esta técnica tenga mayor impacto, es muy importante 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 conseguiremos evitar el acceso a archivos del sistema, aunque no a ficheros propios de la aplicación.
Aparte si queremos una protección mediante programación en vez de controlar mediante permisos 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 anteriormente mencionado, los puntos.
Por último aquí tienes algunas recomendaciones a tener en cuenta publicadas por OWASP:
- 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.
- 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.
- 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.
- Validación estricta de la entrada del usuario.
- 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()