CVE-2026-24633

Add Expires Headers & Optimized Minify <= 3.2.0 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
3.3.0
Patched in
106d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.2.0
PublishedJanuary 9, 2026
Last updatedApril 25, 2026
Affected pluginadd-expires-headers

What Changed in the Fix

Changes introduced in v3.3.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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

  1. Entry Point: In main/class-aeh-main.php, the AEH_Main class 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'));
    
  2. Vulnerable Handler: The refresh_browser_cache function 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();
    }
    
  3. Sink - Filesystem: write_to_htaccess() calls delete_from_htaccess() and getrules(), then writes content to ABSPATH . '.htaccess'.
  4. Sink - Database: update_option modifies the wp_options table.

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.

  1. Step 1: Send an unauthenticated POST request to admin-ajax.php with the action parameter set to purge_cache.
  2. Payload:
    • URL: http://<target>/wp-admin/admin-ajax.php
    • Method: POST
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body: action=purge_cache

6. Test Data Setup

  1. Install and activate AEH Speed Optimization version 3.2.0.
  2. Ensure .htaccess exists in the WordPress root and is writable by the web server (standard for this plugin's functionality).
  3. 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 for wp_die()).
  • The database option aeh_expires_headers_last_modified_time will be updated to the current server time.
  • The site's .htaccess file 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

  1. Check Database Update: Use WP-CLI to check the option value before and after the exploit:

    wp option get aeh_expires_headers_last_modified_time
    

    Verification: The value should match the timestamp of the exploit attempt.

  2. Check Filesystem Update: Inspect the .htaccess file:

    grep "BEGIN AEH: Speed Optimization Plugin" .htaccess
    

    Verification: 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.

Research Findings
Static analysis — not yet PoC-verified

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

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.2.0/add-expires-headers.php /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.3.0/add-expires-headers.php
--- /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.2.0/add-expires-headers.php	2026-04-17 07:43:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.3.0/add-expires-headers.php	2026-04-18 04:29:48.000000000 +0000
@@ -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')) {
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.2.0/main/class-aeh-main.php /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.3.0/main/class-aeh-main.php
--- /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.2.0/main/class-aeh-main.php	2026-04-17 07:43:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.3.0/main/class-aeh-main.php	2026-04-18 04:29:48.000000000 +0000
@@ -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()
 	{
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.2.0/readme.txt /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.3.0/readme.txt
--- /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.2.0/readme.txt	2026-04-17 07:43:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/add-expires-headers/3.3.0/readme.txt	2026-04-18 04:29:48.000000000 +0000
@@ -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.