Inclusión de Archivos Locales (LFI)
La Inclusión de Archivos Locales (LFI) es una vulnerabilidad que permite a un atacante incluir y leer — o en algunos casos ejecutar — archivos del sistema de archivos local del servidor mediante parámetros de entrada manipulados. A diferencia del simple path traversal que solo lee el contenido de archivos, LFI explota mecanismos de inclusión del lado del servidor (PHP include(), JSP <%@ include>, SSI), logrando potencialmente la ejecución remota de código al incluir archivos que contienen contenido controlado por el atacante, como archivos de log, archivos subidos o datos de sesión.
Cómo Funciona Inclusión de Archivos Locales (LFI)
Las vulnerabilidades LFI ocurren cuando las aplicaciones incluyen archivos dinámicamente basándose en entrada controlada por el usuario sin la validación adecuada. El contexto más común son las aplicaciones PHP que usan include(), require(), include_once() o require_once() con nombres de archivo proporcionados por el usuario. Cuando un atacante controla el nombre de archivo pasado a estas funciones, puede incluir cualquier archivo legible en el servidor. La diferencia crítica con el path traversal es que la inclusión puede significar ejecución — si el archivo incluido contiene código PHP válido (incluso incrustado dentro de otro contenido como logs), ese código será ejecutado por el intérprete PHP con los privilegios completos de la aplicación.
Identificar parámetros de inclusión de archivos
El atacante busca parámetros de URL, campos de formulario o cookies que influyan en qué archivos carga la aplicación. Indicadores comunes incluyen parámetros como ?page=home, ?template=header, ?lang=en, ?module=dashboard, o ?file=report. Estos parámetros frecuentemente se mapean directamente a rutas de archivos en el servidor (por ejemplo, include($_GET['page'] . '.php')). Probar con nombres de archivo inexistentes puede revelar mensajes de error relacionados con la inclusión que exponen la ruta del sistema de archivos.
Confirmar LFI con archivos conocidos
El atacante intenta incluir archivos del sistema conocidos para confirmar la vulnerabilidad: /etc/passwd en Linux (siempre legible, confirma la profundidad de traversal), C:\Windows\win.ini en Windows, o los propios archivos de configuración de la aplicación. La carga maliciosa típicamente usa secuencias de directory traversal: ?page=../../../../etc/passwd. Si la aplicación agrega una extensión (por ejemplo, .php), el atacante usa inyección de byte nulo (%00 en versiones antiguas de PHP), truncamiento de ruta o técnicas de wrappers PHP para evadir la extensión.
Escalar a ejecución de código mediante envenenamiento de logs
El atacante identifica archivos escribibles que pueden incluirse para ejecución de código. La técnica más común es el envenenamiento de logs: el atacante envía una solicitud con código PHP incrustado en la cabecera User-Agent o URL (por ejemplo, <?php system($_GET['cmd']); ?>), que se escribe en el log de acceso del servidor web (/var/log/apache2/access.log). Luego, el atacante incluye el archivo de log vía LFI, causando que el código PHP en el log se ejecute. Otros objetivos escribibles incluyen /proc/self/environ, archivos de sesión PHP (/tmp/sess_*) y archivos temporales subidos.
Explotar wrappers PHP para ataques avanzados
PHP proporciona wrappers de flujo que permiten la explotación avanzada de LFI: php://filter permite leer código fuente como base64 (php://filter/convert.base64-encode/resource=config.php), evadiendo la ejecución PHP para revelar secretos; php://input permite incluir el cuerpo del POST como código PHP (requiere allow_url_include=On); data:// permite la ejecución de código en línea; zip:// y phar:// pueden ejecutar código desde archivos comprimidos manipulados. Estos wrappers transforman una simple vulnerabilidad de lectura de archivos en ejecución remota de código completa.
Lograr acceso persistente
Con la ejecución de código establecida, el atacante despliega una web shell para acceso persistente: escribiendo un backdoor PHP en la raíz del documento, modificando archivos existentes de la aplicación para incluir funciones de backdoor ocultas, o estableciendo una conexión de shell reversa a la infraestructura del atacante. Desde esta posición, el atacante puede acceder a bases de datos, robar credenciales, pivotar a redes internas, instalar ransomware o exfiltrar datos a escala.
Ejemplos Reales
Vulnerabilidad de TimThumb en WordPress
Una vulnerabilidad de inclusión de archivos locales en TimThumb, un script PHP de redimensionamiento de imágenes usado por miles de temas y plugins de WordPress, permitía a los atacantes incluir y ejecutar archivos PHP arbitrarios. Los atacantes subían PHP malicioso disfrazado como imágenes, luego incluían estos archivos a través del mecanismo de caché de TimThumb. La vulnerabilidad comprometió un estimado de 1.2 millones de sitios WordPress, con atacantes desplegando backdoors, spam SEO e infraestructura de distribución de malware.
Inyección de parámetros PHP-CGI (CVE-2012-1823)
Una vulnerabilidad en el modo CGI de PHP permitía a los atacantes manipular la configuración de PHP a través de parámetros de cadena de consulta, habilitando la inclusión de archivos y la ejecución remota de código en cualquier instalación PHP-CGI. La vulnerabilidad afectó a millones de servidores web en todo el mundo y fue explotada activamente horas después de su divulgación pública. Los atacantes la usaron para incluir y ejecutar archivos arbitrarios, desplegando web shells y mineros de criptomonedas en servidores comprometidos.
LFI en el framework JEECG Boot (CVE-2023-1454)
Una vulnerabilidad crítica de LFI en JEECG Boot, un popular framework de desarrollo low-code basado en Java utilizado por miles de aplicaciones empresariales, permitía a atacantes no autenticados leer archivos arbitrarios del servidor. Los atacantes explotaron la vulnerabilidad para extraer credenciales de bases de datos, secretos de aplicación y archivos de configuración. La vulnerabilidad fue explotada activamente antes de que estuviera disponible un parche, afectando a organizaciones que habían desplegado JEECG Boot para aplicaciones empresariales internas.
Impacto y Evaluación de Riesgo
La inclusión de archivos locales se clasifica como Crítica porque frecuentemente escala de la lectura de archivos a la ejecución remota de código completa. El impacto inmediato incluye la lectura de archivos de configuración sensibles (credenciales de base de datos, claves API, secretos de cifrado), código fuente de la aplicación, archivos del sistema (/etc/shadow para hashes de contraseñas) y claves privadas. Cuando se escala a ejecución de código mediante envenenamiento de logs, wrappers PHP o inclusión de archivos subidos, el atacante obtiene la capacidad de ejecutar comandos arbitrarios con los privilegios del servidor web, resultando típicamente en el compromiso total del servidor. En entornos contenedorizados, LFI puede exponer variables de entorno que contienen secretos, tokens de cuentas de servicio de Kubernetes y credenciales de metadatos de la nube. La prevalencia de LFI en aplicaciones PHP (que alimentan aproximadamente el 77% de todos los sitios web) la convierte en una de las vulnerabilidades web más comúnmente explotadas.
Cómo Detectar Inclusión de Archivos Locales (LFI)
Monitorear parámetros de solicitudes en busca de indicadores de inclusión de archivos: secuencias de directory traversal (../, ..\), prefijos de wrappers PHP (php://, data://, zip://, phar://), bytes nulos (%00) y referencias a archivos sensibles conocidos (/etc/passwd, /proc/self/environ, win.ini). Analizar los logs del servidor web en busca de cadenas User-Agent sospechosas que contengan código PHP (indicador de envenenamiento de logs). Implementar monitoreo de integridad de archivos en archivos de log del servidor web y directorios de sesión — etiquetas PHP inesperadas en estos archivos indican explotación activa. Desplegar registro a nivel de aplicación que registre todas las operaciones de include/require con la ruta de archivo resuelta, marcando cualquier ruta que se encuentre fuera de la estructura de directorios esperada de la aplicación. Las reglas del WAF deben detectar variantes codificadas de secuencias de traversal y sintaxis de wrappers. La autoprotección de aplicaciones en tiempo de ejecución (RASP) puede interceptar operaciones de archivos en tiempo real, bloqueando inclusiones de archivos fuera de la lista permitida.
Cómo Prevenir Inclusión de Archivos Locales (LFI)
Nunca usar entrada del usuario directamente en funciones de inclusión de archivos. Implementar una lista de permitidos de archivos/módulos autorizados y mapear la entrada del usuario a la lista permitida mediante una tabla de búsqueda (por ejemplo, switch/case o mapeo de diccionario ?page=home a templates/home.php) en lugar de usar la entrada como nombre de archivo. Si la inclusión dinámica de archivos es necesaria, resolver la ruta canónica y verificar que se encuentre dentro del directorio base esperado. Deshabilitar configuraciones peligrosas de PHP: allow_url_include=Off (previene la inclusión remota de archivos), open_basedir para restringir el acceso a archivos al directorio de la aplicación, y disable_functions para system(), exec(), passthru(). Usar un framework moderno que abstraiga la inclusión de archivos detrás de un renderizado de plantillas seguro (Twig, Blade, Jinja2). Mantener PHP y todos los frameworks del lado del servidor actualizados para parchear vulnerabilidades LFI conocidas. Desplegar un WAF con reglas específicas de LFI cubriendo patrones de traversal, wrappers PHP e inyección de bytes nulos. En despliegues contenedorizados, aplicar sistemas de archivos de solo lectura donde sea posible y minimizar los archivos accesibles al proceso de la aplicación.
Ejemplos de Código
<?php
// VULNERABLE: User input directly used in include
$page = $_GET['page'];
include("pages/" . $page . ".php");
// Attacker: ?page=../../../../etc/passwd%00
// Result: includes /etc/passwd (null byte truncates .php on old PHP)
// Attacker: ?page=php://filter/convert.base64-encode/resource=config
// Result: Leaks config.php source code as base64
// Attacker: ?page=../../../../var/log/apache2/access.log
// After poisoning User-Agent with: <?php system($_GET['cmd']); ?>
// Result: Remote code execution via log poisoning
?>
<?php
// SECURE: Map user input to allowed files via allowlist
$allowed_pages = [
'home' => 'pages/home.php',
'about' => 'pages/about.php',
'contact' => 'pages/contact.php',
'products' => 'pages/products.php',
'faq' => 'pages/faq.php',
];
$page = $_GET['page'] ?? 'home';
// Strict allowlist lookup — no file path manipulation possible
if (!array_key_exists($page, $allowed_pages)) {
http_response_code(404);
include('pages/404.php');
exit;
}
include($allowed_pages[$page]);
?>
import re
import os
from functools import wraps
# LFI attack patterns
LFI_PATTERNS = [
r'\.\.[\/\\]', # Directory traversal
r'%2e%2e[%2f%5c]', # URL-encoded traversal
r'%252e%252e', # Double-encoded traversal
r'php:\/\/', # PHP wrapper
r'data:\/\/', # Data wrapper
r'zip:\/\/', # Zip wrapper
r'phar:\/\/', # Phar wrapper
r'expect:\/\/', # Expect wrapper
r'%00', # Null byte
r'\/etc\/(passwd|shadow|hosts)', # Known sensitive files
r'\/proc\/self', # Process info
r'\/(var|tmp)\/log', # Log files
r'web\.config', # IIS config
r'\.htaccess', # Apache config
]
def detect_lfi(request_params):
"""Check request parameters for LFI indicators"""
compiled = [re.compile(p, re.IGNORECASE) for p in LFI_PATTERNS]
for param_name, param_value in request_params.items():
decoded = urllib.parse.unquote(urllib.parse.unquote(param_value))
for pattern in compiled:
if pattern.search(decoded):
return {
'detected': True,
'parameter': param_name,
'value': param_value,
'pattern': pattern.pattern
}
return {'detected': False}
def safe_file_resolve(base_dir, user_input):
"""Safely resolve a file path within a base directory"""
# Reject any path separators in user input
if os.sep in user_input or '/' in user_input or '\\' in user_input:
raise ValueError('Invalid filename: path separators not allowed')
# Resolve canonical path
resolved = os.path.realpath(os.path.join(base_dir, user_input))
base_resolved = os.path.realpath(base_dir)
# Verify resolved path is within base directory
if not resolved.startswith(base_resolved + os.sep):
raise ValueError('Access denied: path outside base directory')
if not os.path.isfile(resolved):
raise FileNotFoundError('File not found')
return resolved
PowerWAF bloquea automáticamente Inclusión de Archivos Locales (LFI) antes de que llegue a tu servidor.
Implementa en minutos. Sin cambios de código. Plan gratuito disponible.
Los cupos del plan gratuito son limitados