Add Expires Headers & Optimized Minify <= 3.2.0 - Missing Authorization
Description
The Add Expires Headers & Optimized Minify plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 3.2.0. This makes it possible for unauthenticated attackers to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=3.2.0What Changed in the Fix
Changes introduced in v3.3.0
Source Code
WordPress.org SVN# Vulnerability Analysis: CVE-2026-24633 ## 1. Vulnerability Summary The **AEH Speed Optimization** plugin (versions <= 3.2.0) is vulnerable to **Missing Authorization**. The plugin registers an AJAX action `purge_cache` via both `wp_ajax_purge_cache` (authenticated) and `wp_ajax_nopriv_purge_cache…
Show full research plan
Vulnerability Analysis: CVE-2026-24633
1. Vulnerability Summary
The AEH Speed Optimization plugin (versions <= 3.2.0) is vulnerable to Missing Authorization. The plugin registers an AJAX action purge_cache via both wp_ajax_purge_cache (authenticated) and wp_ajax_nopriv_purge_cache (unauthenticated). The handler function refresh_browser_cache lacks any capability checks (current_user_can) or nonce verification (check_ajax_referer / wp_verify_nonce).
This allows an unauthenticated attacker to trigger a modification of the site's .htaccess file and update the aeh_expires_headers_last_modified_time option in the WordPress database.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
purge_cache - Method: POST or GET (WordPress AJAX handles both, though POST is standard)
- Authentication: None required (unauthenticated).
- Preconditions: The plugin must be active and the site must be running on a server that uses
.htaccess(e.g., Apache or Litespeed) for the filesystem impact, though the database modification occurs regardless of the server type.
3. Code Flow
- Entry Point: In
main/class-aeh-main.php, theAEH_Mainclass constructor registers the vulnerable hooks:add_action('wp_ajax_purge_cache', array($this, 'refresh_browser_cache')); add_action('wp_ajax_nopriv_purge_cache', array($this, 'refresh_browser_cache')); - Vulnerable Handler: The
refresh_browser_cachefunction is executed:public function refresh_browser_cache() { // Database Sink: Updates an option without authorization update_option('aeh_expires_headers_last_modified_time', date(DATE_RFC822)); // Filesystem Sink: Triggers .htaccess rewrite $this->write_to_htaccess(); wp_die(); } - Sink - Filesystem:
write_to_htaccess()callsdelete_from_htaccess()andgetrules(), then writes content toABSPATH . '.htaccess'. - Sink - Database:
update_optionmodifies thewp_optionstable.
4. Nonce Acquisition Strategy
No nonce is required. The refresh_browser_cache function does not contain any calls to check_ajax_referer or wp_verify_nonce. An attacker can send the request directly to admin-ajax.php.
5. Exploitation Strategy
The goal is to demonstrate that an unauthenticated user can force the plugin to update its configuration and modify the filesystem/database.
- Step 1: Send an unauthenticated POST request to
admin-ajax.phpwith theactionparameter set topurge_cache. - Payload:
- URL:
http://<target>/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=purge_cache
- URL:
6. Test Data Setup
- Install and activate AEH Speed Optimization version 3.2.0.
- Ensure
.htaccessexists in the WordPress root and is writable by the web server (standard for this plugin's functionality). - No specific content or users are required as the vulnerability is unauthenticated.
7. Expected Results
- The server will respond with a status code
200 OK(standard forwp_die()). - The database option
aeh_expires_headers_last_modified_timewill be updated to the current server time. - The site's
.htaccessfile will be rewritten. If it didn't contain the plugin's rules before, it will now contain blocks starting with# BEGIN AEH: Speed Optimization Plugin.
8. Verification Steps
Check Database Update: Use WP-CLI to check the option value before and after the exploit:
wp option get aeh_expires_headers_last_modified_timeVerification: The value should match the timestamp of the exploit attempt.
Check Filesystem Update: Inspect the
.htaccessfile:grep "BEGIN AEH: Speed Optimization Plugin" .htaccessVerification: The presence of this string confirms the
write_to_htaccess()logic was successfully triggered.
9. Alternative Approaches
If the nopriv AJAX action is restricted by another security plugin or firewall, the same function refresh_browser_cache is also triggered by the action hook refresh_cache:
add_action('refresh_cache', array($this, 'refresh_browser_cache'));
However, this is a server-side action hook and typically not directly reachable via HTTP unless another plugin or part of this plugin calls do_action('refresh_cache') based on user input. The AJAX endpoint remains the primary and most direct vector.
Another potential impact is "Missing Authorization" in hide_review_request, though that function does implement a nonce check (wp_verify_nonce($nonce, 'maybelater-nonce')), making it significantly harder to exploit unauthenticated unless a nonce leakage is found. The purge_cache action remains the definitive unauthenticated entry point.
Summary
The Add Expires Headers & Optimized Minify plugin registers the 'purge_cache' AJAX action for both authenticated and unauthenticated users without any capability checks or nonce verification. This allows an unauthenticated attacker to trigger a site-wide browser cache purge, which involves rewriting the site's .htaccess file and updating database options.
Vulnerable Code
// main/class-aeh-main.php line 22-23 add_action('wp_ajax_purge_cache', array($this, 'refresh_browser_cache')); add_action('wp_ajax_nopriv_purge_cache', array($this, 'refresh_browser_cache')); --- // main/class-aeh-main.php line 25-30 public function refresh_browser_cache() { update_option('aeh_expires_headers_last_modified_time', date(DATE_RFC822)); $this->write_to_htaccess(); wp_die(); }
Security Fix
@@ -5,7 +5,7 @@ Plugin URI: http://www.addexpiresheaders.com/ Description: AEH Speed Optimization boosts site speed with caching, minification, lazy loading, and image optimization to improve performance and SEO. Author: Passionate Brains -Version: 3.2.0 +Version: 3.3.0 Author URI: http://www.addexpiresheaders.com/ License: GPLv2 or later */ @@ -75,7 +75,7 @@ define('AEH_PREFIX', 'AEH_'); } if (!defined('AEH_VERSION')) { - define('AEH_VERSION', '3.2.0'); + define('AEH_VERSION', '3.3.0'); } /* Definining main class */ if (!class_exists('AEH_Pro')) { @@ -20,8 +20,17 @@ add_action('wp_ajax_hide_review_notice', array($this, 'hide_review_request')); //add_action('wp_ajax_nopriv_hide_review_notice', array($this, 'hide_review_request')); add_action('refresh_cache', array($this, 'refresh_browser_cache')); - add_action('wp_ajax_purge_cache', array($this, 'refresh_browser_cache')); - add_action('wp_ajax_nopriv_purge_cache', array($this, 'refresh_browser_cache')); + add_action('wp_ajax_purge_cache', array($this, 'click_refresh_browser_cache')); + //add_action('wp_ajax_nopriv_purge_cache', array($this, 'refresh_browser_cache')); + } + + public function click_refresh_browser_cache() + { + check_ajax_referer('purge-cache-nonce', 'security'); + if (! is_user_logged_in() || ! current_user_can('manage_options')) { + wp_send_json_error(['message' => 'Unauthorized'], 403); + } + $this->refresh_browser_cache(); } public function refresh_browser_cache() { @@ -4,7 +4,7 @@ Tags: cache, Optimize, performance, PageSpeed, core web vitals Requires at least: 3.5 Tested up to: 6.9 -Stable tag: 3.2.0 +Stable tag: 3.3.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -222,6 +222,9 @@ Updated Freemius SDK to latest stable Version Latest Security Fixes += 3.3.0 = +Patched one more secuirty issue + == Screenshots == 1. Cache Settings 2. Minify Settings
Exploit Outline
To exploit this vulnerability, an unauthenticated attacker can send a POST request to the WordPress AJAX endpoint (/wp-admin/admin-ajax.php) with the 'action' parameter set to 'purge_cache'. Because the plugin registers this action via 'wp_ajax_nopriv_purge_cache' and the handler 'refresh_browser_cache' performs no authentication, authorization (current_user_can), or nonce checks, the server will execute the function. This results in the database option 'aeh_expires_headers_last_modified_time' being updated and the site's .htaccess file being modified on the filesystem.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.