1. Introducción
Una aplicación PHP puede presentar riesgos importantes cuando combina una configuración de sesiones permisiva, el seguimiento de cargas de archivos y funciones que permiten incluir archivos sin validar su procedencia.
En este laboratorio se estudia cómo inspeccionar esas condiciones utilizando contenedores Docker, registros del servidor y scripts de diagnóstico. El objetivo es comprender qué datos almacena PHP, cómo se comporta la aplicación y qué evidencias son necesarias para determinar si existe una vulnerabilidad explotable.
El laboratorio permite estudiar cuatro áreas:
- Inyección de datos en archivos de sesión.
- Diferencias entre los manejadores de serialización de sesiones.
- Inclusión local de archivos (Local File Inclusion, LFI).
- Consumo de recursos y comportamiento de PHP-FPM.
Las pruebas deben realizarse exclusivamente en sistemas propios o con autorización expresa.
2. Preparar el entorno
Se necesita Docker Desktop, Docker Compose, Python 3 y un terminal.
Clona el repositorio de laboratorio y accede a su directorio:
git clone https://github.com/ikow/php2shell-poc.git
cd php2shell-poc/lab
Antes de iniciar los contenedores, comprueba la configuración:
docker compose config
Si el proyecto utiliza un script de preparación, revisa su contenido antes de ejecutarlo y confirma qué archivos y configuraciones modifica.
Inicia el entorno:
docker compose up -d
Comprueba los servicios:
docker compose ps
Consulta los puertos publicados y verifica que corresponden a los definidos en el archivo Compose.
3. Comprobar la disponibilidad HTTP
En el laboratorio utilizado, los puertos 18080, 18081 y 18082 corresponden a diferentes entornos PHP.
Comprueba las respuestas:
curl -i --max-time 5 http://127.0.0.1:18080/
curl -i --max-time 5 http://127.0.0.1:18081/
curl -i --max-time 5 http://127.0.0.1:18082/
Una respuesta HTTP 200 confirma que el servidor ha respondido correctamente a esa petición. No demuestra que el código sea seguro ni que una vulnerabilidad esté presente o ausente.
4. Identificar la configuración efectiva de PHP
Consulta los parámetros relevantes de cada contenedor:
docker exec php2shell-fpm-80 php-fpm -i 2>/dev/null | \
grep -E 'Loaded Configuration File|memory_limit|session.save_path|session.serialize_handler'
Repite la comprobación en los demás contenedores, cambiando el nombre del servicio.
Los parámetros que interesa documentar son:
memory_limit: límite de memoria de cada script PHP.session.save_path: directorio donde se almacenan las sesiones.session.serialize_handler: formato utilizado para serializar los datos de sesión.session.upload_progress.enabled: seguimiento del progreso de las cargas.session.upload_progress.cleanup: limpieza de los datos de progreso.
La configuración efectiva debe prevalecer sobre las suposiciones basadas únicamente en los archivos del proyecto. Los ajustes pueden variar entre contenedores o directorios.
5. Inspeccionar los archivos de sesión
Si el laboratorio utiliza /tmp/sessions, examina los archivos existentes:
docker exec php2shell-fpm-80 sh -lc \
'ls -lah /tmp/sessions'
Para consultar el tamaño de los archivos:
docker exec php2shell-fpm-80 sh -lc \
'find /tmp/sessions -maxdepth 1 -type f -exec wc -c {} +'
En una sesión que contiene datos de progreso de carga pueden aparecer campos como:
start_timecontent_lengthbytes_processeddonefilesnametmp_name
Estos campos permiten estudiar cómo PHP registra una carga.
Si aparece texto parecido a código PHP dentro de un nombre de archivo, no debe concluirse automáticamente que ese código se ha ejecutado. El contenido almacenado y la ejecución efectiva son hechos distintos.
6. Revisar el riesgo de inclusión local de archivos
Examina el código de la aplicación:
sed -n '1,180p' webroot/vuln.php
Una implementación como esta es insegura:
<?php
$page = $_GET['page'] ?? 'home';
include($page);
El problema es que el valor recibido desde la URL se utiliza directamente como ruta para include().
PHP interpreta el archivo incluido según las reglas del lenguaje. Por tanto, una ruta controlada por el usuario puede permitir acceder a archivos no previstos y, bajo determinadas condiciones, ejecutar código.
La medida correctora consiste en utilizar una lista cerrada de páginas permitidas en lugar de aceptar rutas arbitrarias. También deben revisarse los permisos de lectura, la configuración del servidor y las rutas accesibles desde la aplicación.
7. Analizar las diferencias entre los manejadores de sesión
PHP admite distintos formatos para almacenar sesiones, entre ellos php y php_serialize.
Revisa los archivos de configuración del laboratorio y los posibles ajustes específicos de cada directorio:
find webroot -name '.user.ini' -print
Consulta su contenido y compáralo con la configuración efectiva de PHP-FPM.
Una discrepancia entre componentes merece una revisión, pero no demuestra por sí sola una vulnerabilidad de ejecución de código. Es necesario comprobar cómo se escriben, leen y procesan los datos de sesión.
8. Revisar los registros de PHP-FPM
Para examinar los últimos eventos:
docker logs --tail=50 php2shell-fpm-80
docker logs --tail=50 php2shell-fpm-81
docker logs --tail=50 php2shell-fpm-82
Para seguir los registros durante una comprobación inocua:
docker logs --follow --tail=20 php2shell-fpm-80
Los registros permiten relacionar peticiones HTTP con los servicios que las procesan. No siempre contienen suficiente información para confirmar por sí solos el resultado de una explotación.
Al terminar, pulsa Ctrl+C para dejar de seguir los registros. Esto no detiene el contenedor.
9. Evaluar las pruebas de seguridad
El laboratorio contiene scripts orientados a estudiar diferentes comportamientos:
| Prueba | Objetivo |
|---|---|
verify_session_injection.py | Examinar la escritura de datos en sesiones. |
verify_handler_mismatch.py | Investigar diferencias entre manejadores de sesión. |
verify_oom_trigger.py | Estudiar el comportamiento ante consumo elevado de recursos. |
verify_fpm_stale_state.py | Investigar posibles estados residuales de PHP-FPM. |
chain_a_session_lfi.py | Evaluar una cadena que combina sesiones e inclusión de archivos. |
Antes de ejecutar cada script, revisa su código para conocer las peticiones que envía y los efectos que puede producir. Las pruebas de agotamiento de recursos deben realizarse solo en un entorno aislado y con límites controlados.
Es importante comprobar cómo cada script decide que una prueba ha tenido éxito. Un mensaje como RCE CONFIRMED no constituye una prueba independiente si el propio programa no valida la salida real del comando.
10. Interpretar los resultados
Los resultados deben clasificarse en tres niveles:
Condición observada: un archivo de sesión contiene datos de progreso, una ruta es controlable o una petición obtiene una respuesta HTTP.
Indicio de vulnerabilidad: las condiciones observadas son compatibles con un fallo de seguridad, pero todavía no se ha acreditado el impacto.
Vulnerabilidad validada: existe evidencia reproducible de que una entrada controlada produce el efecto de seguridad esperado, y se ha descartado que el resultado sea un falso positivo.
En las pruebas de ejecución de comandos, también debe documentarse la identidad del proceso. La ejecución como www-data no equivale a obtener privilegios de root.
11. Medidas de protección
Las principales medidas correctoras son:
- Actualizar PHP a una versión con soporte y parches de seguridad vigentes.
- Eliminar las inclusiones de archivos controladas directamente por parámetros de usuario.
- Mantener activada la limpieza de los datos de progreso de carga cuando se utiliza esa funcionalidad.
- Desactivar el seguimiento del progreso si la aplicación no lo necesita.
- Evitar el procesamiento inseguro de datos serializados no confiables.
- Revisar los permisos de los directorios de sesiones y de los archivos de la aplicación.
- Limitar la memoria, el tamaño de las cargas y la concurrencia de los procesos.
- Supervisar los registros y comprobar la configuración efectiva después de cualquier cambio.