Automated Threat Modeling

Brief Summary: CVE-2026-3844 — Unauthenticated Arbitrary File Upload in Breeze Cache for WordPress

A short review of CVE-2026-3844, a critical unauthenticated arbitrary file upload vulnerability in the Breeze Cache WordPress plugin (versions up to 2.4.4), with an analysis of the patch and the configurations it affects.

CVE Analysis

8 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-04-22

Brief Summary: CVE-2026-3844 — Unauthenticated Arbitrary File Upload in Breeze Cache for WordPress
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

The Breeze Cache plugin for WordPress does not validate file types, so unauthenticated attackers can upload arbitrary files to the server. On any site with a specific caching feature enabled, that opens the door to remote code execution. Breeze Cache has over 400,000 active installations and 14 million total downloads, which makes it one of the more popular performance optimization plugins for WordPress, and a CVSS 9.8 flaw in it is a significant concern for the WordPress community.

Cloudways develops Breeze Cache. The plugin speeds up site loading with page caching, file minification, CDN integration, and local hosting of external assets (including Gravatars). It is widely used across shared hosting, VPS, and managed WordPress environments, so even a conditionally exploitable flaw like this one has a meaningful blast radius.

Technical Information

Root Cause: Zero Validation in fetch_gravatar_from_remote()

The vulnerability lives in the fetch_gravatar_from_remote() function in inc/class-breeze-cache-cronjobs.php. In versions 2.4.4 and earlier, this function downloads Gravatar images from remote sources and caches them locally. It accepts a URL, calls the WordPress download_url method to fetch whatever that URL serves, and then moves the downloaded content into a web-accessible cache directory with the WordPress filesystem move method.

None of these steps validate anything. The function does not check the origin domain of the URL, inspect the file extension, or verify the downloaded file's actual content type. It blindly trusts whatever URL it receives.

The Upstream Caller: breeze_replace_gravatar_image()

The breeze_replace_gravatar_image() method parses HTML src and srcset attributes to extract Gravatar URLs, then passes them to fetch_gravatar_from_remote(). In the vulnerable version, the regex that does this extraction was overly permissive:

// Old (vulnerable): matched unquoted/loosely quoted attribute values preg_match_all( '/srcset=["\']?(...)["\']?/', $gravatar, $srcset );

An attacker could trick this loose pattern into matching attacker-controlled strings embedded in other HTML attributes (such as a malicious alt value), which widens the URL injection surface.

Attack Flow

Exploitation works in six steps:

  1. The attacker identifies a WordPress site running Breeze Cache with the "Host Files Locally Gravatars" option enabled.
  2. The attacker crafts input containing a URL that points to a malicious file on an attacker-controlled server. They could do this by controlling a comment author's profile data or by manipulating HTML attributes that the plugin's regex parses.
  3. The breeze_replace_gravatar_image() method extracts the attacker-supplied URL from the HTML.
  4. The method passes the URL to fetch_gravatar_from_remote(), which downloads the file (for example, a PHP webshell) without any validation.
  5. The plugin moves the downloaded file to the publicly reachable cache directory at WP_CONTENT_DIR/cache/breeze extra/gravatars/.
  6. The attacker requests the file directly at its public URL and gets remote code execution on the server.

Gating Condition

Exploitation depends entirely on the "Host Files Locally Gravatars" setting. The feature is disabled by default, which limits the immediate attack surface to sites that have explicitly turned it on for performance reasons. For those sites, though, the risk is immediate, because the vulnerability requires no authentication and can be triggered remotely.

Patch Information

Breeze Cache version 2.4.5 patched the vulnerability. It was released on April 21, 2026 through WordPress plugin repository changeset 3511463. The fix adds a comprehensive, multi-layered defense to both fetch_gravatar_from_remote() and its upstream caller breeze_replace_gravatar_image().

Layer 1: Origin Domain Allowlist

At the top of fetch_gravatar_from_remote(), the patch now extracts and validates the hostname of the incoming URL. It accepts only gravatar.com and its subdomains:

$host = strtolower( (string) wp_parse_url( $url, PHP_URL_HOST ) ); if ( 'gravatar.com' !== $host && '.gravatar.com' !== substr( $host, -13 ) ) { return $url; }

This is the most impactful single change. It closes the SSRF-like vector entirely, so the plugin can never fetch content from attacker-controlled servers.

Layer 2: Pre-Download File Extension Validation

Before making any network request, the patch calls wp_check_filetype() on the URL's basename and compares the result against an explicit allowlist of image MIME types. If the filename has a recognized extension that maps to a non-image MIME type (such as .php or .html), the patch rejects the request. If the filename has no recognizable extension at all, the patch safely normalizes it by appending .jpg:

$filetype = wp_check_filetype( $gravatar_name ); $allowed_images = array( 'image/jpeg', 'image/png', 'image/gif' ); if ( ! empty( $filetype['type'] ) && ! in_array( $filetype['type'], $allowed_images, true ) ) { return $url; } if ( empty( $filetype['ext'] ) || ! in_array( $filetype['type'], $allowed_images, true ) ) { $gravatar_name .= '.jpg'; }

Layer 3: Post-Download Content Verification

The patch does not trust a file even after downloading it to a temporary location. It calls wp_check_filetype_and_ext(), which inspects the actual file contents rather than just the name, and confirms that the MIME type starts with image/. If the downloaded file fails this content-based check, the patch deletes it immediately:

$file_check = wp_check_filetype_and_ext( $temp_gravatar, $gravatar_name ); if ( empty( $file_check['type'] ) || 0 !== strpos( $file_check['type'], 'image/' ) ) { @unlink( $temp_gravatar ); return $url; }

This layer is critical defense in depth. Even if an attacker managed to bypass the domain and extension checks, a file with non-image content (such as a PHP webshell) would be caught and deleted here, before it could be moved to the final web-accessible path.

Layer 4: Input Regex Hardening in the Caller

The regex in breeze_replace_gravatar_image() now requires whitespace before the attribute and properly quoted values, which shrinks the URL injection surface:

// New (patched): requires whitespace before attribute, properly quoted values preg_match( '/\ssrcset=["\']([^"\']+)["\']/', $gravatar, $srcset_match );

The patch also refactors error handling from a "proceed on success" nesting pattern to early return on failure. It now always removes the temporary file with @unlink, whether or not the move succeeded, so orphaned temp files can no longer accumulate.

Affected Systems and Versions

All versions of the Breeze Cache plugin for WordPress up to and including version 2.4.4 are affected. The vulnerability is exploitable only when the "Host Files Locally Gravatars" configuration option is enabled, and that setting is disabled by default.

The fix is available in version 2.4.5 and later.

Vendor Security History

Wordfence intelligence records 9 distinct vulnerabilities in the Breeze plugin since 2022. Notable entries include:

Vulnerability TypeCVE IdentifierCVSS ScoreStatus
Unauthenticated Arbitrary File UploadCVE 2026 38449.8Patched in 2.4.5
Missing Authorization to Cache DeletionCVE 2025 138645.3Patched
Missing AuthorizationCVE 2025 693645.3Patched
Authenticated Stored Cross-Site ScriptingCVE 2024 504314.4Patched

Cloudways has been responsive in patching reported vulnerabilities. Still, the recurring authorization and validation flaws suggest that organizations running Breeze Cache should treat plugin updates as a priority maintenance item.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization