Breeze Cache RCE: Gravatar Fetch Hits 400K Sites (CVE-2026-3844)
By WP Vanguard Team
Wordfence blocked 3,936 exploitation attempts against the Breeze caching plugin in a single 24-hour window. The flaw, tracked as CVE-2026-3844, lets unauthenticated attackers turn a feature designed to speed up sites — local Gravatar caching — into an arbitrary PHP webshell upload. The plugin has more than 400,000 active installations and is bundled by default on Cloudways and several other managed WordPress hosts.
The vulnerability scores 9.8 on CVSS. It affects every Breeze release up to and including 2.4.4. Cloudways shipped a fix in 2.4.5 on April 22, 2026, and disclosed publicly on April 23. If you run Breeze and you ever turned on "Host Files Locally - Gravatars," you need to update before reading the rest of this post.
What Breeze Is and Where the Bug Lives
Breeze is a WordPress page-caching and asset-optimization plugin maintained by Cloudways (DigitalOcean). It ships free on the WordPress.org repository and is preinstalled on Cloudways-managed WordPress sites, which is how its install count climbed past 400,000.
One of Breeze's optional features is "Host Files Locally," which mirrors third-party assets onto the site's own server so visitors don't pay a DNS lookup or TLS handshake for every avatar, font, or analytics script. The Gravatar variant of this feature fetches user avatars from gravatar.com and stores them under /wp-content/uploads/ so they can be served with the same caching rules as the rest of the site.
That fetch is implemented in class-breeze-cache-cronjobs.php in a function called fetch_gravatar_from_remote(). The function takes a URL, downloads whatever's at that URL, and writes the bytes to a local path. It does not check the response's content type. It does not check the file extension. It does not run a magic-byte sniff. Whatever bytes come back, with whatever filename the source suggests, get saved.
The Exploit Chain
To trigger the upload, an attacker needs the plugin to fetch a URL they control. The path to that is straightforward:
- The attacker registers a Gravatar-style email pointer (or simply manipulates an avatar source the plugin trusts) so the plugin attempts to retrieve an image for that identity.
- Instead of a JPG, the attacker's server returns a PHP file with a
Content-Dispositionfilename likeshell.php. - Breeze's fetcher writes the response body to
/wp-content/uploads/breeze/gravatar/shell.php. - The attacker visits that URL directly. The web server executes the PHP. Game over.
There are no authentication or capability checks on the fetcher path. The attack is fully unauthenticated, fully automatable, and visible in server logs only as a normal-looking outbound HTTP request followed by a routine file write. From the moment the response lands on disk, the attacker has the same RCE primitive as the Ninja Forms file upload bug from earlier in April.
That primitive is enough to install a PHP backdoor, read wp-config.php to steal database credentials, create administrator accounts, and pivot. The Wordfence rules that started catching probes within hours of disclosure suggest several attacker groups already had the exploit ready to go.
The "Not Default" Caveat Is Smaller Than It Sounds
Cloudways' advisory notes that exploitation requires the "Host Files Locally - Gravatars" toggle to be enabled, and that this isn't the default. That sounds like a meaningful mitigation. In practice it isn't, for three reasons.
The toggle ships in tutorials. Almost every "speed up your WordPress site with Breeze" guide tells readers to flip on Host Files Locally. Several of those guides specifically recommend the Gravatars variant for sites with comment sections.
Cloudways enables it on managed plans. Some Cloudways stacks ship Breeze with the option pre-enabled in their performance preset. Site owners who never opened Breeze settings may still have it on.
You can't tell from the outside. A site owner cannot easily verify the toggle state without logging into the admin panel and walking the settings tree. An automated attacker can probe for the vulnerable code path directly. The asymmetry favors the attacker.
If you've ever asked anyone to "make my site faster" or run a Pagespeed/GTmetrix chase, the toggle may be on without your knowledge.
How to Check and Patch
Three things, in this order:
1. Update Breeze to 2.4.5 or later.
wp plugin update breeze --version=2.4.5
# or
wp plugin update breeze
If you can't update immediately (staging conflicts, paused deployments), deactivate the plugin: wp plugin deactivate breeze. Page caching going away for a few hours is a smaller cost than a webshell going up.
2. Check the Gravatar settings.
In wp-admin → Breeze → Advanced Options → Files, look for "Host Files Locally" and the Gravatar sub-option. If "Host Files Locally - Gravatars" is on, switch it off until you've patched and verified.
3. Audit the Gravatar cache directory for unexpected file types.
find wp-content/uploads -path "*breeze*gravatar*" -type f \
! \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.gif" -o -name "*.webp" \) \
-ls
Any .php, .phtml, .phar, .html, or extensionless file inside the gravatar cache is a red flag. Move suspicious files out of the web root before deleting them so you can analyze them later. Then walk the signs your WordPress site has been hacked checklist for additional indicators (new admin accounts, unfamiliar cron jobs, modified core files).
If you find a webshell, treat the site as compromised and follow the full malware removal guide. Rotate every credential WordPress had access to: database password, salts, API keys, SMTP credentials.
Why This Class of Bug Keeps Happening
"Plugin fetches a remote URL and writes the response to disk" is one of the most common patterns in the WordPress ecosystem. Image optimizers do it. Font hosting plugins do it. CDN bridge plugins do it. Backup plugins do it. Every one of them has to make the same set of decisions:
- Does the URL come from a trusted source?
- Do we validate the response's content type?
- Do we sniff the actual bytes (magic numbers) before trusting an extension?
- Where do we write the result, and is that path web-accessible?
Skipping any one of those checks is enough for an arbitrary file upload. Breeze's fetch_gravatar_from_remote skipped them all on the basis that "Gravatar URLs are trusted." But the URL the function fetches is constructed from user-controlled inputs (the email hash), and the response bytes are whatever the network returns. There's no actual trust in that chain — it just felt trustworthy in the original code review.
Plugins that fetch remote files should treat the response as adversarial input by default. Validate the content type. Check magic bytes against an allow-list. Write the result to a directory with .htaccess or nginx rules that block PHP execution. Reject anything that doesn't match.
Site owners can't fix this for the plugin authors. What we can do is reduce our exposure: turn off optional fetch-and-store features unless we actually need them, and run weekly security scans so a planted webshell doesn't sit on disk for weeks before anyone notices.
References
- Wordfence: 400,000 WordPress Sites Affected by Arbitrary File Upload Vulnerability in Breeze Plugin
- BleepingComputer: Hackers exploit file upload bug in Breeze Cache WordPress plugin
- Security Affairs: Over 400,000 sites at risk as hackers exploit Breeze Cache plugin flaw
- SC Media: Critical vulnerability in WordPress Breeze Cache plugin exploited
- Tenable CVE-2026-3844 details
Related reading
Check Your WordPress Site Security
Free scan, no login required. Find vulnerabilities before attackers do.
Scan Your Site FreeGet weekly WordPress security tips
Vulnerability alerts, plugin updates, and security guides. No spam. Unsubscribe any time.